# Example of embedding CEF Python browser using PyGObject/PyGI (GTK 3). # # Mac note: This example crashes on Mac with error message: # > _createMenuRef called with existing principal MenuRef.. # Reported as Issue #310. # # Linux note: This example is currently broken in v54+ on Linux (Issue #261). # It works fine with cefpython v53. # # Tested configurations: # - GTK 3.18 on Windows # - GTK 3.10 on Linux # - CEF Python v53.1+ from cefpython3 import cefpython as cef import ctypes import gi import os import platform import sys gi.require_version("Gtk", "3.0") # noinspection PyUnresolvedReferences from gi.repository import Gtk, GObject, Gdk, GdkPixbuf # noqa # Fix for PyCharm hints warnings when using static methods WindowUtils = cef.WindowUtils() # Platforms WINDOWS = (platform.system() == "Windows") LINUX = (platform.system() == "Linux") MAC = (platform.system() == "Darwin") # Linux imports if LINUX: # noinspection PyUnresolvedReferences from gi.repository import GdkX11 def main(): print("[gkt3.py] CEF Python {ver}".format(ver=cef.__version__)) print("[gkt3.py] Python {ver} {arch}".format( ver=platform.python_version(), arch=platform.architecture()[0])) print("[gkt3.py] GTK {major}.{minor}".format( major=Gtk.get_major_version(), minor=Gtk.get_minor_version())) assert cef.__version__ >= "53.1", "CEF Python v53.1+ required to run this" if not MAC: # On Mac exception hook doesn't work and is causing a strange error: # > Python[57738:d07] _createMenuRef called with existing principal # > MenuRef already associated with menu sys.excepthook = cef.ExceptHook # To shutdown CEF processes on error cef.Initialize() app = Gtk3Example() SystemExit(app.run(sys.argv)) class Gtk3Example(Gtk.Application): def __init__(self): super(Gtk3Example, self).__init__(application_id='cefpython.gtk3') self.browser = None self.window = None self.win32_handle = None def run(self, argv): GObject.threads_init() GObject.timeout_add(10, self.on_timer) self.connect("startup", self.on_startup) self.connect("activate", self.on_activate) self.connect("shutdown", self.on_shutdown) return super(Gtk3Example, self).run(argv) def get_handle(self): if WINDOWS: Gdk.threads_enter() ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p ctypes.pythonapi.PyCapsule_GetPointer.argtypes = \ [ctypes.py_object] gpointer = ctypes.pythonapi.PyCapsule_GetPointer( self.window.get_property("window").__gpointer__, None) libgdk = ctypes.CDLL("libgdk-3-0.dll") self.win32_handle = libgdk.gdk_win32_window_get_handle(gpointer) Gdk.threads_leave() return self.win32_handle elif LINUX: return self.window.get_property("window").get_xid() elif MAC: # TODO: Must call libgdk.gdk_quartz_window_get_nsview(gpointer) # similarly as on Windows. print("[gtk3.py] WARNING: get_handle not implemented on Mac") return 0 def on_timer(self): cef.MessageLoopWork() return True def on_startup(self, *_): self.window = Gtk.ApplicationWindow.new(self) self.window.set_title("GTK 3 example (PyGObject)") self.window.set_default_size(800, 600) self.window.connect("configure-event", self.on_configure) self.window.connect("size-allocate", self.on_size_allocate) self.window.connect("focus-in-event", self.on_focus_in) self.window.connect("delete-event", self.on_window_close) self.add_window(self.window) self.setup_icon() def on_activate(self, *_): self.window.realize() self.embed_browser() self.window.show_all() # Must set size of the window again after it was shown, # otherwise browser occupies only part of the window area. self.window.resize(*self.window.get_default_size()) def embed_browser(self): window_info = cef.WindowInfo() # TODO: on Mac pass rect[x, y, width, height] to SetAsChild window_info.SetAsChild(self.get_handle()) self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") def on_configure(self, *_): if self.browser: self.browser.NotifyMoveOrResizeStarted() return False def on_size_allocate(self, _, data): if self.browser: if WINDOWS: WindowUtils.OnSize(self.win32_handle, 0, 0, 0) elif LINUX: self.browser.SetBounds(data.x, data.y, data.width, data.height) def on_focus_in(self, *_): if self.browser: self.browser.SetFocus(True) return True return False def on_window_close(self, *_): if self.browser: self.browser.CloseBrowser(True) self.clear_browser_references() 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 def on_shutdown(self, *_): cef.Shutdown() def setup_icon(self): icon = os.path.join(os.path.dirname(__file__), "resources", "gtk.png") if not os.path.exists(icon): return pixbuf = GdkPixbuf.Pixbuf.new_from_file(icon) transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff) Gtk.Window.set_default_icon_list([transparent]) if __name__ == '__main__': main()