Skip to content
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

Initial support for QtWebEngine and Async callbacks for timeline/Qt Integration #3604

Merged
merged 77 commits into from
Aug 17, 2020

Conversation

jonoomph
Copy link
Member

@jonoomph jonoomph commented Jul 13, 2020

  • Requires QtWebEngine and Qt 5.6+
  • Requires OpenGL python module (to fix black screens on some systems)
  • Enables smooth scrolling on timeline
  • All callbacks from JS to Python are Async

Based on the initial work: #2512

Related to PRs:
OpenShot/libopenshot-audio#100
OpenShot/libopenshot#549

…ntegration.

 - Requires QtWebEngine and Qt 5.6+
 - Requires OpenGL python module (to fix black screens on some systems)
 - Enables smooth scrolling on timeline
@jonoomph
Copy link
Member Author

jonoomph commented Jul 13, 2020

This is going to fail on almost all build servers for a little while. My current plan is...

  • Get travis succeeding (not sure the best approach on that one yet)
  • Create a new Linux builder based on Ubuntu Bionic to run in parallel with our current builder. Then I'll tell this branch to use the Bionic builder, with newer dependencies and newer Qt versions.
  • Install newer Qt and Sip and PyQt5 libraries on Mac, in a new folder, so our old mac builders will continue to work. Then adjust our .gitlab-ci.yml file to point at the newer Qt folders. This should work... I think.
  • Windows builders: not sure what needs to happen here yet. Probably needs a newer version of Qt as well, and some freeze changes, to ensure we are including the QtWebEngine* libraries.

Any suggestions or patches are welcome! 🙏 Thx!

@ferdnyc
Copy link
Contributor

ferdnyc commented Jul 15, 2020

@jonoomph
Travis' problem was a coincidental release of cx_Freeze 6.2, which it appears doesn't like our freeze.py. Builds were failing in every other branch as well. I just locked the version at 6.1 for now, until we can figure out what the issue is.

But, due to whatever other changes you've made to the freeze code in the meantime, it doesn't look like this branch's freeze.py is compatible with 6.1 anymore, either.

@jonoomph
Copy link
Member Author

@ferdnyc Yeah, I ended up using the same version ('5.1.1') of cx_Freeze that we used on the previous Linux builder, because it was 1 less thing to fix at this point. However, I'm all for supporting the latest cx_Freeze, and if someone wants to investigate that, I'll be happy to assist and work on builders to support it!

@jonoomph
Copy link
Member Author

Quick Update: I have a working AppImage created on our Bionic build server! I've tested the AppImage with Ubuntu 18.04 and 20.04. However, I'm pretty sure that further tweaks will be needed for wider compatibility. @ferdnyc Perhaps you can test the AppImage (from GitLab build artifacts on the new-webengine-support branch if you have time.)

On a side note, we might consider switching our build process for Linux to use https://github.com/probonopd/linuxdeployqt, in the future. It looks pretty nice, and will simply our build-server.py script quite a bit, and will probably yield a much more intelligent AppImage, with respect for Qt required files, and required dependencies.

Comment on lines 1932 to 1934
cmd = JS_SCOPE_SELECTOR + ".setAudioData('{}',{});".format(
right_clip.id, self.waveform_cache.get(right_clip.id))
self.page().mainFrame().evaluateJavaScript(cmd)
self.run_js(JS_SCOPE_SELECTOR + ".setAudioData('{}',{});".format(right_clip.id, self.waveform_cache.get(right_clip.id)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aw. I was really hoping to keep those lines narrow enough that they didn't require horizontal scrolling just to read them on GitHub. (Which was the whole point of splitting them up — not the GitHub part, just the length in general.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, sorry. I was trying to simplify things as much as possible, but I also don't want crazy long lines.

Comment on lines 179 to 183
# Not ready, try again in a few milliseconds
if retries > 1:
log.warning("TimelineWebView::eval_js() called before document ready event. Script queued: %s" % code)
QTimer.singleShot(100, partial(self.eval_js, code, retries + 1))
# Not ready, try again in a few moments
log.error("TimelineWebView::run_js() called before document ready event. Script queued: %s" % code)
QTimer.singleShot(50, partial(self.run_js, code, callback))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this less likely to happen, with QtWebEngine? (He asks, hopefully.) Because, on my system, OpenShot used to log that message a minimum of four times — sometimes six to eight times — at each launch, when it was using the old values and didn't skip retries. Which is incredibly annoying and unnecessary.

I'm also not crazy about logging an error for something that we expect to happen — to the point that we just retry the operation, proceeding as if everything's fine. Is there something wrong, or is it expected? I feel like logging it as an error sends mixed messages — heck, even a warning may be excessive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might set a threshold for the number of tries we expect might fail, and only log warnings after X # of attempts. That would still show an error in extreme cases where the document.ready event fails for some reason. I like that approach. 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might set a threshold for the number of tries we expect might fail, and only log warnings after X # of attempts. That would still show an error in extreme cases where the document.ready event fails for some reason. I like that approach.

...So, exactly what the previous code used to do? #JustSayin 😉

# Init javascript bounding box (for snapping support)
code = JS_SCOPE_SELECTOR + ".startManualMove('{}', '{}');".format(self.item_type, self.item_id)
self.eval_js(code)
self.run_js(JS_SCOPE_SELECTOR + ".getJavaScriptPosition({}, {});".format(event_position.x(), event_position.y()), partial(callback, self, data))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as earlier comment, re: the width.

Comment on lines +45 to +49
try:
# Solution to solve QtWebEngineWidgets black screen caused by OpenGL not loaded
from OpenGL import GL
except ImportError:
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I don't know about this. There isn't even a Python module OpenGL on my system.

I wonder if the issue is the QtOpenGL setup not being done at application start? There was something in the documentation about that, I remember having trouble with that during one of my abortive experiments. (Specifically, the afternoon I spent unsuccessfully trying to get openshot-player to render to a QOpenGLWidget.)

Apparently, right after creating the QApplication, you should configure the application's default OpenGL surface handling. In C++ that was:

    QApplication a(argc, argv);

    // Configure all OpenGL contexts
    QSurfaceFormat glFormat;
    glFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
    QSurfaceFormat::setDefaultFormat(glFormat);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll give this a try! I figured the PyOpenGL was just a work-around until I figured out what was happening. Without it, I only get a black rectangle for the timeline.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm. Well, if it works, maybe it doesn't hurt anything to have it there regardless.

My system now does have an OpenGL module, because PyOpenGL was added to the requirements.txt when pipreqs scanned the source tree, so it's not like it was a really difficult dependency to satisfy.

(Of course, it's only present in the source to be found because of this import line, so the whole thing's sort of circular.)

Comment on lines 71 to 79
// Bind to keydown event (to detect SHIFT)
body_object.keydown(function (event) {
if (event.which === 16) {
if (timeline) {
timeline.qt_log("Shift pressed!");
}
body_object.scope().shift_pressed = true;
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, funny story about these. Turns out, scope() is a debugging tool, and we'd actually stand to make a significant performance gain if we could turn off Angular debugging on the Timeline. But then scope() wouldn't work from JQuery.

Unfortunately, I have tried desperately to get shift-tracking working with AngularJS's own ng-keydown and ng-keyup, and I have never gotten it to work anything close to reliably.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if we could disable debugging, but still somehow access Angular, that would work fine. But I have no idea how to do that. If I have time, I'll do some research on this.

Copy link
Contributor

@ferdnyc ferdnyc Jul 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking, in theory we could add hidden <input>s to the HTML, and store the data involved in those. Then, JQuery could update the values without needing scope(), and Angular could access them directly... well, however we want, really. Using jQuery selectors, using angular.element(), etc.

I'm thinking something like,

<html><body>
<stuff stuff stuff>
<input type="hidden" id="shift_state" value="0">
</stuff stuff stuff>
<script>
    $("body").keydown(function (event) {
        if (event.which ===  16) $("#shift_state").val(1);
    });
    $("body").keyup(function (event) {
        if (event.which ===  16) $("#shift_state").val(0);
    });
</script>
<script>
// currently, the only place where shift_pressed is ever read...
function moveBoundingBox(scope, previous_x, previous_y, x_offset, y_offset, left, top) {
    var snapping_result = Object();
    snapping_result.left = left;
    snapping_result.top = top;

    // Check for shift key
    if (parseInt($("#shift_state").val(), 2) === 1) {
        // freeze X movement
        x_offset = 0;
    }
</script>

I mean, I can tell you that with the timeline loaded in a browser window and the debugging console open, I can see the value="" attribute of the <input> flipping from "0" to "1" to "0" as I press and release Shift, so it at least works to that extent...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonoomph

Yeah, if we could disable debugging, but still somehow access Angular, that would work fine. But I have no idea how to do that. If I have time, I'll do some research on this.

@me

I was thinking, in theory we could add hidden <input>s to the HTML, and store the data involved in those. Then, JQuery could update the values without needing scope(), and Angular could access them directly... well, however we want, really. Using jQuery selectors, using angular.element(), etc.

...Actually I suspect there's no point in doing that, and I doubt we'll be able to get away from scope() any time soon. I didn't realize until yesterday that the entire Qt < == > JS interface depends on scope():

# Constants used by this file
JS_SCOPE_SELECTOR = "$('body').scope()"

Maybe if we were to rewrite their entire communication layer as a WebChannel, or something like that, then we could get away from scope(). But for now, it seems it's here to stay.

Comment on lines 2001 to 2009
# Callback function, to redraw audio data after an update
def callback(self, clip_id, callback_data):
has_audio_data = callback_data
print('has_audio_data: %s' % has_audio_data)

if has_audio_data:
# Re-generate waveform since volume curve has changed
self.Show_Waveform_Triggered(clip_id)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not put this right before the run_js() call it's passed to? There's no reason it needs to be defined right at the top of the function; seems like it just makes the code harder to read.

Ooh, hey — ­if self is only used to look up the function the callback is going to call, could you just pass that directly, instead? Then the callback can be just a simple function, instead of looking like a method. I'm thinking something like:

# ...prior stuff in function...
def cb(clip_id, fn, result):
    if result:
        fn(clip_id)
self.run_js(JS_SCOPE_SELECTOR + ".hasAudioData('{}');".format(clip.id),
    partial(cb, clip.id, self.Show_Waveform_Triggered))

I have to test that, but it should work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm, wait, that may not work — now I'm thinking that it loses the self argument to Show_Waveform_Triggered.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, hey, it does work! Python is smart enough to know that self.function_name, when passed as a function object, effectively represents a call to function_name(self, ...) — the value of self doesn't need to be explicitly carried alongside it:

Python 3.8.3 (default, May 29 2020, 00:00:00) 
[GCC 10.1.1 20200507 (Red Hat 10.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import functools
>>> 
>>> class Thinger:
...     def thing(self, count=0):
...         print(["thing! " for x in range(0,count)])
...     def indirect(self, do_thingy=False):
...         def cb(fn, do_it, val):
...             if (do_it):
...                 fn(val)
...         return functools.partial(cb, self.thing, do_thingy)
... 
>>> a = Thinger()
>>> doit = a.indirect(True)
>>> doit(2)
['thing! ', 'thing! ']
>>> doit(5)
['thing! ', 'thing! ', 'thing! ', 'thing! ', 'thing! ']

@ferdnyc
Copy link
Contributor

ferdnyc commented Jul 16, 2020

On a side note, we might consider switching our build process for Linux to use https://github.com/probonopd/linuxdeployqt, in the future. It looks pretty nice, and will simply our build-server.py script quite a bit, and will probably yield a much more intelligent AppImage, with respect for Qt required files, and required dependencies.

I think we should definitely move away from the old, long-since-disavowed AppImageKit, yeah. TBH the freeze.py script is also a problem, as it's doing too much (and doing stuff that should be done by the deployment tool, instead) — and that's why the newer versions break. cx_Freeze should just be packaging the app, not worrying about library dependencies; that's the deployment tool's job.

With cx_Freeze 5.1.1, though, this does build with Bionic ­— not with Xenial, since there's no QtWebEngine. So I added the Travis Xenial build to allow_failures in this branch, for the time being.

@ferdnyc
Copy link
Contributor

ferdnyc commented Jul 16, 2020

Oh! Actually, this built with cx_Freeze 6.1 — it was the Xenial build that failed, but not because of cx_Freeze. I didn't even think to check the build logs. So, going back up to 6.1 should be fine, now that Xenial is allowed_to_fail.

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 7, 2020

@ferdnyc Regarding Windows builds containing QtWebEngine... this one might be tricky. Apparently, QtWebEngine is not supported in MSYS2 because it requires MSVC build tools. This is going to destroy our Windows build server configuration if we can no longer use MSYS2. The pre-built Qt/PyQt5 builds installed with pacman only support WebKit. Hmmm...

Downloadable Windows Qt (The Qt installer version) can include WebEngine, right? If so, switching to the binary PyQt wheels may work, instead of using the MSYS2 packages.

As long as libopenshot is compiled against the same Qt version, it should be compatible with a different packaged binary Qt.

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 7, 2020

Actually, what am I saying? You don't even need to use the Qt installer.

If I get my libopenshot packaging sorted out, that could really make this simple, but even without that it should hopefully just be a matter of:

  1. Build libopenshot on MSYS2 using their Qt packages
  2. Install Python on Windows
  3. Install a PyQt5 wheel with the same Qt version in Windows Python, using c:\whatever\pip install PyQt5 PyQtWebEngine
  4. Install cx_Freeze with c:\whatever\pip install cx_Freeze==6.1
  5. Run freeze.py in Windows Python, pointing it at libopenshot.dll and libopenshot-audio.dll built from MSYS
  6. Cross fingers?

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 7, 2020

I have a Windows 10 VM now, and 20GB of system RAM so I can actually run it without having to shut down everything else on my system. I'll play around with it a bit.

(Edit: Of course, now I'm reminded that I haven't even instaled MSYS2 on this VM yet, never mind any of the other parts. This may take a bit longer than I thought.)

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 7, 2020

Hmmmmm. In fact, now I'm seeing this mingw-w64-x86_64-qt5-static package for MSYS, and I'm wondering if that's an option for libopenshot.dll? We only need a fairly small part of Qt, for the library itself...

@jonoomph
Copy link
Member Author

jonoomph commented Aug 7, 2020

@ferdnyc I don't believe that package contains QtWebEngine (and related files). It looks like they removed all the important web engine bits.

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 7, 2020

@ferdnyc I don't believe that package contains QtWebEngine (and related files)

Doesn't need to! libopenshot has no need for QtWebEngine. Only openshot-qt needs it. And the PyQtWebEngine binary wheel can supply that.

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 7, 2020

CRIPES, pacman is slow.

@jonoomph
Copy link
Member Author

jonoomph commented Aug 9, 2020

@ferdnyc FYI: I sent you an email with the Windows builder info. 👍

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 10, 2020

@jonoomph Yeah, saw that go by, haven't looked yet. Sorry. Figured while I'm experimenting, at least, it made more sense to work locally where I could do insane things like install all 10gb of Visual Studio just to poke around in its bundled Python.

So far, it's... not going as well as I'd like. I haven't given up yet, but I also haven't succeeded yet and I'm not even certain I can. My plan may yet prove unworkable.

The sticking point is Python, which is very picky about how its extensions are compiled. Windows 10, MSYS, and MinGW are three completely different platforms to Python, and compiled extensions built for one of them can't run in any of the others.

(TBC, posting this much from my phone so I can continue on my desktop...)

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 10, 2020

Right, so...

The sticking point is Python, which is very picky about how its extensions are compiled. Windows 10, MSYS, and MinGW are three completely different platforms to Python, and compiled extensions built for one of them can't run in any of the others.

Binary wheels of PyQt5 exist only for Windows-native Python. Not for MinGW Python, not for MSYS Python. And if pip tries to build them from source on either of those platforms, the builds will fail. And it wouldn't matter anyway, since as I'm sure you saw from your own research into it, building QtWebEngine in an MSYS or MinGW environment is impossible. It doesn't exist. Even Qt themselves haven't managed to do it — in fact, even when you install Qt using the official installer, you can choose bundled support from among like 3 MSVC environments and two different MinGW versions, but if you install QtWebEngine as well, it will only be installed into the MSVC environments. It'll just silently be absent in MinGW.

So, that's a non-starter, and in terms of pre-built PyQtWebEngine, Windows Python is the only game in town. And that's... not great, because it means we need a libopenshot built for Windows Python, at least in part.

I think we could get away with the DLLs, probably (though I'm not 100% sure on that either). But to use libopenshot with Windows Python at least the SWIG bindings would have to target that Python directly. So, right now I'm... exploring a few different possible approaches to that.

  1. Using MSYS SWIG, but configuring CMake to load the Python library from C:\Program Files\Python38\ (or I guess /c/Program Files/Python38/) instead of /usr/lib/python3.8/, and trying to build against that Python with the MSYS or MinGW compiler stack. I'm almost certain this won't work.
  2. Pointing MSYS CMake at /c/Program Files/swigwin-4.0.2/ as the UseSWIG backend, and letting SWIG generate bindings for C:\Program Files\Python38\, then compiling them with MSYS/MinGW g++ but linking against the Wnidows libpython38.dll. I think it'd be great if that worked, and I'd like to imagine it will, but I'm betting it probably won't.
  3. Using either MSYS SWIG or winswig (whichever works), but then compiling the resulting C++ source code with the MSVC compiler stack. That probably would work to create a Windows Python compatible _openshot.pyd module, but it might mean having to use Qt for Windows, a Windows FFmpeg install, a Windows ZeroMQ build, etc, etc. Not sure that's worth it, because if we're going to bother with all that, might as well just go straight to...
  4. Punting on MSYS entirely, and migrating the entire build into a Visual Studio project, for our Windows builds.

That last one is probably a certain path to success in terms of the goal, but I'm not sure how difficult the effort would prove, or how successful we're likely to be. But, advantage: We'd get to use the binary distributions of both Qt and PyQt5, without having to build anything ourselves.

Still, I'm not sure I really want to go down either of those last two roads, should the earlier options all fail.

(Part 3 to follow...)

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 10, 2020

There is, however, one light in the tunnel that's not an oncoming train. As I was reading up on all this 'round the web, time and time again I kept running into the same frustrated comments from people in the same boat we're in: Because of the lack of workable free-software-stack QtWebEngine support on Windows, they still can't get rid of their old QtWebKit implementation, and they're forced to maintain dual support for both backends simultaneously.

Well, I'm here to tell you that it's impossible to read that more than like 10, maybe 15 times without eventually noticing that it ALSO applies to you as well.

None of the QtWebEngine changes in the OpenShot timeline_webview implementation would make it in any way incompatible with QtWebKit. Even the async calls — just because it doesn't have to use them, doesn't mean that a QtWebKit backend can't service the same API, making synchronous calls for the data requested and then triggering the callbacks itself when they return. Totally workable.

So, the worst-case scenario for us, really, is that we end up having to support both backends, and really as worst-cases go that's not so bad, as it's pretty doable.

So, regardless what happens with QtWebEngine on Windows, just because it's a good idea ANYWAY I think it makes a lot of sense to take a page from the PIMPL rulebook, and uncouple timeline_webview.py from any specific backend implementation. Instead of being a subclass of QWebView (or now QWebEngineView), we turn it into a class that holds a pointer(reference, obviously, since it's Python) to one or the other, and implements a generic run_js API to interface with either.

That'd also allow us to make other improvements. Like, we could build our run_js() as a Python varargs method, and construct the JS call in the library instead of having to prepend JS_SCOPE_SELECTOR to every call by hand. Insead of:

cmd = JS_SCOPE_SELECTOR + ".setScale(" + str(newScale) + "," + str(cursor_x) + ");"
self.eval_js(cmd)

we'd just do:

run_js('setScale', str(newScale), str(cursor_x))

And trust it to assemble the JS call appropriately. It'd know to quote all the strings and json.dumps() all the lists and dicts. If there's a callback needed, Python's named args come to the rescue and it's just,

run_js('someCall', 'anArg', { "another": "arg", "itsa": "dict" }, callback=self.whatever)

and that gets turned into:

self.page().runJavaScript(
    '$(\'body\').scope().someCall("anArg", { "another": "arg", "itsa": "dict" });',
     callback)

automatically.

Why wouldn't we want that? Regardless! It's like with climate change, the worst-case scenario for responding to it is, "What if we clean up the environment and make the world a better place to live for no reason?" Yeah, oh no. That'd be just awful. 😑

So, I'm excited enough about this idea, and I think it makes enough sense to do even if we DON'T need to for QtWebEngine, that I'll probably start working on that as soon as I get frustrated enough with the building-libopenshot-for-Windows-Python thing.

@jonoomph
Copy link
Member Author

jonoomph commented Aug 10, 2020

@ferdnyc YES, I'm a big fan of what you are describing. 🤯 It seems like it would be a fairly minor change, to abstract the classes a bit, and make it seamlessly fallback to Webkit if WebEngine fails to import. This would buy us some time, and maybe MSYS2 will eventually support QtWebEngine QtWebEngine will eventually support MinGW. Or... it buys us more time to play with Visual Studio, and make a proper MSVC build of OpenShot and it's dependencies. Sign me up!

@jonoomph
Copy link
Member Author

@ferdnyc Are you going to give this approach a try? Or should I begin work on it? Just making sure we don't step on each other. Thx!

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 10, 2020

@jonoomph I've started poking at it, I already have the run_js() method constructing JavaScript strings to pass to the backend. Teasing out where I want the separation between abstract API and implementation class to be, so we don't end up having things like dropEvent() and etc. cut-and-paste duplicated between the two web view implementations.

But I'll push early and often, once I've got things at least semi-working, so you can check it out and hopefully spot whatever I end up missing.

@jonoomph
Copy link
Member Author

Conditional inheritance? ehh?

class newClass(A if os.name == 'nt' else B):
    ...

@jonoomph
Copy link
Member Author

Or perhaps have 2 possible Mixin classes?

class WebEngineMixin(A):
    # class body

class WebKitMixin(B):
    # class body

WebMixIn = WebEngineMixin if some condition else WebKitMixin

@ferdnyc
Copy link
Contributor

ferdnyc commented Aug 10, 2020

Or perhaps have 2 possible Mixin classes?

class WebEngineMixin(A):
    # class body

class WebKitMixin(B):
    # class body

WebMixIn = WebEngineMixin if some condition else WebKitMixin

I actually don't think there will need to be any big split on the JS side, it seems like the JS code could be all but identical to support either/both. (Save for a very thin wrapper around the whole thing, conditionally loading qwebchannel.js iff QtWebEngine is in use and dealing with the plumbing to pass data back and forth.)

The real divide is going to be in the Qt classes. I've spent the past 25 minutes trying to talk myself out of admitting the truth: that we should really have both a timeline_model and a timeline_webview, where most of the shared stuff would move to the model. (Like all of the selection-management, object-lifecycle, etc. stuff, as well as the (modular-backend) Qt-JS communication layer.) And then whichever WebFooView widget is in use would become the much more svelte view class.

Like I said, I've been trying to come up with reasons not to do that, but it's hard to do because they're all wrong and it's the right approach. So... I'm sure I'll see reason eventually.

@jonoomph
Copy link
Member Author

No reason we can't extract out the Inheritance and unique initialization from our current Timeline class, and then come back later with a more complex refactor (model / view). In other words, we can get it fixed now and building, which unblocks all the webengine PRs and keeps our daily builds working for now. Then we can go all out and work on the model / view implementation. How does that sound?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants