-
Notifications
You must be signed in to change notification settings - Fork 548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Creating a mixin class to support both WebKit and WebEngine #3663
Conversation
Turns out we'll also need to make some JS changes to support both webkit and webengine, which you might have already been looking into. Just sharing for my own amusement. =) |
Hmm. I have to wrap my head around this. I don't have a lot of familiarity with mixins (which are sort of the exact opposite of abstraction, really) — I've never had much call to use them. And I don't think it's necessary here, either. If you just want to do fallback replacement of the base class, Python provides that in a much easier form with module aliasing: try:
from PyQt5.QtWebEngineWidgets import QWebEngineView as WebViewParent
web_view_implementation = "WebEngine"
except ImportError:
try:
from PyQt5.QtWebKitWidgets import QWebView as WebViewParent
web_view_implementation = "WebKit"
except ImportError:
raise Exception("Could not find supported web backend!", exc_info=1)
class TimelineWebView(WebViewParent, updates.UpdateInterface):
... In terms of the overridable QtWidgets APIs that we subclass the views to reimplement, the two WebFooView classes should be totally interchangeable. Of course, that becomes even easier if the backend communication isn't part of the view (as it shouldn't be, anyway), which is where a timeline model class still comes in handy. But that's if you just want the code to pick up whatever web implementation it can find, to use as the base class. I was thinking of an abstract implementation where the backend could even be user-selectable (at least in theory), on systems where both backends are available — like any recent Linux distro, for example. That's where a PIMPL-style implementation comes into play, since our timeline class would gain a However, if I'm understanding them correctly, mixins might actually be useful in that scenario, to solve the problem of needing to override all the same methods in whichever view gets chosen. The API customizations we normally perform by subclassing could theoretically become a mixin (or mixins) applied to the Qt-standard view classes (rather than custom subclasses) in a write-once-sprinkle-liberally sort of way, instead of all the rote (and repetitive) method-overriding — which of course is Qt's norm, since C++ is pretty much incompatible with the mixin pattern. In fact, if we were to build (say) a
Hm, bummer. ...Is the WebEngine-style JS definitely incompatible with WebKit? I was hoping they'd play nice. It's worth noting that QtWebChannel is present in all recent releases/builds of Qt, and is not in any way dependent on QtWebEngine — so hopefully we can leave the new WebChannel communication plumbing in place, even when legacy WebKit is the backend. If we're using MSYS2 PyQt5 packages, we don't have to settle for legacy Qt 5.5 or whatever — their current (5.15.0) Qt and PyQt5 packages include QtWebKit still, and definitely include QtWebChannel as well. Only QtWebEngine is missing. I was hoping to approach it from the assumption that we can let the QtWebChannel stuff ride regardless which backend is loaded, and see how that worked out. (Maybe badly, though?) |
I don't see anyway to call |
I am experimenting with calling setHtml (instead of setUrl), so we can make some substitutions, such as including a different mixin-BACKEND.js in the index.html. That would allow us to initialize things correctly for each backend... in theory. |
…ill be found and will work)
@ferdnyc Regarding aliasing, we are essentially doing that here (at the end of the Take a look at the most recent commit. I've inserted in a JS file, based on the backend (WebEngine or WebKit). Each one does something slightly different to initialize things. The WebEngine version must define a global I was also able to add custom JS logging for the WebKit version, but that same approach failed with WebEngine (it always turned the page white and blank with no logging at all). So, I left the WebEngine version without custom logging. As far as I can tell, this PR successfully supports both WebEngine and WebKit fully. |
@ferdnyc Here is my plan. I'm going to merge this into the
Then, in the future, when one of us is so inclined... we can do a more extensive overhaul of the timeline, separating model and view, etc... That seems like a great plan to me! |
Sorry, must've missed or spaced on these.
Yeah, you can't — We'd have to do the work ourselves of connecting up the
Looks like it broke in the diff --git a/src/windows/views/timeline_mixins.py b/src/windows/views/timeline_mixins.py
index 2a553c40..92e05ef4 100644
--- a/src/windows/views/timeline_mixins.py
+++ b/src/windows/views/timeline_mixins.py
@@ -37,6 +37,7 @@ from functools import partial
try:
# Attempt to import QtWebEngine
from PyQt5.QtWebChannel import QWebChannel
+ from PyQt5.QtWebEngineWidgets import QWebEnginePage as WebPageClass
from PyQt5.QtWebEngineWidgets import QWebEngineView
IS_WEBENGINE_VALID = True
except ImportError:
@@ -45,11 +46,12 @@ except ImportError:
try:
# Attempt to import QtWebKit
- from PyQt5.QtWebKitWidgets import QWebView, QWebPage
+ from PyQt5.QtWebKitWidgets import QWebView
+ if not IS_WEBENGINE_VALID:
+ from PyQt5.QtWebKitWidgets import QWebPage as WebPageClass
IS_WEBKIT_VALID = True
except ImportError:
QWebView = object # Prevent inheritance errors
- QWebPage = object
IS_WEBKIT_VALID = False
@@ -143,11 +145,14 @@ class TimelineQtWebEngineMixin(TimelineBaseMixin, QWebEngineView):
event.ignore()
-class LoggingWebPage(QWebPage):
+class LoggingWebPage(WebPageClass):
"""Override console.log message to display messages"""
def javaScriptConsoleMessage(self, msg, line, source):
log.warning('JS: %s line %d: %s' % (source, line, msg))
+ def __init__(self):
+ super().__init__()
+
class TimelineQtWebKitMixin(TimelineBaseMixin, QWebView):
"""QtWebKit Timeline Widget"""
|
Well, I take that back... I accidentally left out of my diff the part where I do: self.setPage(LoggingWebPage()) In the |
Ah, here we go. I needed to make the
|
Ohhh-kay. Bit of a delay there, I went on a detour exploring a logging idea I had. I wanted to see if I could get the logs coming from JS to display the JavaScript filename in place of the Python module name, when the messages are logged. The answer, should it ever come up, is: No. Not without it requiring way more effort than would be worth it, anyway. So, I gave up on that, but my PR #3681 contains changes (significantly less hackish than the above) to set up logging in either web backend. The QWebEnginePage logging did indeed add another parameter to the calls, a log level — they now have three of them. So, those will get mapped to the appropriate |
@ferdnyc What do you think as a short term solution to support both Webkit and Webengine? I just threw this together quickly as a demonstration of what might work.