0

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()
1
  • just added the code for that. Commented Sep 3, 2021 at 7:51

1 Answer 1

1

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()

enter image description here

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.