How to use CEFPython to add a WebBrowser widget in a Tkinter window? How to use CEFPython to add a WebBrowser widget in a Tkinter window? tkinter tkinter

How to use CEFPython to add a WebBrowser widget in a Tkinter window?


So, there are 2 core issues with your main code:

  1. You didn't initialize cefpython
  2. Your browser_frame was not configured to fit the whole area

In short, the answer is:

import tkinter as tkfrom tkinter import *from cefpython3 import cefpython as cefimport ctypesdef 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 = Noneclass 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")        passif __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:

Image

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:

CefPython with MathJax

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....