QWebEngineView change anchor behavior

Heads up! You've already completed this tutorial.

John_Alden | 2020-11-18 14:08:31 UTC | #1

I'm using QWebEngineView to display html that's been produced from markdown. Clicking a link causes the loaded web page to replace the entire contents of the QWebEngineView widget. I'd like to launch the page in a separate browser window. Does it have anything I could override to get my hands on the event and do it myself.


martin | 2020-11-18 14:08:39 UTC | #2

Hi @John_Alden welcome to the forum & nice question. This has changed since earlier versions of PyQt so it wasn't so easy to piece it together, but I have a working example.

The key part is the CustomWebEnginePage class. By creating this custom page class we can intercept the acceptNavigationRequest signal and decline it for specific types of links (here we're intercepting only NavigationLinkRequested). For these links we create a custom web engine view, set the URL and then display the window. Note that we need to keep a reference to the created window (in external_windows) so it isn't destroyed. For everything else, we pass through to the handler on the parent class via super().

python
class CustomWebEnginePage(QWebEnginePage):
    """ Custom WebEnginePage to customize how we handle link navigation """
    # Store external windows.
    external_windows = []

    def acceptNavigationRequest(self, url,  _type, isMainFrame):
        if _type == QWebEnginePage.NavigationTypeLinkClicked:
            w = QWebEngineView()
            w.setUrl(url)
            w.show()

            # Keep reference to external window, so it isn't cleared up.
            self.external_windows.append(w)
            return False
        return super().acceptNavigationRequest(url,  _type, isMainFrame)

To make sure our custom page is used, we need to set it on the browser with .setPage() (wherever it is created).

python
        self.browser = QWebEngineView()
        self.browser.setPage(CustomWebEnginePage(self))
        self.browser.setUrl(QUrl("http://google.com"))

Note that in this example we're creating multiple external windows (rather than just sending all links to the same one). If you want to do that instead, you can do something like --

python
class WebEnginePage(QWebEnginePage):
    # Store second window.
    external_window = None

    def acceptNavigationRequest(self, url,  _type, isMainFrame):
        print(url, _type, isMainFrame)
        if _type == QWebEnginePage.NavigationTypeLinkClicked:
            if not self.external_window:
                self.external_window = QWebEngineView()

            self.external_window.setUrl(url)
            self.external_window.show()
            return False

        return super().acceptNavigationRequest(url,  _type, isMainFrame)

Hope that helps!


John_Alden | 2020-11-18 02:55:10 UTC | #3

Thanks very much, Martin. That's exactly what I was looking for. It's working great! Thanks so much for taking the problem on!


martin | 2020-11-19 13:01:09 UTC | #4

Glad it helped @John_Alden -- I've written up a longer guide to this including how to conditionally open windows, and opening links in the system default browser.


The complete guide to packaging Python GUI applications with PyInstaller.
[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

QWebEngineView change anchor behavior was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.