I want to display a WebBrowser inside my tkinter app. Per the example below:
##############
# label #
#————————————#
# webbrowser #
##############
Here is what I have tried: https://github.com/iCarlosCode/Problematika/blob/88a0f13cbdc4ee48ac9690b3ae26cf231ce3c340/calculator/cef3.py
I tried to copy the cefpython example and use the BrowserFrame, but it does not work. The cefpython tkinter example: https://github.com/cztomczak/cefpython/blob/master/examples/tkinter_.py
Anyone can help me?
So, there are 2 core issues with your main code:
cefpython
browser_frame
was not configured to fit the whole areaIn short, the answer is:
import tkinter as tk
from tkinter import *
from cefpython3 import cefpython as cef
import ctypes
def main():
win = Tk()
cef.Initialize()
win.minsize(600,600)
win.grid_columnconfigure(0, weight=1)
win.grid_rowconfigure(0, weight=1)
#Create Frame
frame = Frame(win, bg='black')
frame.grid(row=0, column=0, sticky=('NSWE'))
# Create Browser Frame
browser_frame = BrowserFrame(frame)
browser_frame.pack(fill=tk.BOTH, expand=tk.YES)
win.mainloop()
cef.Shutdown()
class BrowserFrame(tk.Frame):
def __init__(self, mainframe, navigation_bar=None):
self.navigation_bar = navigation_bar
self.closing = False
self.browser = None
tk.Frame.__init__(self, mainframe)
self.mainframe = mainframe
self.bind("<FocusIn>", self.on_focus_in)
self.bind("<FocusOut>", self.on_focus_out)
self.bind("<Configure>", self.on_configure)
"""For focus problems see Issue #255 and Issue #535. """
self.focus_set()
#URLURLURL
def embed_browser(self):
window_info = cef.WindowInfo()
rect = [0, 0, self.winfo_width(), self.winfo_height()]
window_info.SetAsChild(self.get_window_handle(), rect)
self.browser = cef.CreateBrowserSync(window_info,
url="file:///calculo.html")
assert self.browser
#self.browser.SetClientHandler(LifespanHandler(self))
#self.browser.SetClientHandler(LoadHandler(self))
#self.browser.SetClientHandler(FocusHandler(self))
self.message_loop_work()
def get_window_handle(self):
if self.winfo_id() > 0:
return self.winfo_id()
else:
raise Exception("Couldn't obtain window handle")
def message_loop_work(self):
cef.MessageLoopWork()
self.after(10, self.message_loop_work)
def on_configure(self, _):
if not self.browser:
self.embed_browser()
def on_root_configure(self):
# Root <Configure> event will be called when top window is moved
if self.browser:
self.browser.NotifyMoveOrResizeStarted()
def on_mainframe_configure(self, width, height):
if self.browser:
if WINDOWS:
ctypes.windll.user32.SetWindowPos(
self.browser.GetWindowHandle(), 0,
0, 0, width, height, 0x0002)
self.browser.NotifyMoveOrResizeStarted()
def on_focus_in(self, _):
#logger.debug("BrowserFrame.on_focus_in")
if self.browser:
self.browser.SetFocus(True)
def on_focus_out(self, _):
#logger.debug("BrowserFrame.on_focus_out")
"""For focus problems see Issue #255 and Issue #535. """
pass
def on_root_close(self):
#logger.info("BrowserFrame.on_root_close")
if self.browser:
#logger.debug("CloseBrowser")
self.browser.CloseBrowser(True)
self.clear_browser_references()
else:
#logger.debug("tk.Frame.destroy")
self.destroy()
def clear_browser_references(self):
# Clear browser references that you keep anywhere in your
# code. All references must be cleared for CEF to shutdown cleanly.
self.browser = None
class LifespanHandler(object):
def __init__(self, tkFrame):
self.tkFrame = tkFrame
def OnBeforeClose(self, browser, **_):
#logger.debug("LifespanHandler.OnBeforeClose")
self.tkFrame.quit()
class LoadHandler(object):
def __init__(self, browser_frame):
self.browser_frame = browser_frame
def OnLoadStart(self, browser, **_):
if self.browser_frame.master.navigation_bar:
self.browser_frame.master.navigation_bar.set_url(browser.GetUrl())
class FocusHandler(object):
"""For focus problems see Issue #255 and Issue #535. """
def __init__(self, browser_frame):
self.browser_frame = browser_frame
def OnTakeFocus(self, next_component, **_):
pass#logger.debug("FocusHandler.OnTakeFocus, next={next}".format(next=next_component))
def OnSetFocus(self, source, **_):
return True
def OnGotFocus(self, **_):
#logger.debug("FocusHandler.OnGotFocus")
pass
if __name__ == '__main__':
main()
So to fix issue № 1, within the tkinter main loop you have to type cef.Initialize()
and then put cef.Shutdown()
afterwards to close it. The problem is we still can't see the frame because you didn't do the grid_columnconfigure
nor grid_rowconfigure
so even though you did sticky='nsew'
it didn't do anything as there was no weight added to the row and column, to rectify this I used pack instead.
On windows this created for me:
Annoyingly this doesn't work very well on on cefpython
because the MathJax isn't loaded, to rectify this you need to change your calculo.html
to read:
<!DOCTYPE html>
<html>
<head>
<title>MathJax TeX Test Page</title>
<script type="text/x-mathjax-config" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js">
MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}});
</script>
<script type="text/javascript" async
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML">
</script>
</head>
<body>
When $a \ne 0$, there are two solutions to \(ax^2 + bx + c = 0\) and they are
$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ \[\\ yep \\x = {-b \pm \sqrt{b^2-4ac} \over 2a}.\]
</body>
</html>
Because the inline type text/x-mathjax-config
is not supported by cefpython
.
This creates:
Just as a quick pointer this does take a few seconds before the mathjax loads in, so it may be an idea to host a local MathJax config because this will allow for faster loading times most likely.
In relation to having a label above this, it would be rather simple to code this in by simply move the variable frame to grid(row=1, column=0...
(and changing the row weights) and then adding a label in grid(row=0, column=0...
.