# Example of embedding CEF Python browser using wxPython library. # This example has a top menu and a browser widget without navigation bar. # To install wxPython on Linux type "sudo apt-get install python-wxtools". # Tested configurations: # - wxPython 2.8 on Linux # - wxPython 3.0 on Windows # - CEF Python v55.4+ import wx from cefpython3 import cefpython as cef import platform import sys import os # Fix for PyCharm hints warnings WindowUtils = cef.WindowUtils() # Platforms WINDOWS = (platform.system() == "Windows") LINUX = (platform.system() == "Linux") MAC = (platform.system() == "Darwin") # Configuration WIDTH = 800 HEIGHT = 600 def main(): check_versions() sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error settings = {} if WINDOWS: # High DPI support settings["auto_zooming"] = "system_dpi" # Embed DPI awareness xml manifest inside .exe (recommended, # most reliable) or call the SetProcessDpiAware function. # noinspection PyUnresolvedReferences, PyArgumentList cef.DpiAware.SetProcessDpiAware() cef.Initialize(settings=settings) app = CefApp(False) app.MainLoop() del app # Must destroy before calling Shutdown cef.Shutdown() def check_versions(): print("[wxpython.py] CEF Python {ver}".format(ver=cef.__version__)) print("[wxpython.py] Python {ver}".format(ver=sys.version[:6])) print("[wxpython.py] wxPython {ver}".format(ver=wx.version())) # CEF Python version requirement assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" class MainFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, title='wxPython example', size=(WIDTH, HEIGHT)) self.browser = None self.setup_icon() self.create_menu() self.Bind(wx.EVT_CLOSE, self.OnClose) # Set wx.WANTS_CHARS style for the keyboard to work. # This style also needs to be set for all parent controls. self.browser_panel = wx.Panel(self, style=wx.WANTS_CHARS) self.browser_panel.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) self.browser_panel.Bind(wx.EVT_SIZE, self.OnSize) # Must show so that handle is available when embedding browser self.Show() self.embed_browser() def setup_icon(self): icon_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), "resources", "wxpython.png") if os.path.exists(icon_file): icon = wx.IconFromBitmap(wx.Bitmap(icon_file, wx.BITMAP_TYPE_PNG)) self.SetIcon(icon) def create_menu(self): filemenu = wx.Menu() filemenu.Append(1, "Some option") filemenu.Append(2, "Another option") aboutmenu = wx.Menu() aboutmenu.Append(1, "Yet another option") menubar = wx.MenuBar() menubar.Append(filemenu, "&File") menubar.Append(aboutmenu, "&About") self.SetMenuBar(menubar) def embed_browser(self): window_info = cef.WindowInfo() window_info.SetAsChild(self.browser_panel.GetHandle()) self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") self.browser.SetClientHandler(FocusHandler()) def OnSetFocus(self, _): if not self.browser: return if WINDOWS: WindowUtils.OnSetFocus(self.browser_panel.GetHandle(), 0, 0, 0) self.browser.SetFocus(True) def OnSize(self, _): if not self.browser: return if WINDOWS: WindowUtils.OnSize(self.browser_panel.GetHandle(), 0, 0, 0) elif LINUX: (x, y) = (0, 0) (width, height) = self.browser_panel.GetSizeTuple() self.browser.SetBounds(x, y, width, height) self.browser.NotifyMoveOrResizeStarted() def OnClose(self, event): # In cefpython3.wx.chromectrl example calling browser.CloseBrowser() # and/or self.Destroy() in OnClose is causing crashes when # embedding multiple browser tabs. The solution is to call only # browser.ParentWindowWillClose. Behavior of this example # seems different as it extends wx.Frame, while ChromeWindow # from chromectrl extends wx.Window. Calling CloseBrowser # and Destroy does not cause crashes, but is not recommended. # Call ParentWindowWillClose and event.Skip() instead. See # also Issue #107: https://github.com/cztomczak/cefpython/issues/107 self.browser.ParentWindowWillClose() event.Skip() # Clear all browser references for CEF to shutdown cleanly del self.browser class FocusHandler(object): def __init__(self): pass def OnTakeFocus(self, **kwargs): # print("[wxpython.py] FocusHandler.OnTakeFocus, next={next}" # .format(next=kwargs["next_component"]])) pass def OnSetFocus(self, **kwargs): # source_enum = {cef.FOCUS_SOURCE_NAVIGATION: "navigation", # cef.FOCUS_SOURCE_SYSTEM: "system"} # print("[wxpython.py] FocusHandler.OnSetFocus, source={source}" # .format(source=source_enum[kwargs["source"]])) # return False pass def OnGotFocus(self, browser, **_): # Temporary fix for focus issues on Linux (Issue #284). # If this is not applied then when switching to another # window (alt+tab) and then back to this example, keyboard # focus becomes broken, you can't type anything, even # though a type cursor blinks in web view. if LINUX: print("[wxpython.py] FocusHandler.OnGotFocus:" " keyboard focus fix (#284)") browser.SetFocus(True) class CefApp(wx.App): def __init__(self, redirect): self.timer = None self.timer_id = 1 super(CefApp, self).__init__(redirect=redirect) def OnInit(self): self.create_timer() frame = MainFrame() self.SetTopWindow(frame) frame.Show() return True def create_timer(self): # See also "Making a render loop": # http://wiki.wxwidgets.org/Making_a_render_loop # Another way would be to use EVT_IDLE in MainFrame. self.timer = wx.Timer(self, self.timer_id) self.timer.Start(10) # 10ms wx.EVT_TIMER(self, self.timer_id, self.on_timer) def on_timer(self, _): cef.MessageLoopWork() def OnExit(self): self.timer.Stop() if __name__ == '__main__': main()