|
| 1 | +# This example is throwing segmentation faults when run on Linux, |
| 2 | +# the problem seems to be in a call to CreateBrowserSync(), |
| 3 | +# the backtrace leads to CefBrowserHostImpl::PlatformCreateWindow(). |
| 4 | +# Full backtrace: |
| 5 | +""" |
| 6 | +0x00007ffff1ee52a0 in CefBrowserHostImpl::PlatformCreateWindow() () |
| 7 | + from /home/czarek/cefpython/cefpython/cef3/linux/binaries_64bit/libcef.so |
| 8 | +(gdb) backtrace |
| 9 | +#0 0x00007ffff1ee52a0 in CefBrowserHostImpl::PlatformCreateWindow() () |
| 10 | + from /home/czarek/cefpython/cefpython/cef3/linux/binaries_64bit/libcef.so |
| 11 | +#1 0x00007ffff1e24930 in CefBrowserHostImpl::Create(CefWindowInfo const&, CefStructBase<CefBrowserSettingsTraits> const&, CefRefPtr<CefClient>, content::WebContents*, scoped_refptr<CefBrowserInfo>, _GtkWidget*) () |
| 12 | + from /home/czarek/cefpython/cefpython/cef3/linux/binaries_64bit/libcef.so |
| 13 | +#2 0x00007ffff1e251ec in CefBrowserHost::CreateBrowserSync(CefWindowInfo const&, CefRefPtr<CefClient>, CefStringBase<CefStringTraitsUTF16> const&, CefStructBase<CefBrowserSettingsTraits> const&) () |
| 14 | + from /home/czarek/cefpython/cefpython/cef3/linux/binaries_64bit/libcef.so |
| 15 | +#3 0x00007ffff1dc9fec in cef_browser_host_create_browser_sync () |
| 16 | + from /home/czarek/cefpython/cefpython/cef3/linux/binaries_64bit/libcef.so |
| 17 | +#4 0x00007fffe8136e4f in CefBrowserHost::CreateBrowserSync(CefWindowInfo const&, CefRefPtr<CefClient>, CefStringBase<CefStringTraitsUTF16> const&, CefStructBase<CefBrowserSettingsTraits> const&) () |
| 18 | + from /home/czarek/cefpython/cefpython/cef3/linux/binaries_64bit/cefpython_py27.so |
| 19 | +#5 0x00007fffe8111cce in __pyx_pf_14cefpython_py27_16CreateBrowserSync ( |
| 20 | + __pyx_v_windowInfo=<cefpython_py27.WindowInfo at remote 0xdc7fb0>, |
| 21 | + __pyx_v_browserSettings=<optimized out>, __pyx_v_navigateUrl= |
| 22 | + 'file:///home/czarek/cefpython/cefpython/cef3/linux/binaries_64bit/example.html', __pyx_self=<optimized out>) at cefpython.cpp:65142 |
| 23 | +#6 0x00007fffe8114ea6 in __pyx_pw_14cefpython_py27_17CreateBrowserSync ( |
| 24 | +""" |
| 25 | + |
| 26 | +# An example of embedding CEF Python in PyQt4 application. |
| 27 | + |
| 28 | +# On Ubuntu install the "python-qt4" package. |
| 29 | +# Tested with version 4.9.1-2ubuntu1. |
| 30 | + |
| 31 | +# Important: |
| 32 | +# On Linux importing the cefpython module must be |
| 33 | +# the very first in your application. This is because CEF makes |
| 34 | +# a global tcmalloc hook for memory allocation/deallocation. |
| 35 | +# See Issue 73 that is to provide CEF builds with tcmalloc disabled: |
| 36 | +# https://code.google.com/p/cefpython/issues/detail?id=73 |
| 37 | + |
| 38 | +import ctypes, os, sys |
| 39 | +libcef_so = os.path.join(os.path.dirname(os.path.abspath(__file__)),\ |
| 40 | + 'libcef.so') |
| 41 | +# Import a local module if exists, otherwise import from |
| 42 | +# an installed package. |
| 43 | +if os.path.exists(libcef_so): |
| 44 | + ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL) |
| 45 | + if 0x02070000 <= sys.hexversion < 0x03000000: |
| 46 | + import cefpython_py27 as cefpython |
| 47 | + else: |
| 48 | + raise Exception("Unsupported python version: %s" % sys.version) |
| 49 | +else: |
| 50 | + from cefpython3 import cefpython |
| 51 | + |
| 52 | +from PyQt4 import QtGui |
| 53 | +from PyQt4 import QtCore |
| 54 | + |
| 55 | +TEST_RESPONSE_READING = False |
| 56 | + |
| 57 | +def GetApplicationPath(file=None): |
| 58 | + import re, os, platform |
| 59 | + # If file is None return current directory without trailing slash. |
| 60 | + if file is None: |
| 61 | + file = "" |
| 62 | + # Only when relative path. |
| 63 | + if not file.startswith("/") and not file.startswith("\\") and ( |
| 64 | + not re.search(r"^[\w-]+:", file)): |
| 65 | + if hasattr(sys, "frozen"): |
| 66 | + path = os.path.dirname(sys.executable) |
| 67 | + elif "__file__" in globals(): |
| 68 | + path = os.path.dirname(os.path.realpath(__file__)) |
| 69 | + else: |
| 70 | + path = os.getcwd() |
| 71 | + path = path + os.sep + file |
| 72 | + if platform.system() == "Windows": |
| 73 | + path = re.sub(r"[/\\]+", re.escape(os.sep), path) |
| 74 | + path = re.sub(r"[/\\]+$", "", path) |
| 75 | + return path |
| 76 | + return str(file) |
| 77 | + |
| 78 | +def ExceptHook(excType, excValue, traceObject): |
| 79 | + import traceback, os, time, codecs |
| 80 | + # This hook does the following: in case of exception write it to |
| 81 | + # the "error.log" file, display it to the console, shutdown CEF |
| 82 | + # and exit application immediately by ignoring "finally" (os._exit()). |
| 83 | + errorMsg = "\n".join(traceback.format_exception(excType, excValue, |
| 84 | + traceObject)) |
| 85 | + errorFile = GetApplicationPath("error.log") |
| 86 | + try: |
| 87 | + appEncoding = cefpython.g_applicationSettings["string_encoding"] |
| 88 | + except: |
| 89 | + appEncoding = "utf-8" |
| 90 | + if type(errorMsg) == bytes: |
| 91 | + errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") |
| 92 | + try: |
| 93 | + with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: |
| 94 | + fp.write("\n[%s] %s\n" % ( |
| 95 | + time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) |
| 96 | + except: |
| 97 | + print("cefpython: WARNING: failed writing to error file: %s" % ( |
| 98 | + errorFile)) |
| 99 | + # Convert error message to ascii before printing, otherwise |
| 100 | + # you may get error like this: |
| 101 | + # | UnicodeEncodeError: 'charmap' codec can't encode characters |
| 102 | + errorMsg = errorMsg.encode("ascii", errors="replace") |
| 103 | + errorMsg = errorMsg.decode("ascii", errors="replace") |
| 104 | + print("\n"+errorMsg+"\n") |
| 105 | + cefpython.QuitMessageLoop() |
| 106 | + cefpython.Shutdown() |
| 107 | + os._exit(1) |
| 108 | + |
| 109 | +class MainWindow(QtGui.QMainWindow): |
| 110 | + mainFrame = None |
| 111 | + |
| 112 | + def __init__(self): |
| 113 | + super(MainWindow, self).__init__(None) |
| 114 | + self.createMenu() |
| 115 | + self.mainFrame = MainFrame(self) |
| 116 | + self.setCentralWidget(self.mainFrame) |
| 117 | + self.resize(1024, 768) |
| 118 | + self.setWindowTitle('PyQT CEF 3 example') |
| 119 | + self.setFocusPolicy(QtCore.Qt.StrongFocus) |
| 120 | + |
| 121 | + def createMenu(self): |
| 122 | + menubar = self.menuBar() |
| 123 | + filemenu = menubar.addMenu("&File") |
| 124 | + filemenu.addAction(QtGui.QAction("Open", self)) |
| 125 | + filemenu.addAction(QtGui.QAction("Exit", self)) |
| 126 | + aboutmenu = menubar.addMenu("&About") |
| 127 | + |
| 128 | + def focusInEvent(self, event): |
| 129 | + cefpython.WindowUtils.OnSetFocus( |
| 130 | + int(self.centralWidget().winId()), 0, 0, 0) |
| 131 | + |
| 132 | + def closeEvent(self, event): |
| 133 | + self.mainFrame.browser.CloseBrowser() |
| 134 | + |
| 135 | +class MainFrame(QtGui.QWidget): |
| 136 | + browser = None |
| 137 | + |
| 138 | + def __init__(self, parent=None): |
| 139 | + super(MainFrame, self).__init__(parent) |
| 140 | + windowInfo = cefpython.WindowInfo() |
| 141 | + windowInfo.SetAsChild(int(self.winId())) |
| 142 | + # Linux requires adding "file://" for local files, |
| 143 | + # otherwise /home/some will be replaced as http://home/some |
| 144 | + self.browser = cefpython.CreateBrowserSync(windowInfo, |
| 145 | + browserSettings={}, |
| 146 | + navigateUrl="file://"+GetApplicationPath("example.html")) |
| 147 | + self.show() |
| 148 | + |
| 149 | + def moveEvent(self, event): |
| 150 | + cefpython.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) |
| 151 | + |
| 152 | + def resizeEvent(self, event): |
| 153 | + cefpython.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) |
| 154 | + |
| 155 | +class CefApplication(QtGui.QApplication): |
| 156 | + timer = None |
| 157 | + |
| 158 | + def __init__(self, args): |
| 159 | + super(CefApplication, self).__init__(args) |
| 160 | + self.createTimer() |
| 161 | + |
| 162 | + def createTimer(self): |
| 163 | + self.timer = QtCore.QTimer() |
| 164 | + self.timer.timeout.connect(self.onTimer) |
| 165 | + self.timer.start(10) |
| 166 | + |
| 167 | + def onTimer(self): |
| 168 | + # The proper way of doing message loop should be: |
| 169 | + # 1. In createTimer() call self.timer.start(0) |
| 170 | + # 2. In onTimer() call MessageLoopWork() only when |
| 171 | + # QtGui.QApplication.instance()->hasPendingEvents() |
| 172 | + # returns False. |
| 173 | + # But there is a bug in Qt, hasPendingEvents() returns |
| 174 | + # always true. |
| 175 | + # (The behavior described above was tested on Windows |
| 176 | + # with pyqt 4.8, maybe this is not true anymore, |
| 177 | + # test it TODO) |
| 178 | + cefpython.MessageLoopWork() |
| 179 | + |
| 180 | + def stopTimer(self): |
| 181 | + # Stop the timer after Qt message loop ended, calls to |
| 182 | + # MessageLoopWork() should not happen anymore. |
| 183 | + self.timer.stop() |
| 184 | + |
| 185 | +if __name__ == '__main__': |
| 186 | + print("PyQt version: %s" % QtCore.PYQT_VERSION_STR) |
| 187 | + print("QtCore version: %s" % QtCore.qVersion()) |
| 188 | + |
| 189 | + sys.excepthook = ExceptHook |
| 190 | + cefpython.g_debug = True |
| 191 | + cefpython.g_debugFile = GetApplicationPath("debug.log") |
| 192 | + |
| 193 | + settings = {} |
| 194 | + settings["log_file"] = GetApplicationPath("debug.log") |
| 195 | + settings["log_severity"] = cefpython.LOGSEVERITY_INFO |
| 196 | + settings["release_dcheck_enabled"] = True # Enable only when debugging |
| 197 | + settings["locales_dir_path"] = cefpython.GetModuleDirectory()+"/locales" |
| 198 | + settings["resources_dir_path"] = cefpython.GetModuleDirectory() |
| 199 | + settings["browser_subprocess_path"] = "%s/%s" % ( |
| 200 | + cefpython.GetModuleDirectory(), "subprocess") |
| 201 | + |
| 202 | + cefpython.Initialize(settings) |
| 203 | + |
| 204 | + app = CefApplication(sys.argv) |
| 205 | + mainWindow = MainWindow() |
| 206 | + mainWindow.show() |
| 207 | + app.exec_() |
| 208 | + app.stopTimer() |
| 209 | + |
| 210 | + # Need to destroy QApplication(), otherwise Shutdown() fails. |
| 211 | + # Unset main window also just to be safe. |
| 212 | + del mainWindow |
| 213 | + del app |
| 214 | + |
| 215 | + cefpython.Shutdown() |
0 commit comments