# An example of embedding the CEF browser in PyGTK on Linux.
# Tested with GTK "2.24.10".
# The official CEF Python binaries come with tcmalloc hook
# disabled. But if you've built custom binaries and kept tcmalloc
# hook enabled, then be aware that in such case it is required
# for the cefpython module to be the very first import in
# python scripts. See Issue 73 in the CEF Python Issue Tracker
# for more details.
import ctypes, os, sys
libcef_so = os.path.join(os.path.dirname(os.path.abspath(__file__)),\
'libcef.so')
if os.path.exists(libcef_so):
# Import local module
ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL)
if 0x02070000 <= sys.hexversion < 0x03000000:
import cefpython_py27 as cefpython
else:
raise Exception("Unsupported python version: %s" % sys.version)
else:
# Import from package
from cefpython3 import cefpython
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import re
def GetApplicationPath(file=None):
import re, os, platform
# On Windows after downloading file and calling Browser.GoForward(),
# current working directory is set to %UserProfile%.
# Calling os.path.dirname(os.path.realpath(__file__))
# returns for eg. "C:\Users\user\Downloads". A solution
# is to cache path on first call.
if not hasattr(GetApplicationPath, "dir"):
if hasattr(sys, "frozen"):
dir = os.path.dirname(sys.executable)
elif "__file__" in globals():
dir = os.path.dirname(os.path.realpath(__file__))
else:
dir = os.getcwd()
GetApplicationPath.dir = dir
# If file is None return current directory without trailing slash.
if file is None:
file = ""
# Only when relative path.
if not file.startswith("/") and not file.startswith("\\") and (
not re.search(r"^[\w-]+:", file)):
path = GetApplicationPath.dir + os.sep + file
if platform.system() == "Windows":
path = re.sub(r"[/\\]+", re.escape(os.sep), path)
path = re.sub(r"[/\\]+$", "", path)
return path
return str(file)
def ExceptHook(excType, excValue, traceObject):
import traceback, os, time, codecs
# This hook does the following: in case of exception write it to
# the "error.log" file, display it to the console, shutdown CEF
# and exit application immediately by ignoring "finally" (_exit()).
errorMsg = "\n".join(traceback.format_exception(excType, excValue,
traceObject))
errorFile = GetApplicationPath("error.log")
try:
appEncoding = cefpython.g_applicationSettings["string_encoding"]
except:
appEncoding = "utf-8"
if type(errorMsg) == bytes:
errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace")
try:
with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp:
fp.write("\n[%s] %s\n" % (
time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg))
except:
print("[pygtk_.py]: WARNING: failed writing to error file: %s" % (
errorFile))
# Convert error message to ascii before printing, otherwise
# you may get error like this:
# | UnicodeEncodeError: 'charmap' codec can't encode characters
errorMsg = errorMsg.encode("ascii", errors="replace")
errorMsg = errorMsg.decode("ascii", errors="replace")
print("\n"+errorMsg+"\n")
cefpython.QuitMessageLoop()
cefpython.Shutdown()
os._exit(1)
class PyGTKExample:
mainWindow = None
container = None
browser = None
exiting = None
searchEntry = None
vbox = None
def __init__(self):
self.mainWindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.mainWindow.connect('destroy', self.OnExit)
self.mainWindow.set_size_request(width=800, height=600)
self.mainWindow.set_title('PyGTK CEF example')
self.mainWindow.realize()
self.vbox = gtk.VBox(False, 0)
self.vbox.pack_start(self.CreateMenu(), False, False, 0)
self.mainWindow.add(self.vbox)
m = re.search("GtkVBox at 0x(\w+)", str(self.vbox))
hexID = m.group(1)
windowID = int(hexID, 16)
windowInfo = cefpython.WindowInfo()
windowInfo.SetAsChild(windowID)
# Linux requires adding "file://" for local files,
# otherwise /home/some will be replaced as http://home/some
self.browser = cefpython.CreateBrowserSync(
windowInfo,
browserSettings={},
navigateUrl="file://"+GetApplicationPath("example.html"))
self.vbox.show()
self.mainWindow.show()
gobject.timeout_add(10, self.OnTimer)
def CreateMenu(self):
file = gtk.MenuItem('File')
file.show()
filemenu = gtk.Menu()
item = gtk.MenuItem('Open')
filemenu.append(item)
item.show()
item = gtk.MenuItem('Exit')
filemenu.append(item)
item.show()
file.set_submenu(filemenu)
about = gtk.MenuItem('About')
about.show()
aboutmenu = gtk.Menu()
item = gtk.MenuItem('CEF Python')
aboutmenu.append(item)
item.show()
about.set_submenu(aboutmenu)
menubar = gtk.MenuBar()
menubar.append(file)
menubar.append(about)
menubar.show()
return menubar
def OnWidgetClick(self, widget, data):
self.mainWindow.get_window().focus()
def OnTimer(self):
if self.exiting:
return False
cefpython.MessageLoopWork()
return True
def OnFocusIn(self, widget, data):
# This function is currently not called by any of code,
# but if you would like for browser to have automatic focus
# add such line:
# self.mainWindow.connect('focus-in-event', self.OnFocusIn)
self.browser.SetFocus(True)
def OnExit(self, widget, data=None):
self.exiting = True
gtk.main_quit()
if __name__ == '__main__':
version = '.'.join(map(str, list(gtk.gtk_version)))
print('[pygtk_.py] GTK version: %s' % version)
# Intercept python exceptions. Exit app immediately when exception
# happens on any of the threads.
sys.excepthook = ExceptHook
# Application settings
settings = {
"debug": True, # cefpython debug messages in console and in log_file
"log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE
"log_file": GetApplicationPath("debug.log"), # Set to "" to disable
# This directories must be set on Linux
"locales_dir_path": cefpython.GetModuleDirectory()+"/locales",
"resources_dir_path": cefpython.GetModuleDirectory(),
"browser_subprocess_path": "%s/%s" % (
cefpython.GetModuleDirectory(), "subprocess"),
}
cefpython.Initialize(settings)
gobject.threads_init() # Timer for the message loop
PyGTKExample()
gtk.main()
cefpython.Shutdown()