I am new to Cefpython and PyQt5 both. I have tried to follow the tutorial in the cefpython repository. I was trying to embed cefpython inside a pyqt application and haven't achieved any success, what's wrong here?
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QWindow
from PyQt5.QtWidgets import *
from cefpython3 import cefpython as cef
from navbar import NavigationBar
class ChromiumApplication(QApplication):
def __init__(self):
super().__init__([])
self.timer = self.create_timer()
def create_timer(self):
timer = QTimer()
timer.timeout.connect(self.on_timeout)
timer.start(10)
return timer
def on_timeout(self):
cef.MessageLoopWork()
class ChromiumBrowserWindow(QMainWindow):
DEFAULT_TITLE = "Chromium Browser"
DEFAULT_WIDTH = 800
DEFAULT_HEIGHT = 600
def __init__(self):
super().__init__()
self.chrome = None
self.web_view = None
self.setWindowTitle(self.DEFAULT_TITLE)
self.init_window()
self.show()
def init_window(self):
self.resize(self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)
self.web_view = WebViewWidget(parent=self)
self.chrome = NavigationBar(parent=self, browser=self.web_view.browser)
layout = QGridLayout()
layout.addWidget(self.chrome, 0, 0)
layout.setColumnStretch(0, 1)
layout.addWidget(self.web_view, 1, 0)
layout.setRowStretch(1, 2)
layout.setContentsMargins(0, 0, 0, 0)
frame = QFrame()
frame.setLayout(layout)
self.setCentralWidget(frame)
self.web_view.init_browser()
def closeEvent(self, event):
if self.web_view.browser is not None:
self.web_view.browser.CloseBrowser(True) # force=True
self.web_view.browser = None # required to close cleanly
class WebViewWidget(QWidget):
DEFAULT_URL = "https://www.google.com"
HANDLERS = []
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
self.browser = None
self.browser_window = None
self.timer = None
def init_browser(self):
self.browser_window = QWindow()
window_config = cef.WindowInfo()
rect_pos_and_size = [0, 0, self.width(), self.height()]
window_config.SetAsChild(self.get_window_handle(), rect_pos_and_size)
self.browser = cef.CreateBrowserSync(window_config, url=self.DEFAULT_URL)
self.set_handlers()
def get_window_handle(self):
return int(self.browser_window.winId())
def set_handlers(self):
for handler in self.HANDLERS:
self.browser.SetClientHanlder(handler(self))
if __name__ == "__main__":
sys.excepthook = cef.ExceptHook
cef.Initialize()
app = ChromiumApplication()
window = ChromiumBrowserWindow()
app.exec()
app.timer.stop()
cef.Shutdown()
I know the issue is with my code because the example provided by cefpython works perfectly on my machine. I have no clue what I did wrong here, any suggestions will help!
EDIT: (code for navigation bar)
from PyQt5.QtWidgets import (
QApplication,
QFrame,
QHBoxLayout,
QPushButton,
QLineEdit,
)
class NavigationBar(QFrame):
def __init__(self, parent=None, browser=None):
super().__init__()
self.parent = parent
self.browser = browser
self.back_btn = self.create_button("Back")
self.back_btn.clicked.connect(self.on_back)
self.forward_btn = self.create_button("Forward")
self.forward_btn.clicked.connect(self.on_forward)
self.refresh_btn = self.create_button("Refresh")
self.refresh_btn.clicked.connect(self.on_refresh)
self.url_bar = self.create_url_bar()
self.url_bar.returnPressed.connect(self.on_search)
self.frame_layout = QHBoxLayout()
self.init_layout()
def create_button(self, name):
button_icon_path = f"./{name}.svg"
button = QPushButton()
button.setStyleSheet(f"""
QPushButton {{
background-image: url("{button_icon_path}");
background-repeat: no-repeat;
background-position: center;
background-color: rgba(0, 0, 0, 0.1);
border: 10px;
border-radius: 8px;
padding: 10px;
}}
QPushButton:hover {{
background-color: rgba(0, 0, 0, 0.5);
}}
QPushButton:pressed {{
background-color: none;
}}
""")
return button
def create_url_bar(self):
search = QLineEdit()
search.setStyleSheet("""QLineEdit {
min-width: 300px;
padding: 10px;
margin-left: 50px;
margin-right: 30px;
border-width: 10px;
border-radius: 8px;
background-color: rgba(0, 0, 0, 0.2);
color: white;
}
QLineEdit:hover {
background-color: #454549;
}
""")
return search
def init_layout(self):
self.setStyleSheet("""
background: #2A292E;
max-height: 40px;
""")
self.frame_layout.addWidget(self.back_btn, 0)
self.frame_layout.addWidget(self.forward_btn, 0)
self.frame_layout.addWidget(self.refresh_btn, 0)
self.frame_layout.addWidget(self.url_bar, 1)
self.setLayout(self.frame_layout)
def on_back(self):
if self.browser is not None:
self.browser.GoBack()
def on_forward(self):
if self.browser is not None:
self.browser.GoForward()
def on_refresh(self):
if self.browser is not None:
self.browser.Reload()
def on_search(self):
if self.browser is not None:
url = self.url_bar.text()
self.browser.LoadUrl(url)
if __name__ == "__main__":
app = QApplication([])
nav = NavigationBar()
nav.show()
app.exec()
The problem is that the QWindow
used to render the browser is hidden. The solution is to create a QWidget using QWidget::createWindowContainer()
and add it using a layout.
import sys
from cefpython3 import cefpython as cef
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QWindow
from PyQt5.QtWidgets import QApplication, QFrame, QGridLayout, QMainWindow, QVBoxLayout, QWidget
from navbar import NavigationBar
class ChromiumApplication(QApplication):
def __init__(self):
super().__init__([])
self.timer = self.create_timer()
def create_timer(self):
timer = QTimer()
timer.timeout.connect(self.on_timeout)
timer.start(10)
return timer
def on_timeout(self):
cef.MessageLoopWork()
class ChromiumBrowserWindow(QMainWindow):
DEFAULT_TITLE = "Chromium Browser"
DEFAULT_WIDTH = 800
DEFAULT_HEIGHT = 600
def __init__(self):
super().__init__()
self.chrome = None
self.web_view = None
self.setWindowTitle(self.DEFAULT_TITLE)
self.init_window()
self.show()
def init_window(self):
self.resize(self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)
self.web_view = WebViewWidget()
self.chrome = NavigationBar(parent=self, browser=self.web_view.browser)
frame = QFrame()
self.setCentralWidget(frame)
layout = QGridLayout(frame)
layout.addWidget(self.chrome, 0, 0)
layout.setColumnStretch(0, 1)
layout.addWidget(self.web_view, 1, 0)
layout.setRowStretch(1, 2)
layout.setContentsMargins(0, 0, 0, 0)
def closeEvent(self, event):
if self.web_view.browser is not None:
self.web_view.browser.CloseBrowser(True)
del self.web_view.browser
class WebViewWidget(QWidget):
DEFAULT_URL = "https://www.google.com"
HANDLERS = []
def __init__(self, parent=None):
super().__init__(parent)
self._browser = None
self._browser_widget = None
lay = QVBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
self.init_browser()
@property
def browser(self):
return self._browser
@browser.deleter
def browser(self):
self._browser = None
def init_browser(self):
browser_window = QWindow()
window_config = cef.WindowInfo()
window_config.SetAsChild(
int(browser_window.winId()), list(self.rect().getRect())
)
self._browser = cef.CreateBrowserSync(window_config, url=self.DEFAULT_URL)
self._browser_widget = QWidget.createWindowContainer(browser_window)
self.layout().addWidget(self._browser_widget)
self.set_handlers()
def set_handlers(self):
for handler in self.HANDLERS:
self.browser.SetClientHanlder(handler(self))
def resizeEvent(self, event):
if self.browser and self._browser_widget:
self.browser.SetBounds(*self._browser_widget.geometry().getRect())
self.browser.NotifyMoveOrResizeStarted()
if __name__ == "__main__":
sys.excepthook = cef.ExceptHook
cef.Initialize()
app = ChromiumApplication()
window = ChromiumBrowserWindow()
app.exec()
app.timer.stop()
cef.Shutdown()