From 669756b391d26661cf2e2a97a304e73343ef6655 Mon Sep 17 00:00:00 2001 From: tassaron Date: Sun, 11 Jan 2026 14:29:58 -0500 Subject: update to Qt 6 and Pillow 12 and yeah, I accidentally ran black on the codebase. I don't want to spend more free time fixing that. All of these changes are simple renames or removals, nothing too major. --- src/gui/mainwindow.py | 648 ++++++++++++++++++++++++-------------------------- 1 file changed, 313 insertions(+), 335 deletions(-) (limited to 'src/gui/mainwindow.py') diff --git a/src/gui/mainwindow.py b/src/gui/mainwindow.py index 159dc02..b0a564b 100644 --- a/src/gui/mainwindow.py +++ b/src/gui/mainwindow.py @@ -1,11 +1,13 @@ -''' - When using GUI mode, this module's object (the main window) takes - user input to construct a program state (stored in the Core object). - This shows a preview of the video being created and allows for saving - projects and exporting the video at a later time. -''' -from PyQt5 import QtCore, QtWidgets, uic -import PyQt5.QtWidgets as QtWidgets +""" +When using GUI mode, this module's object (the main window) takes +user input to construct a program state (stored in the Core object). +This shows a preview of the video being created and allows for saving +projects and exporting the video at a later time. +""" + +from PyQt6 import QtCore, QtWidgets, uic +import PyQt6.QtWidgets as QtWidgets +from PyQt6.QtGui import QUndoStack, QShortcut from PIL import Image from queue import Queue import sys @@ -21,46 +23,59 @@ from .preview_win import PreviewWindow from .presetmanager import PresetManager from .actions import * from ..toolkit import ( - disableWhenEncoding, disableWhenOpeningProject, checkOutput, blockSignals + disableWhenEncoding, + disableWhenOpeningProject, + checkOutput, + blockSignals, ) -appName = 'Audio Visualizer' -log = logging.getLogger('AVP.Gui.MainWindow') +appName = "Audio Visualizer" +log = logging.getLogger("AVP.Gui.MainWindow") + + +class MyQUndoStack(QUndoStack): + # FIXME move this class + @property + def encoding(self): + return self.parent().encoding + + @disableWhenEncoding + def undo(self, *args, **kwargs): + super().undo(*args, **kwargs) + + @disableWhenEncoding + def redo(self, *args, **kwargs): + super().redo(*args, **kwargs) class MainWindow(QtWidgets.QMainWindow): - ''' - The MainWindow wraps many Core methods in order to update the GUI - accordingly. E.g., instead of self.core.openProject(), it will use - self.openProject() and update the window titlebar within the wrapper. + """ + The MainWindow wraps many Core methods in order to update the GUI + accordingly. E.g., instead of self.core.openProject(), it will use + self.openProject() and update the window titlebar within the wrapper. - MainWindow manages the autosave feature, although Core has the - primary functions for opening and creating project files. - ''' + MainWindow manages the autosave feature, although Core has the + primary functions for opening and creating project files. + """ createVideo = QtCore.pyqtSignal() newTask = QtCore.pyqtSignal(list) # for the preview window processTask = QtCore.pyqtSignal() - def __init__(self, project): + def __init__(self, project, dpi): super().__init__() - log.debug( - 'Main thread id: {}'.format(int(QtCore.QThread.currentThreadId()))) + log.debug("Main thread id: {}".format(int(QtCore.QThread.currentThreadId()))) uic.loadUi(os.path.join(Core.wd, "gui", "mainwindow.ui"), self) - desk = QtWidgets.QDesktopWidget() - dpi = desk.physicalDpiX() - log.info("Detected screen DPI: %s", dpi) - - self.resize( - int(self.width() * - (dpi / 144)), - int(self.height() * - (dpi / 144)) - ) + + if dpi: + self.resize( + int(self.width() * (dpi / 144)), + int(self.height() * (dpi / 144)), + ) self.core = Core() - Core.mode = 'GUI' + Core.mode = "GUI" # widgets of component settings self.pages = [] self.lastAutosave = time.time() @@ -72,15 +87,13 @@ class MainWindow(QtWidgets.QMainWindow): # Find settings created by Core object self.dataDir = Core.dataDir self.presetDir = Core.presetDir - self.autosavePath = os.path.join(self.dataDir, 'autosave.avp') + self.autosavePath = os.path.join(self.dataDir, "autosave.avp") self.settings = Core.settings # Create stack of undoable user actions - self.undoStack = QtWidgets.QUndoStack(self) + self.undoStack = MyQUndoStack(self) undoLimit = self.settings.value("pref_undoLimit") self.undoStack.setUndoLimit(undoLimit) - self.undoStack.undo = disableWhenEncoding(self.undoStack.undo) - self.undoStack.redo = disableWhenEncoding(self.undoStack.redo) # Create Undo Dialog - A standard QUndoView on a standard QDialog self.undoDialog = QtWidgets.QDialog(self) @@ -94,18 +107,17 @@ class MainWindow(QtWidgets.QMainWindow): self.presetManager = PresetManager(self) # Create the preview window and its thread, queues, and timers - log.debug('Creating preview window') - self.previewWindow = PreviewWindow(self, os.path.join( - Core.wd, 'gui', "background.png")) + log.debug("Creating preview window") + self.previewWindow = PreviewWindow( + self, os.path.join(Core.wd, "gui", "background.png") + ) self.verticalLayout_previewWrapper.addWidget(self.previewWindow) - log.debug('Starting preview thread') + log.debug("Starting preview thread") self.previewQueue = Queue() self.previewThread = QtCore.QThread(self) self.previewWorker = preview_thread.Worker( - self.core, - self.settings, - self.previewQueue + self.core, self.settings, self.previewQueue ) self.previewWorker.moveToThread(self.previewThread) self.newTask.connect(self.previewWorker.createPreviewImage) @@ -113,12 +125,12 @@ class MainWindow(QtWidgets.QMainWindow): self.previewWorker.error.connect(self.previewWindow.threadError) self.previewWorker.imageCreated.connect(self.showPreviewImage) self.previewThread.start() - self.previewThread.finished.connect(lambda: log.info('Preview thread finished.')) + self.previewThread.finished.connect( + lambda: log.info("Preview thread finished.") + ) timeout = 500 - log.debug( - 'Preview timer set to trigger when idle for %sms' % str(timeout) - ) + log.debug("Preview timer set to trigger when idle for %sms" % str(timeout)) self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self.processTask.emit) self.timer.start(timeout) @@ -128,7 +140,7 @@ class MainWindow(QtWidgets.QMainWindow): # Undo Feature def toggleUndoButtonEnabled(*_): - """ Enable/disable undo button depending on whether UndoStack contains Actions """ + """Enable/disable undo button depending on whether UndoStack contains Actions""" try: undoButton.setEnabled(self.undoStack.count()) except RuntimeError: @@ -138,50 +150,41 @@ class MainWindow(QtWidgets.QMainWindow): style = self.pushButton_undo.style() undoButton = self.pushButton_undo undoButton.setIcon( - style.standardIcon(QtWidgets.QStyle.SP_FileDialogBack) + style.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_FileDialogBack) ) undoButton.clicked.connect(self.undoStack.undo) undoButton.setEnabled(False) self.undoStack.cleanChanged.connect(toggleUndoButtonEnabled) self.undoMenu = QtWidgets.QMenu() - self.undoMenu.addAction( - self.undoStack.createUndoAction(self) - ) - self.undoMenu.addAction( - self.undoStack.createRedoAction(self) - ) - action = self.undoMenu.addAction('Show History...') - action.triggered.connect( - lambda _: self.showUndoStack() - ) + self.undoMenu.addAction(self.undoStack.createUndoAction(self)) + self.undoMenu.addAction(self.undoStack.createRedoAction(self)) + action = self.undoMenu.addAction("Show History...") + action.triggered.connect(lambda _: self.showUndoStack()) undoButton.setMenu(self.undoMenu) # end of Undo Feature style = self.pushButton_listMoveUp.style() self.pushButton_listMoveUp.setIcon( - style.standardIcon(QtWidgets.QStyle.SP_ArrowUp) + style.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_ArrowUp) ) style = self.pushButton_listMoveDown.style() self.pushButton_listMoveDown.setIcon( - style.standardIcon(QtWidgets.QStyle.SP_ArrowDown) + style.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_ArrowDown) ) style = self.pushButton_removeComponent.style() self.pushButton_removeComponent.setIcon( - style.standardIcon(QtWidgets.QStyle.SP_DialogDiscardButton) + style.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_DialogDiscardButton) ) - if sys.platform == 'darwin': - log.debug( - 'Darwin detected: showing progress label below progress bar') + if sys.platform == "darwin": + log.debug("Darwin detected: showing progress label below progress bar") self.progressBar_createVideo.setTextVisible(False) else: self.progressLabel.setHidden(True) - self.toolButton_selectAudioFile.clicked.connect( - self.openInputFileDialog) + self.toolButton_selectAudioFile.clicked.connect(self.openInputFileDialog) - self.toolButton_selectOutputFile.clicked.connect( - self.openOutputFileDialog) + self.toolButton_selectOutputFile.clicked.connect(self.openOutputFileDialog) def changedField(): self.autosave() @@ -192,43 +195,36 @@ class MainWindow(QtWidgets.QMainWindow): self.progressBar_createVideo.setValue(0) - self.pushButton_createVideo.clicked.connect( - self.createAudioVisualization) + self.pushButton_createVideo.clicked.connect(self.createAudioVisualization) self.pushButton_Cancel.clicked.connect(self.stopVideo) - for i, container in enumerate(Core.encoderOptions['containers']): - self.comboBox_videoContainer.addItem(container['name']) - if container['name'] == self.settings.value('outputContainer'): + for i, container in enumerate(Core.encoderOptions["containers"]): + self.comboBox_videoContainer.addItem(container["name"]) + if container["name"] == self.settings.value("outputContainer"): selectedContainer = i self.comboBox_videoContainer.setCurrentIndex(selectedContainer) - self.comboBox_videoContainer.currentIndexChanged.connect( - self.updateCodecs - ) + self.comboBox_videoContainer.currentIndexChanged.connect(self.updateCodecs) self.updateCodecs() for i in range(self.comboBox_videoCodec.count()): codec = self.comboBox_videoCodec.itemText(i) - if codec == self.settings.value('outputVideoCodec'): + if codec == self.settings.value("outputVideoCodec"): self.comboBox_videoCodec.setCurrentIndex(i) for i in range(self.comboBox_audioCodec.count()): codec = self.comboBox_audioCodec.itemText(i) - if codec == self.settings.value('outputAudioCodec'): + if codec == self.settings.value("outputAudioCodec"): self.comboBox_audioCodec.setCurrentIndex(i) - self.comboBox_videoCodec.currentIndexChanged.connect( - self.updateCodecSettings - ) + self.comboBox_videoCodec.currentIndexChanged.connect(self.updateCodecSettings) - self.comboBox_audioCodec.currentIndexChanged.connect( - self.updateCodecSettings - ) + self.comboBox_audioCodec.currentIndexChanged.connect(self.updateCodecSettings) - vBitrate = int(self.settings.value('outputVideoBitrate')) - aBitrate = int(self.settings.value('outputAudioBitrate')) + vBitrate = int(self.settings.value("outputVideoBitrate")) + aBitrate = int(self.settings.value("outputAudioBitrate")) self.spinBox_vBitrate.setValue(vBitrate) self.spinBox_aBitrate.setValue(aBitrate) @@ -239,30 +235,27 @@ class MainWindow(QtWidgets.QMainWindow): self.compMenu = QtWidgets.QMenu() for i, comp in enumerate(self.core.modules): action = self.compMenu.addAction(comp.Component.name) - action.triggered.connect( - lambda _, item=i: self.addComponent(0, item) - ) + action.triggered.connect(lambda _, item=i: self.addComponent(0, item)) self.pushButton_addComponent.setMenu(self.compMenu) componentList.dropEvent = self.dragComponent - componentList.itemSelectionChanged.connect( - self.changeComponentWidget - ) + componentList.itemSelectionChanged.connect(self.changeComponentWidget) componentList.itemSelectionChanged.connect( self.presetManager.clearPresetListSelection ) - self.pushButton_removeComponent.clicked.connect( - lambda: self.removeComponent() - ) + self.pushButton_removeComponent.clicked.connect(lambda: self.removeComponent()) - componentList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - componentList.customContextMenuRequested.connect( - self.componentContextMenu + componentList.setContextMenuPolicy( + QtCore.Qt.ContextMenuPolicy.CustomContextMenu ) + componentList.customContextMenuRequested.connect(self.componentContextMenu) - currentRes = str(self.settings.value('outputWidth'))+'x' + \ - str(self.settings.value('outputHeight')) + currentRes = ( + str(self.settings.value("outputWidth")) + + "x" + + str(self.settings.value("outputHeight")) + ) for i, res in enumerate(Core.resolutions): self.comboBox_resolution.addItem(res) if res == currentRes: @@ -272,24 +265,14 @@ class MainWindow(QtWidgets.QMainWindow): self.updateResolution ) - self.pushButton_listMoveUp.clicked.connect( - lambda: self.moveComponent(-1) - ) - self.pushButton_listMoveDown.clicked.connect( - lambda: self.moveComponent(1) - ) + self.pushButton_listMoveUp.clicked.connect(lambda: self.moveComponent(-1)) + self.pushButton_listMoveDown.clicked.connect(lambda: self.moveComponent(1)) # Configure the Projects Menu self.projectMenu = QtWidgets.QMenu() - self.menuButton_newProject = self.projectMenu.addAction( - "New Project" - ) - self.menuButton_newProject.triggered.connect( - lambda: self.createNewProject() - ) - self.menuButton_openProject = self.projectMenu.addAction( - "Open Project" - ) + self.menuButton_newProject = self.projectMenu.addAction("New Project") + self.menuButton_newProject.triggered.connect(lambda: self.createNewProject()) + self.menuButton_openProject = self.projectMenu.addAction("Open Project") self.menuButton_openProject.triggered.connect( lambda: self.openOpenProjectDialog() ) @@ -303,22 +286,18 @@ class MainWindow(QtWidgets.QMainWindow): self.pushButton_projects.setMenu(self.projectMenu) # Configure the Presets Button - self.pushButton_presets.clicked.connect( - self.openPresetManager - ) + self.pushButton_presets.clicked.connect(self.openPresetManager) self.updateWindowTitle() - log.debug('Showing main window') + log.debug("Showing main window") self.show() if project and project != self.autosavePath: - if not project.endswith('.avp'): - project += '.avp' + if not project.endswith(".avp"): + project += ".avp" # open a project from the commandline if not os.path.dirname(project): - project = os.path.join( - self.settings.value("projectDir"), project - ) + project = os.path.join(self.settings.value("projectDir"), project) self.currentProject = project self.settings.setValue("currentProject", project) if os.path.exists(self.autosavePath): @@ -335,7 +314,8 @@ class MainWindow(QtWidgets.QMainWindow): ch = self.showMessage( msg="Restore unsaved changes in project '%s'?" % os.path.basename(self.currentProject)[:-4], - showCancel=True) + showCancel=True, + ) if ch: self.saveProjectChanges() else: @@ -352,16 +332,16 @@ class MainWindow(QtWidgets.QMainWindow): msg="FFmpeg could not be found. This is a critical error. " "Install FFmpeg, or download it and place the program executable " "in the same folder as this program.", - icon='Critical' + icon="Critical", ) else: if not self.settings.value("ffmpegMsgShown"): try: with open(os.devnull, "w") as f: ffmpegVers = checkOutput( - [self.core.FFMPEG_BIN, '-version'], stderr=f + [self.core.FFMPEG_BIN, "-version"], stderr=f ) - goodVersion = str(ffmpegVers).split()[2].startswith('4') + goodVersion = str(ffmpegVers).split()[2].startswith("4") except Exception: goodVersion = False else: @@ -375,70 +355,61 @@ class MainWindow(QtWidgets.QMainWindow): self.settings.setValue("ffmpegMsgShown", True) # Hotkeys for projects - QtWidgets.QShortcut("Ctrl+S", self, self.saveCurrentProject) - QtWidgets.QShortcut("Ctrl+A", self, self.openSaveProjectDialog) - QtWidgets.QShortcut("Ctrl+O", self, self.openOpenProjectDialog) - QtWidgets.QShortcut("Ctrl+N", self, self.createNewProject) + + QShortcut("Ctrl+S", self, self.saveCurrentProject) + QShortcut("Ctrl+A", self, self.openSaveProjectDialog) + QShortcut("Ctrl+O", self, self.openOpenProjectDialog) + QShortcut("Ctrl+N", self, self.createNewProject) # Hotkeys for undo/redo - QtWidgets.QShortcut("Ctrl+Z", self, self.undoStack.undo) - QtWidgets.QShortcut("Ctrl+Y", self, self.undoStack.redo) - QtWidgets.QShortcut("Ctrl+Shift+Z", self, self.undoStack.redo) + QShortcut("Ctrl+Z", self, self.undoStack.undo) + QShortcut("Ctrl+Y", self, self.undoStack.redo) + QShortcut("Ctrl+Shift+Z", self, self.undoStack.redo) # Hotkeys for component list - for inskey in ("Ctrl+T", QtCore.Qt.Key_Insert): - QtWidgets.QShortcut( - inskey, self, - activated=lambda: self.pushButton_addComponent.click() - ) - for delkey in ("Ctrl+R", QtCore.Qt.Key_Delete): - QtWidgets.QShortcut( - delkey, self.listWidget_componentList, - self.removeComponent + for inskey in ("Ctrl+T", QtCore.Qt.Key.Key_Insert): + QShortcut( + inskey, + self, + activated=lambda: self.pushButton_addComponent.click(), ) - QtWidgets.QShortcut( - "Ctrl+Space", self, - activated=lambda: self.listWidget_componentList.setFocus() - ) - QtWidgets.QShortcut( - "Ctrl+Shift+S", self, - self.presetManager.openSavePresetDialog - ) - QtWidgets.QShortcut( - "Ctrl+Shift+C", self, self.presetManager.clearPreset + for delkey in ("Ctrl+R", QtCore.Qt.Key.Key_Delete): + QShortcut(delkey, self.listWidget_componentList, self.removeComponent) + QShortcut( + "Ctrl+Space", + self, + activated=lambda: self.listWidget_componentList.setFocus(), ) + QShortcut("Ctrl+Shift+S", self, self.presetManager.openSavePresetDialog) + QShortcut("Ctrl+Shift+C", self, self.presetManager.clearPreset) - QtWidgets.QShortcut( - "Ctrl+Up", self.listWidget_componentList, - activated=lambda: self.moveComponent(-1) + QShortcut( + "Ctrl+Up", + self.listWidget_componentList, + activated=lambda: self.moveComponent(-1), ) - QtWidgets.QShortcut( - "Ctrl+Down", self.listWidget_componentList, - activated=lambda: self.moveComponent(1) + QShortcut( + "Ctrl+Down", + self.listWidget_componentList, + activated=lambda: self.moveComponent(1), ) - QtWidgets.QShortcut( - "Ctrl+Home", self.listWidget_componentList, - activated=lambda: self.moveComponent('top') + QShortcut( + "Ctrl+Home", + self.listWidget_componentList, + activated=lambda: self.moveComponent("top"), ) - QtWidgets.QShortcut( - "Ctrl+End", self.listWidget_componentList, - activated=lambda: self.moveComponent('bottom') + QShortcut( + "Ctrl+End", + self.listWidget_componentList, + activated=lambda: self.moveComponent("bottom"), ) - QtWidgets.QShortcut( - "Ctrl+Shift+F", self, self.showFfmpegCommand - ) - QtWidgets.QShortcut( - "Ctrl+Shift+U", self, self.showUndoStack - ) + QShortcut("Ctrl+Shift+F", self, self.showFfmpegCommand) + QShortcut("Ctrl+Shift+U", self, self.showUndoStack) if log.isEnabledFor(logging.DEBUG): - QtWidgets.QShortcut( - "Ctrl+Alt+Shift+R", self, self.drawPreview - ) - QtWidgets.QShortcut( - "Ctrl+Alt+Shift+A", self, lambda: log.debug(repr(self)) - ) + QShortcut("Ctrl+Alt+Shift+R", self, self.drawPreview) + QShortcut("Ctrl+Alt+Shift+A", self, lambda: log.debug(repr(self))) # Close MainWindow when receiving Ctrl+C from terminal signal.signal(signal.SIGINT, lambda *args: self.close()) @@ -450,18 +421,27 @@ class MainWindow(QtWidgets.QMainWindow): def __repr__(self): return ( - '%s\n' - '\n%s\n' - '#####\n' - 'Preview thread is %s\n' % ( + "%s\n" + "\n%s\n" + "#####\n" + "Preview thread is %s\n" + % ( super().__repr__(), - "core not initialized" if not hasattr(self, "core") else repr(self.core), - 'live' if hasattr(self, "previewThread") and self.previewThread.isRunning() else 'dead', + ( + "core not initialized" + if not hasattr(self, "core") + else repr(self.core) + ), + ( + "live" + if hasattr(self, "previewThread") and self.previewThread.isRunning() + else "dead" + ), ) ) def closeEvent(self, event): - log.info('Ending the preview thread') + log.info("Ending the preview thread") self.timer.stop() self.previewThread.quit() self.previewThread.wait() @@ -473,11 +453,11 @@ class MainWindow(QtWidgets.QMainWindow): windowTitle = appName try: if self.currentProject: - windowTitle += ' - %s' % \ - os.path.splitext( - os.path.basename(self.currentProject))[0] + windowTitle += ( + " - %s" % os.path.splitext(os.path.basename(self.currentProject))[0] + ) if self.autosaveExists(identical=False): - windowTitle += '*' + windowTitle += "*" except AttributeError: pass log.verbose(f'Window title is "{windowTitle}"') @@ -485,38 +465,38 @@ class MainWindow(QtWidgets.QMainWindow): @QtCore.pyqtSlot(int, dict) def updateComponentTitle(self, pos, presetStore=False): - ''' - Sets component title to modified or unmodified when given boolean. - If given a preset dict, compares it against the component to - determine if it is modified. - A component with no preset is always unmodified. - ''' + """ + Sets component title to modified or unmodified when given boolean. + If given a preset dict, compares it against the component to + determine if it is modified. + A component with no preset is always unmodified. + """ if type(presetStore) is dict: - name = presetStore['preset'] + name = presetStore["preset"] if name is None or name not in self.core.savedPresets: modified = False else: - modified = (presetStore != self.core.savedPresets[name]) + modified = presetStore != self.core.savedPresets[name] modified = bool(presetStore) if pos < 0: - pos = len(self.core.selectedComponents)-1 + pos = len(self.core.selectedComponents) - 1 name = self.core.selectedComponents[pos].name title = str(name) if self.core.selectedComponents[pos].currentPreset: - title += ' - %s' % self.core.selectedComponents[pos].currentPreset + title += " - %s" % self.core.selectedComponents[pos].currentPreset if modified: - title += '*' + title += "*" if type(presetStore) is bool: log.debug( - 'Forcing %s #%s\'s modified status to %s: %s', - name, pos, modified, title + "Forcing %s #%s's modified status to %s: %s", + name, + pos, + modified, + title, ) else: - log.debug( - 'Setting %s #%s\'s title: %s', - name, pos, title - ) + log.debug("Setting %s #%s's title: %s", name, pos, title) self.listWidget_componentList.item(pos).setText(title) def updateCodecs(self): @@ -525,20 +505,20 @@ class MainWindow(QtWidgets.QMainWindow): aCodecWidget = self.comboBox_audioCodec index = containerWidget.currentIndex() name = containerWidget.itemText(index) - self.settings.setValue('outputContainer', name) + self.settings.setValue("outputContainer", name) vCodecWidget.clear() aCodecWidget.clear() - for container in Core.encoderOptions['containers']: - if container['name'] == name: - for vCodec in container['video-codecs']: + for container in Core.encoderOptions["containers"]: + if container["name"] == name: + for vCodec in container["video-codecs"]: vCodecWidget.addItem(vCodec) - for aCodec in container['audio-codecs']: + for aCodec in container["audio-codecs"]: aCodecWidget.addItem(aCodec) def updateCodecSettings(self): - '''Updates settings.ini to match encoder option widgets''' + """Updates settings.ini to match encoder option widgets""" vCodecWidget = self.comboBox_videoCodec vBitrateWidget = self.spinBox_vBitrate aBitrateWidget = self.spinBox_aBitrate @@ -549,10 +529,10 @@ class MainWindow(QtWidgets.QMainWindow): currentAudioCodec = aCodecWidget.currentIndex() currentAudioCodec = aCodecWidget.itemText(currentAudioCodec) currentAudioBitrate = aBitrateWidget.value() - self.settings.setValue('outputVideoCodec', currentVideoCodec) - self.settings.setValue('outputAudioCodec', currentAudioCodec) - self.settings.setValue('outputVideoBitrate', currentVideoBitrate) - self.settings.setValue('outputAudioBitrate', currentAudioBitrate) + self.settings.setValue("outputVideoCodec", currentVideoCodec) + self.settings.setValue("outputAudioCodec", currentAudioCodec) + self.settings.setValue("outputVideoBitrate", currentVideoBitrate) + self.settings.setValue("outputAudioBitrate", currentAudioBitrate) @disableWhenOpeningProject def autosave(self, force=False): @@ -567,54 +547,54 @@ class MainWindow(QtWidgets.QMainWindow): # curve up to 5 seconds cooldown and maintains that for 30 secs # if a component is continuously updated timeDiff = self.lastAutosave - self.autosaveTimes.pop() - if not force and timeDiff >= 1.0 \ - and timeDiff <= 10.0: + if not force and timeDiff >= 1.0 and timeDiff <= 10.0: if self.autosaveCooldown / 4.0 < 0.5: self.autosaveCooldown += 1.0 - self.autosaveCooldown = ( - 5.0 * (self.autosaveCooldown / 5.0) - ) + (self.autosaveCooldown / 5.0) * 2 + self.autosaveCooldown = (5.0 * (self.autosaveCooldown / 5.0)) + ( + self.autosaveCooldown / 5.0 + ) * 2 elif force or timeDiff >= self.autosaveCooldown * 5: self.autosaveCooldown = 0.2 self.autosaveTimes.insert(0, self.lastAutosave) else: - log.debug('Autosave rejected by cooldown') + log.debug("Autosave rejected by cooldown") def autosaveExists(self, identical=True): - '''Determines if creating the autosave should be blocked.''' + """Determines if creating the autosave should be blocked.""" try: - if self.currentProject and os.path.exists(self.autosavePath) \ - and filecmp.cmp( - self.autosavePath, self.currentProject) == identical: + if ( + self.currentProject + and os.path.exists(self.autosavePath) + and filecmp.cmp(self.autosavePath, self.currentProject) == identical + ): log.debug( - 'Autosave found %s to be identical' - % 'not' if not identical else '' + "Autosave found %s to be identical" % "not" if not identical else "" ) return True except FileNotFoundError: - log.error( - 'Project file couldn\'t be located: %s', self.currentProject) + log.error("Project file couldn't be located: %s", self.currentProject) return identical return False def saveProjectChanges(self): - '''Overwrites project file with autosave file''' + """Overwrites project file with autosave file""" try: os.remove(self.currentProject) os.rename(self.autosavePath, self.currentProject) return True except (FileNotFoundError, IsADirectoryError) as e: - self.showMessage( - msg='Project file couldn\'t be saved.', - detail=str(e)) + self.showMessage(msg="Project file couldn't be saved.", detail=str(e)) return False def openInputFileDialog(self): inputDir = self.settings.value("inputDir", os.path.expanduser("~")) fileName, _ = QtWidgets.QFileDialog.getOpenFileName( - self, "Open Audio File", - inputDir, "Audio Files (%s)" % " ".join(Core.audioFormats)) + self, + "Open Audio File", + inputDir, + "Audio Files (%s)" % " ".join(Core.audioFormats), + ) if fileName: self.settings.setValue("inputDir", os.path.dirname(fileName)) @@ -624,17 +604,18 @@ class MainWindow(QtWidgets.QMainWindow): outputDir = self.settings.value("outputDir", os.path.expanduser("~")) fileName, _ = QtWidgets.QFileDialog.getSaveFileName( - self, "Set Output Video File", + self, + "Set Output Video File", outputDir, - "Video Files (%s);; All Files (*)" % " ".join( - Core.videoFormats)) + "Video Files (%s);; All Files (*)" % " ".join(Core.videoFormats), + ) if fileName: self.settings.setValue("outputDir", os.path.dirname(fileName)) self.lineEdit_outputFile.setText(fileName) def stopVideo(self): - log.info('Export cancelled') + log.info("Export cancelled") self.videoWorker.cancel() self.canceled = True @@ -645,14 +626,13 @@ class MainWindow(QtWidgets.QMainWindow): if audioFile and outputPath and self.core.selectedComponents: if not os.path.dirname(outputPath): - outputPath = os.path.join( - os.path.expanduser("~"), outputPath) + outputPath = os.path.join(os.path.expanduser("~"), outputPath) if outputPath and os.path.isdir(outputPath): self.showMessage( - msg='Chosen filename matches a directory, which ' - 'cannot be overwritten. Please choose a different ' - 'filename or move the directory.', - icon='Warning', + msg="Chosen filename matches a directory, which " + "cannot be overwritten. Please choose a different " + "filename or move the directory.", + icon="Warning", ) return else: @@ -661,19 +641,14 @@ class MainWindow(QtWidgets.QMainWindow): msg="You must select an audio file and output filename." ) elif not self.core.selectedComponents: - self.showMessage( - msg="Not enough components." - ) + self.showMessage(msg="Not enough components.") return self.canceled = False self.progressBarUpdated(-1) - self.videoWorker = self.core.newVideoWorker( - self, audioFile, outputPath - ) + self.videoWorker = self.core.newVideoWorker(self, audioFile, outputPath) self.videoWorker.progressBarUpdate.connect(self.progressBarUpdated) - self.videoWorker.progressBarSetText.connect( - self.progressBarSetText) + self.videoWorker.progressBarSetText.connect(self.progressBarSetText) self.videoWorker.imageCreated.connect(self.showPreviewImage) self.videoWorker.encoding.connect(self.changeEncodingStatus) self.createVideo.emit() @@ -683,14 +658,14 @@ class MainWindow(QtWidgets.QMainWindow): try: self.stopVideo() except AttributeError as e: - if 'videoWorker' not in str(e): + if "videoWorker" not in str(e): raise self.showMessage( msg=msg, detail=detail, - icon='Critical', + icon="Critical", ) - log.info('%s', repr(self)) + log.info("%s", repr(self)) def changeEncodingStatus(self, status): self.encoding = status @@ -718,7 +693,7 @@ class MainWindow(QtWidgets.QMainWindow): # Close undo history dialog if open self.undoDialog.close() # Show label under progress bar on macOS - if sys.platform == 'darwin': + if sys.platform == "darwin": self.progressLabel.setHidden(False) else: self.pushButton_createVideo.setEnabled(True) @@ -749,33 +724,33 @@ class MainWindow(QtWidgets.QMainWindow): @QtCore.pyqtSlot(str) def progressBarSetText(self, value): - if sys.platform == 'darwin': + if sys.platform == "darwin": self.progressLabel.setText(value) else: self.progressBar_createVideo.setFormat(value) def updateResolution(self): resIndex = int(self.comboBox_resolution.currentIndex()) - res = Core.resolutions[resIndex].split('x') + res = Core.resolutions[resIndex].split("x") changed = res[0] != self.settings.value("outputWidth") - self.settings.setValue('outputWidth', res[0]) - self.settings.setValue('outputHeight', res[1]) + self.settings.setValue("outputWidth", res[0]) + self.settings.setValue("outputHeight", res[1]) if changed: for i in range(len(self.core.selectedComponents)): self.core.updateComponent(i) def drawPreview(self, force=False, **kwargs): - '''Use autosave keyword arg to force saving or not saving if needed''' + """Use autosave keyword arg to force saving or not saving if needed""" self.newTask.emit(self.core.selectedComponents) # self.processTask.emit() - if force or 'autosave' in kwargs: - if force or kwargs['autosave']: + if force or "autosave" in kwargs: + if force or kwargs["autosave"]: self.autosave(True) else: self.autosave() self.updateWindowTitle() - @QtCore.pyqtSlot('QImage') + @QtCore.pyqtSlot("QImage") def showPreviewImage(self, image): self.previewWindow.changePixmap(image) @@ -786,36 +761,35 @@ class MainWindow(QtWidgets.QMainWindow): def showFfmpegCommand(self): from textwrap import wrap from ..toolkit.ffmpeg import createFfmpegCommand + command = createFfmpegCommand( self.lineEdit_audioFile.text(), self.lineEdit_outputFile.text(), - self.core.selectedComponents + self.core.selectedComponents, ) command = " ".join(command) log.info(f"FFmpeg command: {command}") lines = wrap(command, 49) - self.showMessage( - msg=f"Current FFmpeg command:\n\n{' '.join(lines)}" - ) + self.showMessage(msg=f"Current FFmpeg command:\n\n{' '.join(lines)}") def addComponent(self, compPos, moduleIndex): - '''Creates an undoable action that adds a new component.''' + """Creates an undoable action that adds a new component.""" action = AddComponent(self, compPos, moduleIndex) self.undoStack.push(action) def insertComponent(self, index): - '''Triggered by Core to finish initializing a new component.''' + """Triggered by Core to finish initializing a new component.""" + if not hasattr(self.core.selectedComponents[index], "page"): + log.error("Component failed to initialize") + return componentList = self.listWidget_componentList stackedWidget = self.stackedWidget - componentList.insertItem( - index, - self.core.selectedComponents[index].name) + componentList.insertItem(index, self.core.selectedComponents[index].name) componentList.setCurrentRow(index) # connect to signal that adds an asterisk when modified - self.core.selectedComponents[index].modified.connect( - self.updateComponentTitle) + self.core.selectedComponents[index].modified.connect(self.updateComponentTitle) self.pages.insert(index, self.core.selectedComponents[index].page) stackedWidget.insertWidget(index, self.pages[index]) @@ -842,15 +816,15 @@ class MainWindow(QtWidgets.QMainWindow): @disableWhenEncoding def moveComponent(self, change): - '''Moves a component relatively from its current position''' + """Moves a component relatively from its current position""" componentList = self.listWidget_componentList tag = change - if change == 'top': + if change == "top": change = -componentList.currentRow() - elif change == 'bottom': - change = len(componentList)-componentList.currentRow()-1 + elif change == "bottom": + change = len(componentList) - componentList.currentRow() - 1 else: - tag = 'down' if change == 1 else 'up' + tag = "down" if change == 1 else "up" row = componentList.currentRow() newRow = row + change @@ -859,38 +833,39 @@ class MainWindow(QtWidgets.QMainWindow): self.undoStack.push(action) def getComponentListMousePos(self, position): - ''' + """ Given a QPos, returns the component index under the mouse cursor or -1 if no component is there. - ''' + """ componentList = self.listWidget_componentList + if hasattr(position, "toPointF"): + position = position.toPointF() + position = position.toPoint() + modelIndexes = [ - componentList.model().index(i) - for i in range(componentList.count()) - ] - rects = [ - componentList.visualRect(modelIndex) - for modelIndex in modelIndexes + componentList.model().index(i) for i in range(componentList.count()) ] + rects = [componentList.visualRect(modelIndex) for modelIndex in modelIndexes] mousePos = [rect.contains(position) for rect in rects] if not any(mousePos): # Not clicking a component mousePos = -1 else: mousePos = mousePos.index(True) - log.debug('Click component list row %s' % mousePos) + log.debug("Click component list row %s" % mousePos) return mousePos @disableWhenEncoding def dragComponent(self, event): - '''Used as Qt drop event for the component listwidget''' + """Used as Qt drop event for the component listwidget""" componentList = self.listWidget_componentList - mousePos = self.getComponentListMousePos(event.pos()) + mousePos = self.getComponentListMousePos(event.position()) + if mousePos > -1: change = (componentList.currentRow() - mousePos) * -1 else: - change = (componentList.count() - componentList.currentRow() - 1) + change = componentList.count() - componentList.currentRow() - 1 self.moveComponent(change) def changeComponentWidget(self): @@ -900,30 +875,27 @@ class MainWindow(QtWidgets.QMainWindow): self.stackedWidget.setCurrentIndex(index) def openPresetManager(self): - '''Preset manager for importing, exporting, renaming, deleting''' + """Preset manager for importing, exporting, renaming, deleting""" self.presetManager.show_() def clear(self): - '''Get a blank slate''' + """Get a blank slate""" self.core.clearComponents() self.listWidget_componentList.clear() for widget in self.pages: self.stackedWidget.removeWidget(widget) self.pages = [] - for field in ( - self.lineEdit_audioFile, - self.lineEdit_outputFile - ): + for field in (self.lineEdit_audioFile, self.lineEdit_outputFile): with blockSignals(field): - field.setText('') + field.setText("") self.progressBarUpdated(0) - self.progressBarSetText('') + self.progressBarSetText("") self.undoStack.clear() @disableWhenEncoding def createNewProject(self, prompt=True): if prompt: - self.openSaveChangesDialog('starting a new project') + self.openSaveChangesDialog("starting a new project") self.clear() self.currentProject = None @@ -946,11 +918,10 @@ class MainWindow(QtWidgets.QMainWindow): if self.autosaveExists(identical=False): ch = self.showMessage( msg="You have unsaved changes in project '%s'. " - "Save before %s?" % ( - os.path.basename(self.currentProject)[:-4], - phrase - ), - showCancel=True) + "Save before %s?" + % (os.path.basename(self.currentProject)[:-4], phrase), + showCancel=True, + ) if ch: success = self.saveProjectChanges() @@ -959,13 +930,15 @@ class MainWindow(QtWidgets.QMainWindow): def openSaveProjectDialog(self): filename, _ = QtWidgets.QFileDialog.getSaveFileName( - self, "Create Project File", + self, + "Create Project File", self.settings.value("projectDir"), - "Project Files (*.avp)") + "Project Files (*.avp)", + ) if not filename: return if not filename.endswith(".avp"): - filename += '.avp' + filename += ".avp" self.settings.setValue("projectDir", os.path.dirname(filename)) self.settings.setValue("currentProject", filename) self.currentProject = filename @@ -975,20 +948,25 @@ class MainWindow(QtWidgets.QMainWindow): @disableWhenEncoding def openOpenProjectDialog(self): filename, _ = QtWidgets.QFileDialog.getOpenFileName( - self, "Open Project File", + self, + "Open Project File", self.settings.value("projectDir"), - "Project Files (*.avp)") + "Project Files (*.avp)", + ) self.openProject(filename) def openProject(self, filepath, prompt=True): - if not filepath or not os.path.exists(filepath) \ - or not filepath.endswith('.avp'): + if ( + not filepath + or not os.path.exists(filepath) + or not filepath.endswith(".avp") + ): return self.clear() # ask to save any changes that are about to get deleted if prompt: - self.openSaveChangesDialog('opening another project') + self.openSaveChangesDialog("opening another project") self.currentProject = filepath self.settings.setValue("currentProject", filepath) @@ -999,29 +977,32 @@ class MainWindow(QtWidgets.QMainWindow): self.updateWindowTitle() def showMessage(self, **kwargs): - parent = kwargs['parent'] if 'parent' in kwargs else self + parent = kwargs["parent"] if "parent" in kwargs else self msg = QtWidgets.QMessageBox(parent) msg.setWindowTitle(appName) msg.setModal(True) - msg.setText(kwargs['msg']) + msg.setText(kwargs["msg"]) msg.setIcon( - eval('QtWidgets.QMessageBox.%s' % kwargs['icon']) - if 'icon' in kwargs else QtWidgets.QMessageBox.Information + eval("QtWidgets.QMessageBox.Icon.%s" % kwargs["icon"]) + if "icon" in kwargs + else QtWidgets.QMessageBox.Icon.Information ) - msg.setDetailedText(kwargs['detail'] if 'detail' in kwargs else None) - if 'showCancel'in kwargs and kwargs['showCancel']: + msg.setDetailedText(kwargs["detail"] if "detail" in kwargs else None) + if "showCancel" in kwargs and kwargs["showCancel"]: msg.setStandardButtons( - QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + QtWidgets.QMessageBox.StandardButton.Ok + | QtWidgets.QMessageBox.StandardButton.Cancel + ) else: - msg.setStandardButtons(QtWidgets.QMessageBox.Ok) - ch = msg.exec_() + msg.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) + ch = msg.exec() if ch == 1024: return True return False @disableWhenEncoding def componentContextMenu(self, QPos): - '''Appears when right-clicking the component list''' + """Appears when right-clicking the component list""" componentList = self.listWidget_componentList self.menu = QtWidgets.QMenu() parentPosition = componentList.mapToGlobal(QtCore.QPoint(0, 0)) @@ -1031,9 +1012,7 @@ class MainWindow(QtWidgets.QMainWindow): # Show preset menu if clicking a component self.presetManager.findPresets() menuItem = self.menu.addAction("Save Preset") - menuItem.triggered.connect( - self.presetManager.openSavePresetDialog - ) + menuItem.triggered.connect(self.presetManager.openSavePresetDialog) # submenu for opening presets try: @@ -1046,17 +1025,16 @@ class MainWindow(QtWidgets.QMainWindow): for version, presetName in presets: menuItem = self.presetSubmenu.addAction(presetName) menuItem.triggered.connect( - lambda _, presetName=presetName: - self.presetManager.openPreset(presetName) + lambda _, presetName=presetName: self.presetManager.openPreset( + presetName + ) ) except KeyError: pass if self.core.selectedComponents[index].currentPreset: menuItem = self.menu.addAction("Clear Preset") - menuItem.triggered.connect( - self.presetManager.clearPreset - ) + menuItem.triggered.connect(self.presetManager.clearPreset) self.menu.addSeparator() # "Add Component" submenu -- cgit v1.2.3