# An example of embedding CEF browser in wxPython on Windows.
# Tested with wxPython 2.8.12.1 and 3.0.2.0.
import os, sys
libcef_dll = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'libcef.dll')
if os.path.exists(libcef_dll):
# Import a local module
if (2,7) <= sys.version_info < (2,8):
import cefpython_py27 as cefpython
elif (3,4) <= sys.version_info < (3,4):
import cefpython_py34 as cefpython
else:
raise Exception("Unsupported python version: %s" % sys.version)
else:
# Import an installed package
from cefpython3 import cefpython
import wx
import time
import re
import uuid
import platform
import inspect
import struct
# -----------------------------------------------------------------------------
# Globals
g_applicationSettings = None
g_browserSettings = None
g_commandLineSwitches = None
# Which method to use for message loop processing.
# EVT_IDLE - wx application has priority
# EVT_TIMER - cef browser has priority (default)
# It seems that Flash content behaves better when using a timer.
# Not sure if using EVT_IDLE is correct, it doesn't work on Linux,
# on Windows it works fine. See also the post by Robin Dunn:
# https://groups.google.com/d/msg/wxpython-users/hcNdMEx8u48/MD5Jgbm_k1kJ
USE_EVT_IDLE = False # If False then Timer will be used
TEST_EMBEDDING_IN_PANEL = True
# -----------------------------------------------------------------------------
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" (os._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("[wxpython.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 MainFrame(wx.Frame):
browser = None
mainPanel = None
def GetHandleForBrowser(self):
if self.mainPanel:
return self.mainPanel.GetHandle()
else:
return self.GetHandle()
def __init__(self, url=None, popup=False):
if popup:
title = "wxPython Popup"
else:
title = "wxPython CEF 3 example"
wx.Frame.__init__(self, parent=None, id=wx.ID_ANY,
title=title)
size=(800,600)
# This is an optional code to enable High DPI support.
if "auto_zooming" in g_applicationSettings \
and g_applicationSettings["auto_zooming"] == "system_dpi":
# This utility function will adjust width/height using
# OS DPI settings. For 800/600 with Win7 DPI settings
# being set to "Larger 150%" will return 1200/900.
size = cefpython.DpiAware.CalculateWindowSize(size[0], size[1])
self.SetSize(size)
if not url:
url = "file://"+GetApplicationPath("wxpython.html")
# Test hash in url.
# url += "#test-hash"
self.CreateMenu()
if TEST_EMBEDDING_IN_PANEL:
print("Embedding in a wx.Panel!")
# You also have to set the wx.WANTS_CHARS style for
# all parent panels/controls, if it's deeply embedded.
self.mainPanel = wx.Panel(self, style=wx.WANTS_CHARS)
# Global client callbacks must be set before browser is created.
self.clientHandler = ClientHandler()
cefpython.SetGlobalClientCallback("OnCertificateError",
self.clientHandler._OnCertificateError)
cefpython.SetGlobalClientCallback("OnBeforePluginLoad",
self.clientHandler._OnBeforePluginLoad)
cefpython.SetGlobalClientCallback("OnAfterCreated",
self.clientHandler._OnAfterCreated)
windowInfo = cefpython.WindowInfo()
windowInfo.SetAsChild(self.GetHandleForBrowser())
self.browser = cefpython.CreateBrowserSync(windowInfo,
browserSettings=g_browserSettings,
navigateUrl=url)
self.clientHandler.mainBrowser = self.browser
self.browser.SetClientHandler(self.clientHandler)
jsBindings = cefpython.JavascriptBindings(
bindToFrames=False, bindToPopups=True)
jsBindings.SetFunction("PyPrint", PyPrint)
jsBindings.SetProperty("pyProperty", "This was set in Python")
jsBindings.SetProperty("pyConfig", ["This was set in Python",
{"name": "Nested dictionary", "isNested": True},
[1,"2", None]])
self.javascriptExternal = JavascriptExternal(self.browser)
jsBindings.SetObject("external", self.javascriptExternal)
jsBindings.SetProperty("sources", GetSources())
self.browser.SetJavascriptBindings(jsBindings)
if self.mainPanel:
self.mainPanel.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
self.mainPanel.Bind(wx.EVT_SIZE, self.OnSize)
else:
self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_CLOSE, self.OnClose)
if USE_EVT_IDLE and not popup:
# Bind EVT_IDLE only for the main application frame.
print("Using EVT_IDLE to execute the CEF message loop work")
self.Bind(wx.EVT_IDLE, self.OnIdle)
def CreateMenu(self):
filemenu = wx.Menu()
filemenu.Append(1, "Open")
exit = filemenu.Append(2, "Exit")
self.Bind(wx.EVT_MENU, self.OnClose, exit)
aboutmenu = wx.Menu()
aboutmenu.Append(1, "CEF Python")
menubar = wx.MenuBar()
menubar.Append(filemenu,"&File")
menubar.Append(aboutmenu, "&About")
self.SetMenuBar(menubar)
def OnSetFocus(self, event):
cefpython.WindowUtils.OnSetFocus(self.GetHandleForBrowser(), 0, 0, 0)
def OnSize(self, event):
cefpython.WindowUtils.OnSize(self.GetHandleForBrowser(), 0, 0, 0)
def OnClose(self, event):
# Remove all CEF browser references so that browser is closed
# cleanly. Otherwise there may be issues for example with cookies
# not being flushed to disk when closing app immediately
# (Issue 158).
del self.javascriptExternal.mainBrowser
del self.clientHandler.mainBrowser
del self.browser
# Destroy wx frame, this will complete the destruction of CEF browser
self.Destroy()
# In wx.chromectrl calling browser.CloseBrowser and/or self.Destroy
# may cause crashes when embedding multiple browsers in tab
# (Issue 107). In such case instead of calling CloseBrowser/Destroy
# try this code:
# | self.browser.ParentWindowWillClose()
# | event.Skip()
def OnIdle(self, event):
cefpython.MessageLoopWork()
def PyPrint(message):
print("[wxpython.py] PyPrint: "+message)
class JavascriptExternal:
mainBrowser = None
stringVisitor = None
def __init__(self, mainBrowser):
self.mainBrowser = mainBrowser
def GoBack(self):
self.mainBrowser.GoBack()
def GoForward(self):
self.mainBrowser.GoForward()
def SetZoomLevel(self, zoomLevel):
self.mainBrowser.SetZoomLevel(zoomLevel)
def CreateAnotherBrowser(self, url=None):
frame = MainFrame(url=url)
frame.Show()
def Print(self, message):
print("[wxpython.py] Print: "+message)
def TestAllTypes(self, *args):
print("[wxpython.py] TestAllTypes: "+str(args))
def ExecuteFunction(self, *args):
self.mainBrowser.ExecuteFunction(*args)
def TestJSCallback(self, jsCallback):
print("[wxpython.py] jsCallback.GetFunctionName() = %s"\
% jsCallback.GetFunctionName())
print("[wxpython.py] jsCallback.GetFrame().GetIdentifier() = %s" % \
jsCallback.GetFrame().GetIdentifier())
jsCallback.Call("This message was sent from python using js callback")
def TestJSCallbackComplexArguments(self, jsObject):
jsCallback = jsObject["myCallback"];
jsCallback.Call(1, None, 2.14, "string", ["list", ["nested list", \
{"nested object":None}]], \
{"nested list next":[{"deeply nested object":1}]})
def TestPythonCallback(self, jsCallback):
jsCallback.Call(self.PyCallback)
def PyCallback(self, *args):
message = "PyCallback() was executed successfully! "\
"Arguments: %s" % str(args)
print("[wxpython.py] "+message)
self.mainBrowser.GetMainFrame().ExecuteJavascript(
"window.alert(\"%s\")" % message)
def GetSource(self):
# Must keep a strong reference to the StringVisitor object
# during the visit.
self.stringVisitor = StringVisitor()
self.mainBrowser.GetMainFrame().GetSource(self.stringVisitor)
def GetText(self):
# Must keep a strong reference to the StringVisitor object
# during the visit.
self.stringVisitor = StringVisitor()
self.mainBrowser.GetMainFrame().GetText(self.stringVisitor)
def ShowDevTools(self):
print("[wxpython.py] external.ShowDevTools called")
self.mainBrowser.ShowDevTools()
# -------------------------------------------------------------------------
# Cookies
# -------------------------------------------------------------------------
cookieVisitor = None
def VisitAllCookies(self):
# Need to keep the reference alive.
self.cookieVisitor = CookieVisitor()
cookieManager = self.mainBrowser.GetUserData("cookieManager")
if not cookieManager:
print("\n[wxpython.py] Cookie manager not yet created! Visit"\
" the cookietester website first and create some cookies")
return
cookieManager.VisitAllCookies(self.cookieVisitor)
def VisitUrlCookies(self):
# Need to keep the reference alive.
self.cookieVisitor = CookieVisitor()
cookieManager = self.mainBrowser.GetUserData("cookieManager")
if not cookieManager:
print("\n[wxpython.py] Cookie manager not yet created! Visit"\
" the cookietester website first and create some cookies")
return
cookieManager.VisitUrlCookies(
"http://www.html-kit.com/tools/cookietester/",
False, self.cookieVisitor)
# .www.html-kit.com
def SetCookie(self):
cookieManager = self.mainBrowser.GetUserData("cookieManager")
if not cookieManager:
print("\n[wxpython.py] Cookie manager not yet created! Visit"\
"the cookietester website first and create some cookies")
return
cookie = cefpython.Cookie()
cookie.SetName("Created_Via_Python")
cookie.SetValue("yeah really")
cookieManager.SetCookie("http://www.html-kit.com/tools/cookietester/",
cookie)
print("\n[wxpython.py] Cookie created! Visit html-kit cookietester to"\
" see it")
def DeleteCookies(self):
cookieManager = self.mainBrowser.GetUserData("cookieManager")
if not cookieManager:
print("\n[wxpython.py] Cookie manager not yet created! Visit"\
" the cookietester website first and create some cookies")
return
cookieManager.DeleteCookies(
"http://www.html-kit.com/tools/cookietester/",
"Created_Via_Python")
print("\n[wxpython.py] Cookie deleted! Visit html-kit cookietester "\
"to see the result")
class StringVisitor:
def Visit(self, string):
print("\n[wxpython.py] StringVisitor.Visit(): string:")
print("--------------------------------")
print(string)
print("--------------------------------")
class CookieVisitor:
def Visit(self, cookie, count, total, deleteCookie):
if count == 0:
print("\n[wxpython.py] CookieVisitor.Visit(): total cookies: %s"\
% total)
print("\n[wxpython.py] CookieVisitor.Visit(): cookie:")
print(" "+str(cookie.Get()))
# True to continue visiting cookies
return True
class ClientHandler:
mainBrowser = None # May be None for global client callbacks.
def __init__(self):
pass
# -------------------------------------------------------------------------
# DisplayHandler
# -------------------------------------------------------------------------
def OnAddressChange(self, browser, frame, url):
print("[wxpython.py] DisplayHandler::OnAddressChange()")
print(" url = %s" % url)
def OnTitleChange(self, browser, title):
print("[wxpython.py] DisplayHandler::OnTitleChange()")
print(" title = %s" % title)
def OnTooltip(self, browser, textOut):
# OnTooltip not yet implemented (both Linux and Windows),
# will be fixed in next CEF release, see Issue 783:
# https://code.google.com/p/chromiumembedded/issues/detail?id=783
print("[wxpython.py] DisplayHandler::OnTooltip()")
print(" text = %s" % textOut[0])
statusMessageCount = 0
def OnStatusMessage(self, browser, value):
if not value:
# Do not notify in the console about empty statuses.
return
self.statusMessageCount += 1
if self.statusMessageCount > 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):
print("[wxpython.py] KeyboardHandler::OnPreKeyEvent()")
def OnKeyEvent(self, browser, event, eventHandle):
if event["type"] == cefpython.KEYEVENT_KEYUP:
# OnKeyEvent is called twice for F5/Esc keys, with event
# type KEYEVENT_RAWKEYDOWN and KEYEVENT_KEYUP.
# Normal characters a-z should have KEYEVENT_CHAR.
return False
print("[wxpython.py] KeyboardHandler::OnKeyEvent()")
print(" type=%s" % event["type"])
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"])
linux = (platform.system() == "Linux")
windows = (platform.system() == "Windows")
# F5
if (linux and event["native_key_code"] == 71) \
or (windows and event["windows_key_code"] == 116):
print("[wxpython.py] F5 pressed, calling"
" browser.ReloadIgnoreCache()")
browser.ReloadIgnoreCache()
return True
# Escape
if (linux and event["native_key_code"] == 9) \
or (windows and event["windows_key_code"] == 27):
print("[wxpython.py] Esc pressed, calling browser.StopLoad()")
browser.StopLoad()
return True
# F12
if (linux and event["native_key_code"] == 96) \
or (windows and event["windows_key_code"] == 123):
print("[wxpython.py] F12 pressed, calling"
" browser.ShowDevTools()")
browser.ShowDevTools()
return True
return False
# -------------------------------------------------------------------------
# RequestHandler
# -------------------------------------------------------------------------
def OnBeforeBrowse(self, browser, frame, request, isRedirect):
print("[wxpython.py] RequestHandler::OnBeforeBrowse()")
print(" url = %s" % request.GetUrl()[:100])
# Handle "magnet:" links.
if request.GetUrl().startswith("magnet:"):
print("[wxpython.p] RequestHandler::OnBeforeBrowse(): "
"magnet link clicked, cancelling browse request")
return True
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("")
if "cache_path" in g_applicationSettings:
path = g_applicationSettings["cache_path"]
# path = os.path.join(path, "cookies_browser_{}".format(
# browser.GetIdentifier()))
cookieManager.SetStoragePath(path)
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 == "https://testssl-expire.disig.sk/index.en.html":
print(" Not allowed!")
return False
if requestUrl \
== "https://testssl-expire.disig.sk/index.en.html?allow=1":
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
# 3. Certificate error
if errorCode == cefpython.ERR_ABORTED:
print("[wxpython.py] LoadHandler::OnLoadError(): Ignoring load "
"error: Esc was pressed or file download was aborted, "
"or there was certificate error")
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()
# On Windows there are keyboard problems in popups, when popup
# is created using "window.open" or "target=blank". This issue
# occurs only in wxPython. PyGTK or PyQt do not require this fix.
# The solution is to create window explicitilly, and not depend
# on CEF to create window internally. See Issue 80 for details:
# https://github.com/cztomczak/cefpython/issues/80
# If you set allowPopups=True then CEF will create popup window.
# The wx.Frame cannot be created here, as this callback is
# executed on the IO thread. Window should be created on the UI
# thread. One solution is to call cefpython.CreateBrowser()
# which runs asynchronously and can be called on any thread.
# The other solution is to post a task on the UI thread, so
# that cefpython.CreateBrowserSync() can be used.
# Note that if you return True and create the popup window yourself,
# then the popup window and parent window will not be able to script
# each other. There will be no "window.opener" property available
# in the popup window.
cefpython.PostTask(cefpython.TID_UI, self._CreatePopup, targetUrl)
allowPopups = False
return not allowPopups
def _CreatePopup(self, url):
frame = MainFrame(url=url, popup=True)
frame.Show()
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
mainFrame = None
def OnInit(self):
if not USE_EVT_IDLE:
print("[wxpython.py] Using TIMER to run CEF message loop")
self.CreateTimer()
self.mainFrame = MainFrame()
self.SetTopWindow(self.mainFrame)
self.mainFrame.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):
cefpython.MessageLoopWork()
def OnExit(self):
# When app.MainLoop() returns, MessageLoopWork() should
# not be called anymore.
print("[wxpython.py] MyApp.OnExit")
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] architecture=%s-bit' % (8 * struct.calcsize("P")))
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
g_applicationSettings = {
# Disk cache
# "cache_path": "webcache/",
# 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": True,
}
# You can comment out the code below if you do not want High
# DPI support. If you disable it text will look fuzzy on
# high DPI displays.
#
# Enabling High DPI support in app can be done by
# embedding a DPI awareness xml manifest in executable
# (see Issue 112 comment #2), or by calling SetProcessDpiAware
# function. Embedding xml manifest is the most reliable method.
# The downside of calling SetProcessDpiAware is that scrollbar
# in CEF browser is smaller than it should be. This is because
# DPI awareness was set too late, after the CEF dll was loaded.
# To fix that embed DPI awareness xml manifest in the .exe file.
#
# There is one bug when enabling High DPI support - fonts in
# javascript dialogs (alert) are tiny. However, you can implement
# custom javascript dialogs using JavascriptDialogHandler.
#
# Additionally you have to set "auto_zomming" application
# setting. High DPI support is available only on Windows.
# You may set auto_zooming to "system_dpi" and browser
# contents will be zoomed using OS DPI settings. On Win7
# these can be set in: Control Panel > Appearance and
# Personalization > Display.
#
# Example values for auto_zooming are:
# "system_dpi", "0.0" (96 DPI), "1.0" (120 DPI),
# "2.0" (144 DPI), "-1.0" (72 DPI)
# Numeric value means a zoom level.
# Example values that can be set in Win7 DPI settings:
# Smaller 100% (Default) = 96 DPI = 0.0 zoom level
# Medium 125% = 120 DPI = 1.0 zoom level
# Larger 150% = 144 DPI = 2.0 zoom level
# Custom 75% = 72 DPI = -1.0 zoom level
g_applicationSettings["auto_zooming"] = "system_dpi"
print("[wxpython.py] Calling SetProcessDpiAware")
cefpython.DpiAware.SetProcessDpiAware()
# 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
g_commandLineSwitches = {
# "proxy-server": "socks5://127.0.0.1:8888",
# "no-proxy-server": "",
# "enable-media-stream": "",
# "disable-gpu": "",
}
cefpython.Initialize(g_applicationSettings, g_commandLineSwitches)
app = MyApp(False)
app.MainLoop()
# Let wx.App destructor do the cleanup before calling
# cefpython.Shutdown(). This is to ensure reliable CEF shutdown.
del app
cefpython.Shutdown()