Skip to content

Commit 6ff5f4b

Browse files
committed
Deep refactor of the QShortcut code used for arrow key detection, with a custom eventFilter to stop it from messing with our primary UI tabs (effects, transitions, files, emojis)
1 parent 65ce747 commit 6ff5f4b

File tree

2 files changed

+88
-50
lines changed

2 files changed

+88
-50
lines changed

src/windows/main_window.py

+87-2
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@
3939
import openshot # Python module for libopenshot (required video editing module installed separately)
4040
from PyQt5.QtCore import (
4141
Qt, pyqtSignal, pyqtSlot, QCoreApplication, PYQT_VERSION_STR,
42-
QTimer, QDateTime, QFileInfo, QUrl,
42+
QTimer, QDateTime, QFileInfo, QUrl, QEvent
4343
)
4444
from PyQt5.QtGui import QIcon, QCursor, QKeySequence, QTextCursor
4545
from PyQt5.QtWidgets import (
4646
QMainWindow, QWidget, QDockWidget,
4747
QMessageBox, QDialog, QFileDialog, QInputDialog,
4848
QAction, QActionGroup, QSizePolicy,
4949
QStatusBar, QToolBar, QToolButton,
50-
QLineEdit, QComboBox, QTextEdit
50+
QLineEdit, QComboBox, QTextEdit, QShortcut
5151
)
5252

5353
from classes import exceptions, info, qt_types, sentry, ui_util, updates
@@ -98,6 +98,9 @@ class MainWindow(updates.UpdateWatcher, QMainWindow):
9898
StopSignal = pyqtSignal()
9999
SeekSignal = pyqtSignal(int)
100100
SpeedSignal = pyqtSignal(float)
101+
SeekPreviousFrame = pyqtSignal()
102+
SeekNextFrame = pyqtSignal()
103+
PlayPauseToggleSignal = pyqtSignal()
101104
RecoverBackup = pyqtSignal()
102105
FoundVersionSignal = pyqtSignal(str)
103106
TransformSignal = pyqtSignal(str)
@@ -1554,6 +1557,43 @@ def actionCenterOnPlayhead_trigger(self, checked=True):
15541557
""" Center the timeline on the current playhead position """
15551558
self.timeline.centerOnPlayhead()
15561559

1560+
def handleSeekPreviousFrame(self):
1561+
"""Handle previous-frame keypress"""
1562+
player = get_app().window.preview_thread.player
1563+
frame_num = player.Position() - 1
1564+
1565+
# Seek to previous frame
1566+
get_app().window.PauseSignal.emit()
1567+
get_app().window.SpeedSignal.emit(0)
1568+
get_app().window.previewFrameSignal.emit(frame_num)
1569+
1570+
# Notify properties dialog
1571+
get_app().window.propertyTableView.select_frame(frame_num)
1572+
1573+
def handleSeekNextFrame(self):
1574+
"""Handle next-frame keypress"""
1575+
player = get_app().window.preview_thread.player
1576+
frame_num = player.Position() + 1
1577+
1578+
# Seek to next frame
1579+
get_app().window.PauseSignal.emit()
1580+
get_app().window.SpeedSignal.emit(0)
1581+
get_app().window.previewFrameSignal.emit(frame_num)
1582+
1583+
# Notify properties dialog
1584+
get_app().window.propertyTableView.select_frame(frame_num)
1585+
1586+
def handlePlayPauseToggleSignal(self):
1587+
"""Handle play-pause-toggle keypress"""
1588+
player = get_app().window.preview_thread.player
1589+
frame_num = player.Position()
1590+
1591+
# Toggle Play/Pause
1592+
get_app().window.actionPlay.trigger()
1593+
1594+
# Notify properties dialog
1595+
get_app().window.propertyTableView.select_frame(frame_num)
1596+
15571597
def getShortcutByName(self, setting_name):
15581598
""" Get a key sequence back from the setting name """
15591599
s = get_app().get_settings()
@@ -3090,12 +3130,44 @@ def initModels(self):
30903130
self.emojiListView = EmojisListView(self.emojis_model)
30913131
self.tabEmojis.layout().addWidget(self.emojiListView)
30923132

3133+
def seekPreviousFrame(self):
3134+
"""Handle previous-frame keypress"""
3135+
# Ignore certain focused widgets
3136+
get_app().window.SeekPreviousFrame.emit()
3137+
3138+
def seekNextFrame(self):
3139+
"""Handle next-frame keypress"""
3140+
get_app().window.SeekNextFrame.emit()
3141+
3142+
def playToggle(self):
3143+
"""Handle play-pause-toggle keypress"""
3144+
get_app().window.PlayPauseToggleSignal.emit()
3145+
3146+
def eventFilter(self, obj, event):
3147+
"""Filter out certain QShortcuts - for example, arrow keys used
3148+
in our files, transitions, effects, and emojis views."""
3149+
if event.type() == QEvent.ShortcutOverride:
3150+
if self.emojiListView.hasFocus() or self.filesView.hasFocus() or \
3151+
self.transitionsView.hasFocus() or self.effectsView.hasFocus():
3152+
# Mark event as 'handled' so it stops propagating
3153+
event.accept()
3154+
3155+
elif self.propertyTableView.hasFocus() and \
3156+
(event.key() == get_app().window.getShortcutByName("playToggle") or
3157+
event.key() == get_app().window.getShortcutByName("playToggle1") or
3158+
event.key() == get_app().window.getShortcutByName("playToggle2") or
3159+
event.key() == get_app().window.getShortcutByName("playToggle3")):
3160+
# Mark event as 'handled' so it stops propagating
3161+
event.accept()
3162+
return super(MainWindow, self).eventFilter(obj, event)
3163+
30933164
def __init__(self, *args):
30943165

30953166
# Create main window base class
30963167
super().__init__(*args)
30973168
self.initialized = False
30983169
self.shutting_down = False
3170+
self.installEventFilter(self)
30993171

31003172
# set window on app for reference during initialization of children
31013173
app = get_app()
@@ -3177,6 +3249,9 @@ def __init__(self, *args):
31773249

31783250
# Connect signals
31793251
self.RecoverBackup.connect(self.recover_backup)
3252+
self.SeekPreviousFrame.connect(self.handleSeekPreviousFrame)
3253+
self.SeekNextFrame.connect(self.handleSeekNextFrame)
3254+
self.PlayPauseToggleSignal.connect(self.handlePlayPauseToggleSignal)
31803255

31813256
# Create the timeline sync object (used for previewing timeline)
31823257
self.timeline_sync = TimelineSync(self)
@@ -3367,3 +3442,13 @@ def __init__(self, *args):
33673442

33683443
# Main window is initialized
33693444
self.initialized = True
3445+
3446+
# Use shortcuts to override keypress capturing for arrow keys
3447+
# These keys are a bit special, and other approaches fail on certain
3448+
# combinations of OS and Webview backend
3449+
QShortcut(app.window.getShortcutByName("seekPreviousFrame"), self, activated=self.seekPreviousFrame, context=Qt.WindowShortcut)
3450+
QShortcut(app.window.getShortcutByName("seekNextFrame"), self, activated=self.seekNextFrame, context=Qt.WindowShortcut)
3451+
QShortcut(app.window.getShortcutByName("playToggle"), self, activated=self.playToggle, context=Qt.WindowShortcut)
3452+
QShortcut(app.window.getShortcutByName("playToggle1"), self, activated=self.playToggle, context=Qt.WindowShortcut)
3453+
QShortcut(app.window.getShortcutByName("playToggle2"), self, activated=self.playToggle, context=Qt.WindowShortcut)
3454+
QShortcut(app.window.getShortcutByName("playToggle3"), self, activated=self.playToggle, context=Qt.WindowShortcut)

src/windows/views/webview.py

+1-48
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
from PyQt5.QtCore import QFileInfo, pyqtSlot, QUrl, Qt, QCoreApplication, QTimer, pyqtSignal
4141
from PyQt5.QtGui import QCursor, QKeySequence, QColor
42-
from PyQt5.QtWidgets import QMenu, QDialog, QShortcut
42+
from PyQt5.QtWidgets import QMenu, QDialog
4343

4444
from classes import info, updates
4545
from classes.app import get_app
@@ -3309,43 +3309,6 @@ def render_cache_json(self):
33093309
# Log the exception and ignore
33103310
log.warning("Exception processing timeline cache: %s", ex)
33113311

3312-
def seekPreviousFrame(self):
3313-
"""Handle previous-frame keypress"""
3314-
player = get_app().window.preview_thread.player
3315-
frame_num = player.Position() - 1
3316-
3317-
# Seek to prevoius frame
3318-
get_app().window.PauseSignal.emit()
3319-
get_app().window.SpeedSignal.emit(0)
3320-
get_app().window.previewFrameSignal.emit(frame_num)
3321-
3322-
# Notify properties dialog
3323-
get_app().window.propertyTableView.select_frame(frame_num)
3324-
3325-
def seekNextFrame(self):
3326-
"""Handle next-frame keypress"""
3327-
player = get_app().window.preview_thread.player
3328-
frame_num = player.Position() + 1
3329-
3330-
# Seek to next frame
3331-
get_app().window.PauseSignal.emit()
3332-
get_app().window.SpeedSignal.emit(0)
3333-
get_app().window.previewFrameSignal.emit(frame_num)
3334-
3335-
# Notify properties dialog
3336-
get_app().window.propertyTableView.select_frame(frame_num)
3337-
3338-
def playToggle(self):
3339-
"""Handle play-pause-toggle keypress"""
3340-
player = get_app().window.preview_thread.player
3341-
frame_num = player.Position()
3342-
3343-
# Toggle Play/Pause
3344-
get_app().window.actionPlay.trigger()
3345-
3346-
# Notify properties dialog
3347-
get_app().window.propertyTableView.select_frame(frame_num)
3348-
33493312
def __init__(self, window):
33503313
super().__init__()
33513314
self.setObjectName("TimelineWebView")
@@ -3403,13 +3366,3 @@ def __init__(self, window):
34033366
# connect signal to receive waveform data
34043367
self.clipAudioDataReady.connect(self.clipAudioDataReady_Triggered)
34053368
self.fileAudioDataReady.connect(self.fileAudioDataReady_Triggered)
3406-
3407-
# Use shortcuts to override keypress capturing for arrow keys
3408-
# This is needed mostly due to WebEngine backend eating keypress events
3409-
# This approach works well for ALL backends though
3410-
QShortcut(app.window.getShortcutByName("seekPreviousFrame"), self, activated=self.seekPreviousFrame)
3411-
QShortcut(app.window.getShortcutByName("seekNextFrame"), self, activated=self.seekNextFrame)
3412-
QShortcut(app.window.getShortcutByName("playToggle"), self, activated=self.playToggle)
3413-
QShortcut(app.window.getShortcutByName("playToggle1"), self, activated=self.playToggle)
3414-
QShortcut(app.window.getShortcutByName("playToggle2"), self, activated=self.playToggle)
3415-
QShortcut(app.window.getShortcutByName("playToggle3"), self, activated=self.playToggle)

0 commit comments

Comments
 (0)