Skip to content

Commit d07488d

Browse files
committed
Creating a mixin class to support both WebKit and WebEngine
1 parent 5a146e2 commit d07488d

File tree

2 files changed

+172
-50
lines changed

2 files changed

+172
-50
lines changed

src/windows/views/timeline_mixins.py

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""
2+
@file
3+
@brief This file allows for switching between QtWebEngine and QtWebKit
4+
@author Jonathan Thomas <[email protected]>
5+
6+
@section LICENSE
7+
8+
Copyright (c) 2008-2018 OpenShot Studios, LLC
9+
(http://www.openshotstudios.com). This file is part of
10+
OpenShot Video Editor (http://www.openshot.org), an open-source project
11+
dedicated to delivering high quality video editing and animation solutions
12+
to the world.
13+
14+
OpenShot Video Editor is free software: you can redistribute it and/or modify
15+
it under the terms of the GNU General Public License as published by
16+
the Free Software Foundation, either version 3 of the License, or
17+
(at your option) any later version.
18+
19+
OpenShot Video Editor is distributed in the hope that it will be useful,
20+
but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
GNU General Public License for more details.
23+
24+
You should have received a copy of the GNU General Public License
25+
along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
26+
"""
27+
28+
import os
29+
from classes import info
30+
from PyQt5.QtCore import QFileInfo, pyqtSlot, QUrl, Qt, QCoreApplication, QTimer
31+
from PyQt5.QtGui import QCursor, QKeySequence, QColor
32+
from PyQt5.QtWidgets import QMenu
33+
from classes.logger import log
34+
from functools import partial
35+
36+
37+
try:
38+
# Attempt to import QtWebEngine
39+
from PyQt5.QtWebEngineWidgets import QWebEngineView
40+
from PyQt5.QtWebChannel import QWebChannel
41+
IS_WEBENGINE_VALID = True
42+
except ImportError:
43+
IS_WEBENGINE_VALID = False
44+
45+
try:
46+
# Attempt to import QtWebKit
47+
from PyQt5.QtWebKitWidgets import QWebView
48+
IS_WEBKIT_VALID = True
49+
except ImportError:
50+
IS_WEBKIT_VALID = False
51+
52+
53+
class TimelineBaseMixin(object):
54+
"""OpenShot Timeline Base Mixin Class"""
55+
def __init__(self):
56+
"""Initialization code required for parent widget"""
57+
self.document_is_ready = False
58+
self.html_path = html_path = os.path.join(info.PATH, 'timeline', 'index.html')
59+
60+
def run_js(self, code, callback=None, retries=0):
61+
"""Run javascript code snippet"""
62+
raise Exception("run_js not implemented")
63+
64+
65+
class TimelineQtWebEngineMixin(TimelineBaseMixin, QWebEngineView):
66+
"""QtWebEngine Timeline Widget"""
67+
68+
def __init__(self):
69+
"""Initialization code required for widget"""
70+
TimelineBaseMixin.__init__(self)
71+
QWebEngineView.__init__(self)
72+
73+
# Set background color of timeline
74+
self.page().setBackgroundColor(QColor("#363636"))
75+
76+
# Delete the webview when closed
77+
self.setAttribute(Qt.WA_DeleteOnClose)
78+
79+
# Enable smooth scrolling on timeline
80+
self.settings().setAttribute(self.settings().ScrollAnimatorEnabled, True)
81+
82+
# Set url from configuration (QUrl takes absolute paths for file system paths, create from QFileInfo)
83+
self.webchannel = QWebChannel(self.page())
84+
self.setUrl(QUrl.fromLocalFile(QFileInfo(self.html_path).absoluteFilePath()))
85+
self.page().setWebChannel(self.webchannel)
86+
87+
# Connect signal of javascript initialization to our javascript reference init function
88+
self.page().loadStarted.connect(self.setup_js_data)
89+
90+
def run_js(self, code, callback=None, retries=0):
91+
'''Run JS code async and optionally have a callback for response'''
92+
# Check if document.Ready has fired in JS
93+
if not self.document_is_ready:
94+
# Not ready, try again in a few moments
95+
if retries == 0:
96+
# Log the script contents, the first time
97+
log.debug("run_js() called before document ready event. Script queued: %s" % code)
98+
elif retries % 5 == 0:
99+
log.warning("WebEngine backend still not ready after {} retries.".format(retries))
100+
else:
101+
log.debug("Script queued, {} retries so far".format(retries))
102+
QTimer.singleShot(200, partial(self.run_js, code, callback, retries + 1))
103+
return None
104+
else:
105+
# Execute JS code
106+
if callback:
107+
return self.page().runJavaScript(code, callback)
108+
else:
109+
return self.page().runJavaScript(code)
110+
111+
def setup_js_data(self):
112+
# Export self as a javascript object in webview
113+
self.webchannel.registerObject('timeline', self)
114+
115+
116+
class TimelineQtWebKitMixin(TimelineBaseMixin, QWebView):
117+
"""QtWebKit Timeline Widget"""
118+
119+
def __init__(self):
120+
"""Initialization code required for widget"""
121+
TimelineBaseMixin.__init__(self)
122+
QWebView.__init__(self)
123+
124+
# Delete the webview when closed
125+
self.setAttribute(Qt.WA_DeleteOnClose)
126+
127+
# Disable image caching on timeline
128+
self.settings().setObjectCacheCapacities(0, 0, 0)
129+
130+
# Set url from configuration (QUrl takes absolute paths for file system paths, create from QFileInfo)
131+
self.setUrl(QUrl.fromLocalFile(QFileInfo(self.html_path).absoluteFilePath()))
132+
133+
# Connect signal of javascript initialization to our javascript reference init function
134+
self.page().mainFrame().javaScriptWindowObjectCleared.connect(self.setup_js_data)
135+
136+
def run_js(self, code, callback=None, retries=0):
137+
'''Run JS code async and optionally have a callback for response'''
138+
# Check if document.Ready has fired in JS
139+
if not self.document_is_ready:
140+
# Not ready, try again in a few moments
141+
if retries == 0:
142+
# Log the script contents, the first time
143+
log.debug("run_js() called before document ready event. Script queued: %s" % code)
144+
elif retries % 5 == 0:
145+
log.warning("WebKit backend still not ready after {} retries.".format(retries))
146+
else:
147+
log.debug("Script queued, {} retries so far".format(retries))
148+
QTimer.singleShot(200, partial(self.run_js, code, callback, retries + 1))
149+
return None
150+
else:
151+
# Execute JS code
152+
if callback:
153+
return self.page().mainFrame().evaluateJavaScript(code, callback)
154+
else:
155+
return self.page().mainFrame().evaluateJavaScript(code)
156+
157+
def setup_js_data(self):
158+
# Export self as a javascript object in webview
159+
self.page().mainFrame().addToJavaScriptWindowObject('timeline', self)
160+
self.page().mainFrame().addToJavaScriptWindowObject('mainWindow', self.window)
161+
162+
163+
# Set correct Mixin (with QtWebEngine preference)
164+
if IS_WEBENGINE_VALID:
165+
TimelineMixin = TimelineQtWebEngineMixin
166+
elif IS_WEBKIT_VALID:
167+
TimelineMixin = TimelineQtWebKitMixin
168+
else:
169+
TimelineMixin = None

src/windows/views/timeline_webview.py

+3-50
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838
import openshot # Python module for libopenshot (required video editing module installed separately)
3939
from PyQt5.QtCore import QFileInfo, pyqtSlot, QUrl, Qt, QCoreApplication, QTimer
4040
from PyQt5.QtGui import QCursor, QKeySequence, QColor
41-
from PyQt5.QtWebEngineWidgets import QWebEngineView
42-
from PyQt5.QtWebChannel import QWebChannel
4341
from PyQt5.QtWidgets import QMenu
4442

4543
from classes import info, updates
@@ -49,6 +47,7 @@
4947
from classes.query import File, Clip, Transition, Track
5048
from classes.waveform import get_audio_data
5149
from classes.conversion import zoomToSeconds, secondsToZoom
50+
from .timeline_mixins import TimelineMixin
5251

5352
import json
5453

@@ -152,7 +151,7 @@
152151
MENU_SPLIT_AUDIO_MULTIPLE = 1
153152

154153

155-
class TimelineWebView(QWebEngineView, updates.UpdateInterface):
154+
class TimelineWebView(TimelineMixin, updates.UpdateInterface):
156155
""" A Web(Engine)View QWidget used to load the Timeline """
157156

158157
# Path to html file
@@ -175,27 +174,6 @@ def get_thumb_address(self):
175174
thumb_address = "http://%s:%s/thumbnails/" % (thumb_server_details[0], thumb_server_details[1])
176175
return thumb_address
177176

178-
def run_js(self, code, callback=None, retries=0):
179-
'''Run JS code async and optionally have a callback for response'''
180-
# Check if document.Ready has fired in JS
181-
if not self.document_is_ready:
182-
# Not ready, try again in a few moments
183-
if retries == 0:
184-
# Log the script contents, the first time
185-
log.debug("run_js() called before document ready event. Script queued: %s" % code)
186-
elif retries % 5 == 0:
187-
log.warning("WebEngine backend still not ready after {} retries.".format(retries))
188-
else:
189-
log.debug("Script queued, {} retries so far".format(retries))
190-
QTimer.singleShot(200, partial(self.run_js, code, callback, retries + 1))
191-
return None
192-
else:
193-
# Execute JS code
194-
if callback:
195-
return self.page().runJavaScript(code, callback)
196-
else:
197-
return self.page().runJavaScript(code)
198-
199177
# This method is invoked by the UpdateManager each time a change happens (i.e UpdateInterface)
200178
def changed(self, action):
201179
# Remove unused action attribute (old_values)
@@ -2793,13 +2771,6 @@ def wheelEvent(self, event):
27932771
# Otherwise pass on to implement default functionality (scroll in QWebEngineView)
27942772
super(type(self), self).wheelEvent(event)
27952773

2796-
def setup_js_data(self):
2797-
# Export self as a javascript object in webview
2798-
self.webchannel.registerObject('timeline', self)
2799-
2800-
# Initialize snapping mode
2801-
self.SetSnappingMode(self.window.actionSnappingTool.isChecked())
2802-
28032774
# An item is being dragged onto the timeline (mouse is entering the timeline now)
28042775
def dragEnterEvent(self, event):
28052776

@@ -3116,20 +3087,10 @@ def render_cache_json(self):
31163087
pass
31173088

31183089
def __init__(self, window):
3119-
QWebEngineView.__init__(self)
3090+
TimelineMixin.__init__(self)
31203091
self.window = window
31213092
self.setAcceptDrops(True)
31223093
self.last_position_frames = None
3123-
self.document_is_ready = False
3124-
3125-
# Set background color of timeline
3126-
self.page().setBackgroundColor(QColor("#363636"))
3127-
3128-
# Delete the webview when closed
3129-
self.setAttribute(Qt.WA_DeleteOnClose)
3130-
3131-
# Enable smooth scrolling on timeline
3132-
self.settings().setAttribute(self.settings().ScrollAnimatorEnabled, True)
31333094

31343095
# Get settings & logger
31353096
self.settings_obj = settings.get_settings()
@@ -3138,14 +3099,6 @@ def __init__(self, window):
31383099
# Add self as listener to project data updates (used to update the timeline)
31393100
get_app().updates.add_listener(self)
31403101

3141-
self.webchannel = QWebChannel(self.page())
3142-
# set url from configuration (QUrl takes absolute paths for file system paths, create from QFileInfo)
3143-
self.setUrl(QUrl.fromLocalFile(QFileInfo(self.html_path).absoluteFilePath()))
3144-
self.page().setWebChannel(self.webchannel)
3145-
3146-
# Connect signal of javascript initialization to our javascript reference init function
3147-
self.page().loadStarted.connect(self.setup_js_data)
3148-
31493102
# Connect zoom functionality
31503103
window.sliderZoom.valueChanged.connect(self.update_zoom)
31513104

0 commit comments

Comments
 (0)