# An example of embedding CEF browser in wxPython on Linux.
# Tested with wxPython 2.8.12.1 (gtk2-unicode).
# To install wxPython type "sudo apt-get install python-wxtools".
# 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 a local module
ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL)
if 0x02070000 3:
# Do not spam too much.
return
print("[wxpython.py] DisplayHandler::OnStatusMessage()")
print(" value = %s" % value)
def OnConsoleMessage(self, browser, message, source, line):
print("[wxpython.py] DisplayHandler::OnConsoleMessage()")
print(" message = %s" % message)
print(" source = %s" % source)
print(" line = %s" % line)
# -------------------------------------------------------------------------
# KeyboardHandler
# -------------------------------------------------------------------------
def OnPreKeyEvent(self, browser, event, eventHandle,
isKeyboardShortcutOut):
stype = event["type"]
if stype == cefpython.KEYEVENT_RAWKEYDOWN:
stype = "RAWKEYDOWN"
elif stype == cefpython.KEYEVENT_KEYDOWN:
stype = "KEYDOWN"
elif stype == cefpython.KEYEVENT_KEYUP:
stype = "KEYUP"
elif stype == cefpython.KEYEVENT_CHAR:
stype = "CHAR"
print("[wxpython.py] KeyboardHandler::OnPreKeyEvent()")
print(" type=%s" % stype)
print(" modifiers=%s" % event["modifiers"])
print(" windows_key_code=%s" % event["windows_key_code"])
print(" native_key_code=%s" % event["native_key_code"])
print(" is_system_key=%s" % event["is_system_key"])
print(" character=%s" % event["character"])
print(" unmodified_character=%s" % event["unmodified_character"])
print(" focus_on_editable_field=%s"\
% event["focus_on_editable_field"])
def OnKeyEvent(self, browser, event, eventHandle):
pass
# -------------------------------------------------------------------------
# RequestHandler
# -------------------------------------------------------------------------
def OnBeforeBrowse(self, browser, frame, request, isRedirect):
print("[wxpython.py] RequestHandler::OnBeforeBrowse()")
print(" url = %s" % request.GetUrl()[:100])
return False
def OnBeforeResourceLoad(self, browser, frame, request):
print("[wxpython.py] RequestHandler::OnBeforeResourceLoad()")
print(" url = %s" % request.GetUrl()[:100])
return False
def OnResourceRedirect(self, browser, frame, oldUrl, newUrlOut, request):
print("[wxpython.py] RequestHandler::OnResourceRedirect()")
print(" old url = %s" % oldUrl[:100])
print(" new url = %s" % newUrlOut[0][:100])
def GetAuthCredentials(self, browser, frame, isProxy, host, port, realm,
scheme, callback):
# This callback is called on the IO thread, thus print messages
# may not be visible.
print("[wxpython.py] RequestHandler::GetAuthCredentials()")
print(" host = %s" % host)
print(" realm = %s" % realm)
callback.Continue(username="test", password="test")
return True
def OnQuotaRequest(self, browser, originUrl, newSize, callback):
print("[wxpython.py] RequestHandler::OnQuotaRequest()")
print(" origin url = %s" % originUrl)
print(" new size = %s" % newSize)
callback.Continue(True)
return True
def GetCookieManager(self, browser, mainUrl):
# Create unique cookie manager for each browser.
# You must set the "unique_request_context_per_browser"
# application setting to True for the cookie manager
# to work.
# Return None to have one global cookie manager for
# all CEF browsers.
if not browser:
# The browser param may be empty in some exceptional
# case, see docs.
return None
cookieManager = browser.GetUserData("cookieManager")
if cookieManager:
return cookieManager
else:
print("[wxpython.py] RequestHandler::GetCookieManager():"\
" created cookie manager")
cookieManager = cefpython.CookieManager.CreateManager("")
browser.SetUserData("cookieManager", cookieManager)
return cookieManager
def OnProtocolExecution(self, browser, url, allowExecutionOut):
# There's no default implementation for OnProtocolExecution on Linux,
# you have to make OS system call on your own. You probably also need
# to use LoadHandler::OnLoadError() when implementing this on Linux.
print("[wxpython.py] RequestHandler::OnProtocolExecution()")
print(" url = %s" % url)
if url.startswith("magnet:"):
print("[wxpython.py] Magnet link allowed!")
allowExecutionOut[0] = True
def _OnBeforePluginLoad(self, browser, mimeType, pluginUrl, topOriginUrl,
info):
# This is a global callback set using SetGlobalClientCallback().
# Plugins are loaded on demand, only when website requires it,
# the same plugin may be called multiple times.
# This callback is called on various threads, thus print messages
# may not be visible.
print("[wxpython.py] RequestHandler::_OnBeforePluginLoad()")
print(" mimeType = %s" % mimeType)
print(" pluginUrl = %s" % pluginUrl)
print(" topOriginUrl = %s" % topOriginUrl)
print(" info.GetName() = %s" % info.GetName())
print(" info.GetPath() = %s" % info.GetPath())
print(" info.GetVersion() = %s" % info.GetVersion())
print(" info.GetDescription() = %s" % info.GetDescription())
# False to allow, True to block plugin.
return False
def _OnCertificateError(self, certError, requestUrl, callback):
# This is a global callback set using SetGlobalClientCallback().
print("[wxpython.py] RequestHandler::_OnCertificateError()")
print(" certError = %s" % certError)
print(" requestUrl = %s" % requestUrl)
if requestUrl.startswith(
"https://tv.eurosport.com/do-not-allow"):
print(" Not allowed!")
return False
if requestUrl.startswith(
"https://tv.eurosport.com/"):
print(" Allowed!")
callback.Continue(True)
return True
return False
def OnRendererProcessTerminated(self, browser, status):
print("[wxpython.py] RequestHandler::OnRendererProcessTerminated()")
statuses = {
cefpython.TS_ABNORMAL_TERMINATION: "TS_ABNORMAL_TERMINATION",
cefpython.TS_PROCESS_WAS_KILLED: "TS_PROCESS_WAS_KILLED",
cefpython.TS_PROCESS_CRASHED: "TS_PROCESS_CRASHED"
}
statusName = "Unknown"
if status in statuses:
statusName = statuses[status]
print(" status = %s" % statusName)
def OnPluginCrashed(self, browser, pluginPath):
print("[wxpython.py] RequestHandler::OnPluginCrashed()")
print(" plugin path = %s" % pluginPath)
# -------------------------------------------------------------------------
# LoadHandler
# -------------------------------------------------------------------------
def OnLoadingStateChange(self, browser, isLoading, canGoBack,
canGoForward):
print("[wxpython.py] LoadHandler::OnLoadingStateChange()")
print(" isLoading = %s, canGoBack = %s, canGoForward = %s" \
% (isLoading, canGoBack, canGoForward))
def OnLoadStart(self, browser, frame):
print("[wxpython.py] LoadHandler::OnLoadStart()")
print(" frame url = %s" % frame.GetUrl()[:100])
def OnLoadEnd(self, browser, frame, httpStatusCode):
print("[wxpython.py] LoadHandler::OnLoadEnd()")
print(" frame url = %s" % frame.GetUrl()[:100])
# For file:// urls the status code = 0
print(" http status code = %s" % httpStatusCode)
# Tests for the Browser object methods
self._Browser_LoadUrl(browser)
def _Browser_LoadUrl(self, browser):
if browser.GetUrl() == "data:text/html,Test#Browser.LoadUrl":
browser.LoadUrl("file://"+GetApplicationPath("wxpython.html"))
def OnLoadError(self, browser, frame, errorCode, errorTextList, failedUrl):
print("[wxpython.py] LoadHandler::OnLoadError()")
print(" frame url = %s" % frame.GetUrl()[:100])
print(" error code = %s" % errorCode)
print(" error text = %s" % errorTextList[0])
print(" failed url = %s" % failedUrl)
# Handle ERR_ABORTED error code, to handle the following cases:
# 1. Esc key was pressed which calls browser.StopLoad() in OnKeyEvent
# 2. Download of a file was aborted
if errorCode == cefpython.ERR_ABORTED:
print("[wxpython.py] LoadHandler::OnLoadError(): Ignoring load"\
" error: Esc was pressed or file download was aborted")
return;
customErrorMessage = "My custom error message!"
frame.LoadUrl("data:text/html,%s" % customErrorMessage)
# -------------------------------------------------------------------------
# LifespanHandler
# -------------------------------------------------------------------------
# ** This callback is executed on the IO thread **
# Empty place-holders: popupFeatures, client.
def OnBeforePopup(self, browser, frame, targetUrl, targetFrameName,
targetDisposition, userGesture,
popupFeatures, windowInfo, client, browserSettings,
noJavascriptAccess):
print("[wxpython.py] LifespanHandler::OnBeforePopup()")
print(" targetUrl = %s" % targetUrl)
# Custom browser settings for popups:
# > browserSettings[0] = {"plugins_disabled": True}
# Set WindowInfo object:
# > windowInfo[0] = cefpython.WindowInfo()
allowPopups = True
return not allowPopups
def _OnAfterCreated(self, browser):
# This is a global callback set using SetGlobalClientCallback().
print("[wxpython.py] LifespanHandler::_OnAfterCreated()")
print(" browserId=%s" % browser.GetIdentifier())
def DoClose(self, browser):
print("[wxpython.py] LifespanHandler::DoClose()")
print(" browserId=%s" % browser.GetIdentifier())
def OnBeforeClose(self, browser):
print("[wxpython.py] LifespanHandler::OnBeforeClose")
print(" browserId=%s" % browser.GetIdentifier())
# -------------------------------------------------------------------------
# JavascriptDialogHandler
# -------------------------------------------------------------------------
def OnJavascriptDialog(self, browser, originUrl, dialogType,
messageText, defaultPromptText, callback,
suppressMessage):
print("[wxpython.py] JavascriptDialogHandler::OnJavascriptDialog()")
print(" originUrl="+originUrl)
print(" dialogType="+str(dialogType))
print(" messageText="+messageText)
print(" defaultPromptText="+defaultPromptText)
# If you want to suppress the javascript dialog:
# suppressMessage[0] = True
return False
def OnBeforeUnloadJavascriptDialog(self, browser, messageText, isReload,
callback):
print("[wxpython.py] OnBeforeUnloadJavascriptDialog()")
print(" messageText="+messageText)
print(" isReload="+str(isReload))
# Return True if the application will use a custom dialog:
# callback.Continue(allow=True, userInput="")
# return True
return False
def OnResetJavascriptDialogState(self, browser):
print("[wxpython.py] OnResetDialogState()")
def OnJavascriptDialogClosed(self, browser):
print("[wxpython.py] OnDialogClosed()")
class MyApp(wx.App):
timer = None
timerID = 1
timerCount = 0
def OnInit(self):
if not USE_EVT_IDLE:
self.CreateTimer()
frame = MainFrame()
self.SetTopWindow(frame)
frame.Show()
return True
def CreateTimer(self):
# See "Making a render loop":
# http://wiki.wxwidgets.org/Making_a_render_loop
# Another approach is to use EVT_IDLE in MainFrame,
# see which one fits you better.
self.timer = wx.Timer(self, self.timerID)
self.timer.Start(10) # 10ms
wx.EVT_TIMER(self, self.timerID, self.OnTimer)
def OnTimer(self, event):
self.timerCount += 1
# print("[wxpython.py] OnTimer() %d" % self.timerCount)
cefpython.MessageLoopWork()
def OnExit(self):
# When app.MainLoop() returns, MessageLoopWork() should
# not be called anymore.
if not USE_EVT_IDLE:
self.timer.Stop()
def GetSources():
# Get sources of all python functions and methods from this file.
# This is to provide sources preview to wxpython.html.
# The dictionary of functions is binded to "window.sources".
thisModule = sys.modules[__name__]
functions = inspect.getmembers(thisModule, inspect.isfunction)
classes = inspect.getmembers(thisModule, inspect.isclass)
sources = {}
for funcTuple in functions:
sources[funcTuple[0]] = inspect.getsource(funcTuple[1])
for classTuple in classes:
className = classTuple[0]
classObject = classTuple[1]
methods = inspect.getmembers(classObject)
for methodTuple in methods:
try:
sources[methodTuple[0]] = inspect.getsource(\
methodTuple[1])
except:
pass
return sources
if __name__ == '__main__':
print('[wxpython.py] wx.version=%s' % wx.version())
# Intercept python exceptions. Exit app immediately when exception
# happens on any of the threads.
sys.excepthook = ExceptHook
# Application settings
settings = {
# CEF Python debug messages in console and in log_file
"debug": True,
# Set it to LOGSEVERITY_VERBOSE for more details
"log_severity": cefpython.LOGSEVERITY_INFO,
# Set to "" to disable logging to a file
"log_file": GetApplicationPath("debug.log"),
# These directories must be set on Linux
"locales_dir_path": cefpython.GetModuleDirectory()+"/locales",
"resources_dir_path": cefpython.GetModuleDirectory(),
# The "subprocess" executable that launches the Renderer
# and GPU processes among others. You may rename that
# executable if you like.
"browser_subprocess_path": "%s/%s" % (
cefpython.GetModuleDirectory(), "subprocess"),
# This option is required for the GetCookieManager callback
# to work. It affects renderer processes, when this option
# is set to True. It will force a separate renderer process
# for each browser created using CreateBrowserSync.
"unique_request_context_per_browser": True,
# Downloads are handled automatically. A default SaveAs file
# dialog provided by OS will be displayed.
"downloads_enabled": True,
# Remote debugging port, required for Developer Tools support.
# A value of 0 will generate a random port. To disable devtools
# support set it to -1.
"remote_debugging_port": 0,
# Mouse context menu
"context_menu": {
"enabled": True,
"navigation": True, # Back, Forward, Reload
"print": True,
"view_source": True,
"external_browser": True, # Open in external browser
"devtools": True, # Developer Tools
},
# See also OnCertificateError which allows you to ignore
# certificate errors for specific websites.
"ignore_certificate_errors": False,
}
# Browser settings. You may have different settings for each
# browser, see the call to CreateBrowserSync.
g_browserSettings = {
# "plugins_disabled": True,
# "file_access_from_file_urls_allowed": True,
# "universal_access_from_file_urls_allowed": True,
}
# Command line switches set programmatically
switches = {
# "proxy-server": "socks5://127.0.0.1:8888",
# "no-proxy-server": "",
# "enable-media-stream": "",
# "remote-debugging-port": "12345",
# "disable-gpu": "",
# "--invalid-switch": "" -> Invalid switch name
}
cefpython.Initialize(settings, switches)
app = MyApp(False)
app.MainLoop()
# Let wx.App destructor do the cleanup before calling cefpython.Shutdown().
del app
cefpython.Shutdown()