aboutsummaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/__init__.py0
-rw-r--r--src/gui/actions.py196
-rw-r--r--src/gui/background.pngbin45367 -> 0 bytes
-rw-r--r--src/gui/mainwindow.py1053
-rw-r--r--src/gui/mainwindow.ui835
-rw-r--r--src/gui/presetmanager.py349
-rw-r--r--src/gui/presetmanager.ui150
-rw-r--r--src/gui/preview_thread.py93
-rw-r--r--src/gui/preview_win.py58
9 files changed, 0 insertions, 2734 deletions
diff --git a/src/gui/__init__.py b/src/gui/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/gui/__init__.py
+++ /dev/null
diff --git a/src/gui/actions.py b/src/gui/actions.py
deleted file mode 100644
index 654b2a0..0000000
--- a/src/gui/actions.py
+++ /dev/null
@@ -1,196 +0,0 @@
-"""
-QCommand classes for every undoable user action performed in the MainWindow
-"""
-
-from PyQt6.QtGui import QUndoCommand
-import os
-import logging
-from copy import copy
-
-from ..core import Core
-
-
-log = logging.getLogger("AVP.Gui.Actions")
-
-
-# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~
-# COMPONENT ACTIONS
-# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~
-
-
-class AddComponent(QUndoCommand):
- def __init__(self, parent, compI, moduleI):
- super().__init__(
- "create new %s component" % parent.core.modules[moduleI].Component.name
- )
- self.parent = parent
- self.moduleI = moduleI
- self.compI = compI
- self.comp = None
- self.valid = True
-
- def redo(self):
- if self.comp is None:
- i = self.parent.core.insertComponent(self.compI, self.moduleI, self.parent)
- if i != self.compI:
- self.valid = False
- if i is not None:
- log.error(
- f"Expected new component index to be {self.compI} but received {i}"
- )
- else:
- # inserting previously-created component
- self.parent.core.insertComponent(self.compI, self.comp, self.parent)
-
- def undo(self):
- if not self.valid:
- return
- self.comp = self.parent.core.selectedComponents[self.compI]
- self.parent._removeComponent(self.compI)
-
-
-class RemoveComponent(QUndoCommand):
- def __init__(self, parent, selectedRows):
- super().__init__("remove component")
- self.parent = parent
- componentList = self.parent.listWidget_componentList
- self.selectedRows = [componentList.row(selected) for selected in selectedRows]
- self.components = [parent.core.selectedComponents[i] for i in self.selectedRows]
-
- def redo(self):
- self.parent._removeComponent(self.selectedRows[0])
-
- def undo(self):
- componentList = self.parent.listWidget_componentList
- for index, comp in zip(self.selectedRows, self.components):
- self.parent.core.insertComponent(index, comp, self.parent)
- self.parent.drawPreview()
-
-
-class MoveComponent(QUndoCommand):
- def __init__(self, parent, row, newRow, tag):
- super().__init__("move component %s" % tag)
- self.parent = parent
- self.row = row
- self.newRow = newRow
- self.id_ = ord(tag[0])
-
- def id(self):
- """If 2 consecutive updates have same id, Qt will call mergeWith()"""
- return self.id_
-
- def mergeWith(self, other):
- self.newRow = other.newRow
- return True
-
- def do(self, rowa, rowb):
- componentList = self.parent.listWidget_componentList
-
- page = self.parent.pages.pop(rowa)
- self.parent.pages.insert(rowb, page)
-
- item = componentList.takeItem(rowa)
- componentList.insertItem(rowb, item)
-
- stackedWidget = self.parent.stackedWidget
- widget = stackedWidget.removeWidget(page)
- stackedWidget.insertWidget(rowb, page)
- componentList.setCurrentRow(rowb)
- stackedWidget.setCurrentIndex(rowb)
- self.parent.core.moveComponent(rowa, rowb)
- self.parent.drawPreview(True)
-
- def redo(self):
- self.do(self.row, self.newRow)
-
- def undo(self):
- self.do(self.newRow, self.row)
-
-
-# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~
-# PRESET ACTIONS
-# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~
-
-
-class ClearPreset(QUndoCommand):
- def __init__(self, parent, compI):
- super().__init__("clear preset")
- self.parent = parent
- self.compI = compI
- self.component = self.parent.core.selectedComponents[compI]
- self.store = self.component.savePreset()
- self.store["preset"] = self.component.currentPreset
-
- def redo(self):
- self.parent.core.clearPreset(self.compI)
- self.parent.updateComponentTitle(self.compI, False)
-
- def undo(self):
- self.parent.core.selectedComponents[self.compI].loadPreset(self.store)
- self.parent.updateComponentTitle(self.compI, self.store)
-
-
-class OpenPreset(QUndoCommand):
- def __init__(self, parent, presetName, compI):
- super().__init__("open %s preset" % presetName)
- self.parent = parent
- self.presetName = presetName
- self.compI = compI
-
- comp = self.parent.core.selectedComponents[compI]
- self.store = comp.savePreset()
- self.store["preset"] = copy(comp.currentPreset)
-
- def redo(self):
- self.parent._openPreset(self.presetName, self.compI)
-
- def undo(self):
- self.parent.core.selectedComponents[self.compI].loadPreset(self.store)
- self.parent.parent.updateComponentTitle(self.compI, self.store)
-
-
-class RenamePreset(QUndoCommand):
- def __init__(self, parent, path, oldName, newName):
- super().__init__("rename preset")
- self.parent = parent
- self.path = path
- self.oldName = oldName
- self.newName = newName
-
- def redo(self):
- self.parent.renamePreset(self.path, self.oldName, self.newName)
-
- def undo(self):
- self.parent.renamePreset(self.path, self.newName, self.oldName)
-
-
-class DeletePreset(QUndoCommand):
- def __init__(self, parent, compName, vers, presetFile):
- self.parent = parent
- self.preset = (compName, vers, presetFile)
- self.path = os.path.join(Core.presetDir, compName, str(vers), presetFile)
- self.store = self.parent.core.getPreset(self.path)
- self.presetName = self.store["preset"]
- super().__init__("delete %s preset (%s)" % (self.presetName, compName))
- self.loadedPresets = [
- i
- for i, comp in enumerate(self.parent.core.selectedComponents)
- if self.presetName == str(comp.currentPreset)
- ]
-
- def redo(self):
- os.remove(self.path)
- for i in self.loadedPresets:
- self.parent.core.clearPreset(i)
- self.parent.parent.updateComponentTitle(i, False)
- self.parent.findPresets()
- self.parent.drawPresetList()
-
- def undo(self):
- self.parent.createNewPreset(*self.preset, self.store)
- selectedComponents = self.parent.core.selectedComponents
- for i in self.loadedPresets:
- selectedComponents[i].currentPreset = self.presetName
- self.parent.parent.updateComponentTitle(i)
- self.parent.findPresets()
- self.parent.drawPresetList()
diff --git a/src/gui/background.png b/src/gui/background.png
deleted file mode 100644
index fb58593..0000000
--- a/src/gui/background.png
+++ /dev/null
Binary files differ
diff --git a/src/gui/mainwindow.py b/src/gui/mainwindow.py
deleted file mode 100644
index b0a564b..0000000
--- a/src/gui/mainwindow.py
+++ /dev/null
@@ -1,1053 +0,0 @@
-"""
-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
-import os
-import signal
-import filecmp
-import time
-import logging
-
-from ..core import Core
-from . import preview_thread
-from .preview_win import PreviewWindow
-from .presetmanager import PresetManager
-from .actions import *
-from ..toolkit import (
- disableWhenEncoding,
- disableWhenOpeningProject,
- checkOutput,
- blockSignals,
-)
-
-
-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.
-
- 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, dpi):
- super().__init__()
- log.debug("Main thread id: {}".format(int(QtCore.QThread.currentThreadId())))
- uic.loadUi(os.path.join(Core.wd, "gui", "mainwindow.ui"), self)
-
- if dpi:
- self.resize(
- int(self.width() * (dpi / 144)),
- int(self.height() * (dpi / 144)),
- )
-
- self.core = Core()
- Core.mode = "GUI"
- # widgets of component settings
- self.pages = []
- self.lastAutosave = time.time()
- # list of previous five autosave times, used to reduce update spam
- self.autosaveTimes = []
- self.autosaveCooldown = 0.2
- self.encoding = False
-
- # Find settings created by Core object
- self.dataDir = Core.dataDir
- self.presetDir = Core.presetDir
- self.autosavePath = os.path.join(self.dataDir, "autosave.avp")
- self.settings = Core.settings
-
- # Create stack of undoable user actions
- self.undoStack = MyQUndoStack(self)
- undoLimit = self.settings.value("pref_undoLimit")
- self.undoStack.setUndoLimit(undoLimit)
-
- # Create Undo Dialog - A standard QUndoView on a standard QDialog
- self.undoDialog = QtWidgets.QDialog(self)
- self.undoDialog.setWindowTitle("Undo History")
- undoView = QtWidgets.QUndoView(self.undoStack)
- layout = QtWidgets.QVBoxLayout()
- layout.addWidget(undoView)
- self.undoDialog.setLayout(layout)
-
- # Create Preset Manager
- 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")
- )
- self.verticalLayout_previewWrapper.addWidget(self.previewWindow)
-
- 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.previewWorker.moveToThread(self.previewThread)
- self.newTask.connect(self.previewWorker.createPreviewImage)
- self.processTask.connect(self.previewWorker.process)
- 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.")
- )
-
- timeout = 500
- 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)
-
- # Begin decorating the window and connecting events
- componentList = self.listWidget_componentList
-
- # Undo Feature
- def toggleUndoButtonEnabled(*_):
- """Enable/disable undo button depending on whether UndoStack contains Actions"""
- try:
- undoButton.setEnabled(self.undoStack.count())
- except RuntimeError:
- # program is probably in midst of exiting
- pass
-
- style = self.pushButton_undo.style()
- undoButton = self.pushButton_undo
- undoButton.setIcon(
- 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())
- undoButton.setMenu(self.undoMenu)
- # end of Undo Feature
-
- style = self.pushButton_listMoveUp.style()
- self.pushButton_listMoveUp.setIcon(
- style.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_ArrowUp)
- )
- style = self.pushButton_listMoveDown.style()
- self.pushButton_listMoveDown.setIcon(
- style.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_ArrowDown)
- )
- style = self.pushButton_removeComponent.style()
- self.pushButton_removeComponent.setIcon(
- style.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_DialogDiscardButton)
- )
-
- 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_selectOutputFile.clicked.connect(self.openOutputFileDialog)
-
- def changedField():
- self.autosave()
- self.updateWindowTitle()
-
- self.lineEdit_audioFile.textChanged.connect(changedField)
- self.lineEdit_outputFile.textChanged.connect(changedField)
-
- self.progressBar_createVideo.setValue(0)
-
- 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"):
- selectedContainer = i
-
- self.comboBox_videoContainer.setCurrentIndex(selectedContainer)
- 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"):
- 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"):
- self.comboBox_audioCodec.setCurrentIndex(i)
-
- self.comboBox_videoCodec.currentIndexChanged.connect(self.updateCodecSettings)
-
- self.comboBox_audioCodec.currentIndexChanged.connect(self.updateCodecSettings)
-
- vBitrate = int(self.settings.value("outputVideoBitrate"))
- aBitrate = int(self.settings.value("outputAudioBitrate"))
-
- self.spinBox_vBitrate.setValue(vBitrate)
- self.spinBox_aBitrate.setValue(aBitrate)
- self.spinBox_vBitrate.valueChanged.connect(self.updateCodecSettings)
- self.spinBox_aBitrate.valueChanged.connect(self.updateCodecSettings)
-
- # Make component buttons
- 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))
-
- self.pushButton_addComponent.setMenu(self.compMenu)
-
- componentList.dropEvent = self.dragComponent
- componentList.itemSelectionChanged.connect(self.changeComponentWidget)
- componentList.itemSelectionChanged.connect(
- self.presetManager.clearPresetListSelection
- )
- self.pushButton_removeComponent.clicked.connect(lambda: self.removeComponent())
-
- componentList.setContextMenuPolicy(
- QtCore.Qt.ContextMenuPolicy.CustomContextMenu
- )
- componentList.customContextMenuRequested.connect(self.componentContextMenu)
-
- 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:
- currentRes = i
- self.comboBox_resolution.setCurrentIndex(currentRes)
- self.comboBox_resolution.currentIndexChanged.connect(
- self.updateResolution
- )
-
- 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_openProject.triggered.connect(
- lambda: self.openOpenProjectDialog()
- )
-
- action = self.projectMenu.addAction("Save Project")
- action.triggered.connect(self.saveCurrentProject)
-
- action = self.projectMenu.addAction("Save Project As")
- action.triggered.connect(self.openSaveProjectDialog)
-
- self.pushButton_projects.setMenu(self.projectMenu)
-
- # Configure the Presets Button
- self.pushButton_presets.clicked.connect(self.openPresetManager)
-
- self.updateWindowTitle()
- log.debug("Showing main window")
- self.show()
-
- if project and project != self.autosavePath:
- 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)
- self.currentProject = project
- self.settings.setValue("currentProject", project)
- if os.path.exists(self.autosavePath):
- os.remove(self.autosavePath)
- else:
- # open the last currentProject from settings
- self.currentProject = self.settings.value("currentProject")
-
- # delete autosave if it's identical to this project
- if self.autosaveExists(identical=True):
- os.remove(self.autosavePath)
-
- if self.currentProject and os.path.exists(self.autosavePath):
- ch = self.showMessage(
- msg="Restore unsaved changes in project '%s'?"
- % os.path.basename(self.currentProject)[:-4],
- showCancel=True,
- )
- if ch:
- self.saveProjectChanges()
- else:
- os.remove(self.autosavePath)
-
- self.openProject(self.currentProject, prompt=False)
- self.drawPreview(True)
-
- log.info("Pillow version %s", Image.__version__)
-
- # verify Ffmpeg version
- if not self.core.FFMPEG_BIN:
- self.showMessage(
- 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",
- )
- else:
- if not self.settings.value("ffmpegMsgShown"):
- try:
- with open(os.devnull, "w") as f:
- ffmpegVers = checkOutput(
- [self.core.FFMPEG_BIN, "-version"], stderr=f
- )
- goodVersion = str(ffmpegVers).split()[2].startswith("4")
- except Exception:
- goodVersion = False
- else:
- goodVersion = True
-
- if not goodVersion:
- self.showMessage(
- msg="You're using an old version of Ffmpeg. "
- "Some features may not work as expected."
- )
- self.settings.setValue("ffmpegMsgShown", True)
-
- # Hotkeys for projects
-
- 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
- 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.Key_Insert):
- QShortcut(
- inskey,
- self,
- activated=lambda: self.pushButton_addComponent.click(),
- )
- 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)
-
- QShortcut(
- "Ctrl+Up",
- self.listWidget_componentList,
- activated=lambda: self.moveComponent(-1),
- )
- QShortcut(
- "Ctrl+Down",
- self.listWidget_componentList,
- activated=lambda: self.moveComponent(1),
- )
- QShortcut(
- "Ctrl+Home",
- self.listWidget_componentList,
- activated=lambda: self.moveComponent("top"),
- )
- QShortcut(
- "Ctrl+End",
- self.listWidget_componentList,
- activated=lambda: self.moveComponent("bottom"),
- )
-
- QShortcut("Ctrl+Shift+F", self, self.showFfmpegCommand)
- QShortcut("Ctrl+Shift+U", self, self.showUndoStack)
-
- if log.isEnabledFor(logging.DEBUG):
- 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())
-
- # Add initial components if none are in the list
- if not self.core.selectedComponents:
- self.core.insertComponent(0, 0, self)
- self.core.insertComponent(1, 1, self)
-
- def __repr__(self):
- return (
- "%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"
- ),
- )
- )
-
- def closeEvent(self, event):
- log.info("Ending the preview thread")
- self.timer.stop()
- self.previewThread.quit()
- self.previewThread.wait()
- return super().closeEvent(event)
-
- @disableWhenOpeningProject
- def updateWindowTitle(self):
- log.debug("Setting main window's title")
- windowTitle = appName
- try:
- if self.currentProject:
- windowTitle += (
- " - %s" % os.path.splitext(os.path.basename(self.currentProject))[0]
- )
- if self.autosaveExists(identical=False):
- windowTitle += "*"
- except AttributeError:
- pass
- log.verbose(f'Window title is "{windowTitle}"')
- self.setWindowTitle(windowTitle)
-
- @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.
- """
- if type(presetStore) is dict:
- name = presetStore["preset"]
- if name is None or name not in self.core.savedPresets:
- modified = False
- else:
- modified = presetStore != self.core.savedPresets[name]
-
- modified = bool(presetStore)
- if pos < 0:
- 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
- if modified:
- title += "*"
- if type(presetStore) is bool:
- log.debug(
- "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)
- self.listWidget_componentList.item(pos).setText(title)
-
- def updateCodecs(self):
- containerWidget = self.comboBox_videoContainer
- vCodecWidget = self.comboBox_videoCodec
- aCodecWidget = self.comboBox_audioCodec
- index = containerWidget.currentIndex()
- name = containerWidget.itemText(index)
- 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"]:
- vCodecWidget.addItem(vCodec)
- for aCodec in container["audio-codecs"]:
- aCodecWidget.addItem(aCodec)
-
- def updateCodecSettings(self):
- """Updates settings.ini to match encoder option widgets"""
- vCodecWidget = self.comboBox_videoCodec
- vBitrateWidget = self.spinBox_vBitrate
- aBitrateWidget = self.spinBox_aBitrate
- aCodecWidget = self.comboBox_audioCodec
- currentVideoCodec = vCodecWidget.currentIndex()
- currentVideoCodec = vCodecWidget.itemText(currentVideoCodec)
- currentVideoBitrate = vBitrateWidget.value()
- 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)
-
- @disableWhenOpeningProject
- def autosave(self, force=False):
- if not self.currentProject:
- if os.path.exists(self.autosavePath):
- os.remove(self.autosavePath)
- elif force or time.time() - self.lastAutosave >= self.autosaveCooldown:
- self.core.createProjectFile(self.autosavePath, self)
- self.lastAutosave = time.time()
- if len(self.autosaveTimes) >= 5:
- # Do some math to reduce autosave spam. This gives a smooth
- # 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 self.autosaveCooldown / 4.0 < 0.5:
- self.autosaveCooldown += 1.0
- 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")
-
- def autosaveExists(self, identical=True):
- """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
- ):
- log.debug(
- "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)
- return identical
- return False
-
- def saveProjectChanges(self):
- """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))
- 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),
- )
-
- if fileName:
- self.settings.setValue("inputDir", os.path.dirname(fileName))
- self.lineEdit_audioFile.setText(fileName)
-
- def openOutputFileDialog(self):
- outputDir = self.settings.value("outputDir", os.path.expanduser("~"))
-
- fileName, _ = QtWidgets.QFileDialog.getSaveFileName(
- self,
- "Set Output Video File",
- outputDir,
- "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")
- self.videoWorker.cancel()
- self.canceled = True
-
- def createAudioVisualization(self):
- # create output video if mandatory settings are filled in
- audioFile = self.lineEdit_audioFile.text()
- outputPath = self.lineEdit_outputFile.text()
-
- if audioFile and outputPath and self.core.selectedComponents:
- if not os.path.dirname(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",
- )
- return
- else:
- if not audioFile or not outputPath:
- self.showMessage(
- msg="You must select an audio file and output filename."
- )
- elif not self.core.selectedComponents:
- self.showMessage(msg="Not enough components.")
- return
-
- self.canceled = False
- self.progressBarUpdated(-1)
- self.videoWorker = self.core.newVideoWorker(self, audioFile, outputPath)
- self.videoWorker.progressBarUpdate.connect(self.progressBarUpdated)
- self.videoWorker.progressBarSetText.connect(self.progressBarSetText)
- self.videoWorker.imageCreated.connect(self.showPreviewImage)
- self.videoWorker.encoding.connect(self.changeEncodingStatus)
- self.createVideo.emit()
-
- @QtCore.pyqtSlot(str, str)
- def videoThreadError(self, msg, detail):
- try:
- self.stopVideo()
- except AttributeError as e:
- if "videoWorker" not in str(e):
- raise
- self.showMessage(
- msg=msg,
- detail=detail,
- icon="Critical",
- )
- log.info("%s", repr(self))
-
- def changeEncodingStatus(self, status):
- self.encoding = status
- if status:
- # Disable many widgets when starting to export
- self.pushButton_createVideo.setEnabled(False)
- self.pushButton_Cancel.setEnabled(True)
- self.comboBox_resolution.setEnabled(False)
- self.stackedWidget.setEnabled(False)
- self.tab_encoderSettings.setEnabled(False)
- self.label_audioFile.setEnabled(False)
- self.toolButton_selectAudioFile.setEnabled(False)
- self.label_outputFile.setEnabled(False)
- self.toolButton_selectOutputFile.setEnabled(False)
- self.lineEdit_audioFile.setEnabled(False)
- self.lineEdit_outputFile.setEnabled(False)
- self.listWidget_componentList.setEnabled(False)
- self.pushButton_addComponent.setEnabled(False)
- self.pushButton_removeComponent.setEnabled(False)
- self.pushButton_listMoveDown.setEnabled(False)
- self.pushButton_listMoveUp.setEnabled(False)
- self.pushButton_undo.setEnabled(False)
- self.menuButton_newProject.setEnabled(False)
- self.menuButton_openProject.setEnabled(False)
- # Close undo history dialog if open
- self.undoDialog.close()
- # Show label under progress bar on macOS
- if sys.platform == "darwin":
- self.progressLabel.setHidden(False)
- else:
- self.pushButton_createVideo.setEnabled(True)
- self.pushButton_Cancel.setEnabled(False)
- self.comboBox_resolution.setEnabled(True)
- self.stackedWidget.setEnabled(True)
- self.tab_encoderSettings.setEnabled(True)
- self.label_audioFile.setEnabled(True)
- self.toolButton_selectAudioFile.setEnabled(True)
- self.lineEdit_audioFile.setEnabled(True)
- self.label_outputFile.setEnabled(True)
- self.toolButton_selectOutputFile.setEnabled(True)
- self.lineEdit_outputFile.setEnabled(True)
- self.pushButton_addComponent.setEnabled(True)
- self.pushButton_removeComponent.setEnabled(True)
- self.pushButton_listMoveDown.setEnabled(True)
- self.pushButton_listMoveUp.setEnabled(True)
- self.pushButton_undo.setEnabled(True)
- self.menuButton_newProject.setEnabled(True)
- self.menuButton_openProject.setEnabled(True)
- self.listWidget_componentList.setEnabled(True)
- self.progressLabel.setHidden(True)
- self.drawPreview(True)
-
- @QtCore.pyqtSlot(int)
- def progressBarUpdated(self, value):
- self.progressBar_createVideo.setValue(value)
-
- @QtCore.pyqtSlot(str)
- def progressBarSetText(self, value):
- 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")
- changed = res[0] != self.settings.value("outputWidth")
- 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"""
- self.newTask.emit(self.core.selectedComponents)
- # self.processTask.emit()
- if force or "autosave" in kwargs:
- if force or kwargs["autosave"]:
- self.autosave(True)
- else:
- self.autosave()
- self.updateWindowTitle()
-
- @QtCore.pyqtSlot("QImage")
- def showPreviewImage(self, image):
- self.previewWindow.changePixmap(image)
-
- @disableWhenEncoding
- def showUndoStack(self):
- self.undoDialog.show()
-
- 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,
- )
- command = " ".join(command)
- log.info(f"FFmpeg command: {command}")
- lines = wrap(command, 49)
- 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."""
- action = AddComponent(self, compPos, moduleIndex)
- self.undoStack.push(action)
-
- def insertComponent(self, index):
- """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.setCurrentRow(index)
-
- # connect to signal that adds an asterisk when modified
- self.core.selectedComponents[index].modified.connect(self.updateComponentTitle)
-
- self.pages.insert(index, self.core.selectedComponents[index].page)
- stackedWidget.insertWidget(index, self.pages[index])
- stackedWidget.setCurrentIndex(index)
-
- return index
-
- def removeComponent(self):
- componentList = self.listWidget_componentList
- selected = componentList.selectedItems()
- if selected:
- action = RemoveComponent(self, selected)
- self.undoStack.push(action)
-
- def _removeComponent(self, index):
- stackedWidget = self.stackedWidget
- componentList = self.listWidget_componentList
- stackedWidget.removeWidget(self.pages[index])
- componentList.takeItem(index)
- self.core.removeComponent(index)
- self.pages.pop(index)
- self.changeComponentWidget()
- self.drawPreview()
-
- @disableWhenEncoding
- def moveComponent(self, change):
- """Moves a component relatively from its current position"""
- componentList = self.listWidget_componentList
- tag = change
- if change == "top":
- change = -componentList.currentRow()
- elif change == "bottom":
- change = len(componentList) - componentList.currentRow() - 1
- else:
- tag = "down" if change == 1 else "up"
-
- row = componentList.currentRow()
- newRow = row + change
- if newRow > -1 and newRow < componentList.count():
- action = MoveComponent(self, row, newRow, tag)
- 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]
- 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)
- return mousePos
-
- @disableWhenEncoding
- def dragComponent(self, event):
- """Used as Qt drop event for the component listwidget"""
- componentList = self.listWidget_componentList
- mousePos = self.getComponentListMousePos(event.position())
-
- if mousePos > -1:
- change = (componentList.currentRow() - mousePos) * -1
- else:
- change = componentList.count() - componentList.currentRow() - 1
- self.moveComponent(change)
-
- def changeComponentWidget(self):
- selected = self.listWidget_componentList.selectedItems()
- if selected:
- index = self.listWidget_componentList.row(selected[0])
- self.stackedWidget.setCurrentIndex(index)
-
- def openPresetManager(self):
- """Preset manager for importing, exporting, renaming, deleting"""
- self.presetManager.show_()
-
- def clear(self):
- """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):
- with blockSignals(field):
- field.setText("")
- self.progressBarUpdated(0)
- self.progressBarSetText("")
- self.undoStack.clear()
-
- @disableWhenEncoding
- def createNewProject(self, prompt=True):
- if prompt:
- self.openSaveChangesDialog("starting a new project")
-
- self.clear()
- self.currentProject = None
- self.settings.setValue("currentProject", None)
- self.drawPreview(True)
-
- def saveCurrentProject(self):
- if self.currentProject:
- self.core.createProjectFile(self.currentProject, self)
- try:
- os.remove(self.autosavePath)
- except FileNotFoundError:
- pass
- self.updateWindowTitle()
- else:
- self.openSaveProjectDialog()
-
- def openSaveChangesDialog(self, phrase):
- success = True
- 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,
- )
- if ch:
- success = self.saveProjectChanges()
-
- if success and os.path.exists(self.autosavePath):
- os.remove(self.autosavePath)
-
- def openSaveProjectDialog(self):
- filename, _ = QtWidgets.QFileDialog.getSaveFileName(
- self,
- "Create Project File",
- self.settings.value("projectDir"),
- "Project Files (*.avp)",
- )
- if not filename:
- return
- if not filename.endswith(".avp"):
- filename += ".avp"
- self.settings.setValue("projectDir", os.path.dirname(filename))
- self.settings.setValue("currentProject", filename)
- self.currentProject = filename
- self.core.createProjectFile(filename, self)
- self.updateWindowTitle()
-
- @disableWhenEncoding
- def openOpenProjectDialog(self):
- filename, _ = QtWidgets.QFileDialog.getOpenFileName(
- self,
- "Open Project File",
- self.settings.value("projectDir"),
- "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")
- ):
- return
-
- self.clear()
- # ask to save any changes that are about to get deleted
- if prompt:
- self.openSaveChangesDialog("opening another project")
-
- self.currentProject = filepath
- self.settings.setValue("currentProject", filepath)
- self.settings.setValue("projectDir", os.path.dirname(filepath))
- # actually load the project using core method
- self.core.openProject(self, filepath)
- self.drawPreview(autosave=False)
- self.updateWindowTitle()
-
- def showMessage(self, **kwargs):
- parent = kwargs["parent"] if "parent" in kwargs else self
- msg = QtWidgets.QMessageBox(parent)
- msg.setWindowTitle(appName)
- msg.setModal(True)
- msg.setText(kwargs["msg"])
- msg.setIcon(
- 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.setStandardButtons(
- QtWidgets.QMessageBox.StandardButton.Ok
- | QtWidgets.QMessageBox.StandardButton.Cancel
- )
- else:
- 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"""
- componentList = self.listWidget_componentList
- self.menu = QtWidgets.QMenu()
- parentPosition = componentList.mapToGlobal(QtCore.QPoint(0, 0))
-
- index = self.getComponentListMousePos(QPos)
- if index > -1:
- # Show preset menu if clicking a component
- self.presetManager.findPresets()
- menuItem = self.menu.addAction("Save Preset")
- menuItem.triggered.connect(self.presetManager.openSavePresetDialog)
-
- # submenu for opening presets
- try:
- presets = self.presetManager.presets[
- str(self.core.selectedComponents[index])
- ]
- self.presetSubmenu = QtWidgets.QMenu("Open Preset")
- self.menu.addMenu(self.presetSubmenu)
-
- for version, presetName in presets:
- menuItem = self.presetSubmenu.addAction(presetName)
- menuItem.triggered.connect(
- 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)
- self.menu.addSeparator()
-
- # "Add Component" submenu
- self.submenu = QtWidgets.QMenu("Add")
- self.menu.addMenu(self.submenu)
- insertCompAtTop = self.settings.value("pref_insertCompAtTop")
- for i, comp in enumerate(self.core.modules):
- menuItem = self.submenu.addAction(comp.Component.name)
- menuItem.triggered.connect(
- lambda _, item=i: self.addComponent(
- 0 if insertCompAtTop else index, item
- )
- )
-
- self.menu.move(parentPosition + QPos)
- self.menu.show()
diff --git a/src/gui/mainwindow.ui b/src/gui/mainwindow.ui
deleted file mode 100644
index cd8454d..0000000
--- a/src/gui/mainwindow.ui
+++ /dev/null
@@ -1,835 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>1008</width>
- <height>575</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="windowTitle">
- <string>MainWindow</string>
- </property>
- <widget class="QWidget" name="centralwidget">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="leftMargin">
- <number>9</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::MinimumExpanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>360</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_previewWrapper">
- <property name="sizeConstraint">
- <enum>QLayout::SetDefaultConstraint</enum>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <item>
- <spacer name="horizontalSpacer_previewSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::MinimumExpanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>420</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_16">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
- <item>
- <widget class="QPushButton" name="pushButton_undo">
- <property name="text">
- <string>Undo</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_6">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>140</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_projects">
- <property name="text">
- <string>Projects</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_presets">
- <property name="text">
- <string>Presets</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Minimum</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>2</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QListWidget" name="listWidget_componentList">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="acceptDrops">
- <bool>true</bool>
- </property>
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Sunken</enum>
- </property>
- <property name="lineWidth">
- <number>1</number>
- </property>
- <property name="tabKeyNavigation">
- <bool>true</bool>
- </property>
- <property name="dragEnabled">
- <bool>true</bool>
- </property>
- <property name="dragDropOverwriteMode">
- <bool>false</bool>
- </property>
- <property name="dragDropMode">
- <enum>QAbstractItemView::InternalMove</enum>
- </property>
- <property name="defaultDropAction">
- <enum>Qt::MoveAction</enum>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_14">
- <item>
- <widget class="QPushButton" name="pushButton_addComponent">
- <property name="text">
- <string>Add</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_removeComponent">
- <property name="text">
- <string>Remove</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_listMoveUp">
- <property name="text">
- <string>Up</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_listMoveDown">
- <property name="text">
- <string>Down</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_7">
- <property name="leftMargin">
- <number>4</number>
- </property>
- <property name="rightMargin">
- <number>2</number>
- </property>
- </layout>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <property name="sizeConstraint">
- <enum>QLayout::SetFixedSize</enum>
- </property>
- <property name="topMargin">
- <number>4</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QTabWidget" name="tabWidget">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>500</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>180</height>
- </size>
- </property>
- <property name="tabPosition">
- <enum>QTabWidget::North</enum>
- </property>
- <property name="tabShape">
- <enum>QTabWidget::Rounded</enum>
- </property>
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="tab_exportVideo">
- <attribute name="title">
- <string>Export Video</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_10">
- <property name="margin">
- <number>10</number>
- </property>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_4">
- <property name="topMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QLabel" name="label_audioFile">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>85</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>80</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Audio File</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="lineEdit_audioFile">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>28</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>28</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="toolButton_selectAudioFile">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>28</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>28</height>
- </size>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_11">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_6">
- <item>
- <widget class="QLabel" name="label_outputFile">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>85</width>
- <height>0</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Output File</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="lineEdit_outputFile">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>28</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>28</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="toolButton_selectOutputFile">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>28</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>28</height>
- </size>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <property name="margin">
- <number>0</number>
- </property>
- <item>
- <widget class="QProgressBar" name="progressBar_createVideo">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="value">
- <number>24</number>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Minimum</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>10</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_createVideo">
- <property name="text">
- <string>Create Video</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_Cancel">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="text">
- <string>Cancel</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QLabel" name="progressLabel">
- <property name="text">
- <string/>
- </property>
- <property name="scaledContents">
- <bool>true</bool>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="indent">
- <number>-1</number>
- </property>
- </widget>
- </item>
- </layout>
- <zorder></zorder>
- <zorder></zorder>
- <zorder>progressLabel</zorder>
- </widget>
- <widget class="QWidget" name="tab_encoderSettings">
- <attribute name="title">
- <string>Encoder Settings</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_9">
- <property name="margin">
- <number>10</number>
- </property>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_13">
- <item>
- <widget class="QLabel" name="label_videoFormat">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>85</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Container</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="comboBox_videoContainer">
- <property name="minimumSize">
- <size>
- <width>150</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Minimum</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>5</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="label_videoPreset">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Resolution</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="comboBox_resolution">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_10">
- <item>
- <widget class="QLabel" name="label_videoCodec">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>85</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Video Codec</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="comboBox_videoCodec">
- <property name="minimumSize">
- <size>
- <width>150</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>5</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="label_resolution">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Video Bitrate (Kbps)</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QSpinBox" name="spinBox_vBitrate">
- <property name="maximum">
- <number>99999</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_11">
- <item>
- <widget class="QLabel" name="label_audioCodec">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>85</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Audio Codec</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="comboBox_audioCodec">
- <property name="minimumSize">
- <size>
- <width>150</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>5</width>
- <height>10</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="label_audioBitrate">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Audio Bitrate (Kbps)</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QSpinBox" name="spinBox_aBitrate">
- <property name="maximum">
- <number>9999</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <property name="sizeConstraint">
- <enum>QLayout::SetDefaultConstraint</enum>
- </property>
- <item>
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::MinimumExpanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>500</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QStackedWidget" name="stackedWidget">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>180</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>180</height>
- </size>
- </property>
- <property name="currentIndex">
- <number>-1</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/gui/presetmanager.py b/src/gui/presetmanager.py
deleted file mode 100644
index 980a969..0000000
--- a/src/gui/presetmanager.py
+++ /dev/null
@@ -1,349 +0,0 @@
-"""
-Preset manager object handles all interactions with presets, including
-the context menu accessed from MainWindow.
-"""
-
-from PyQt6 import QtCore, QtWidgets, uic
-import string
-import os
-import logging
-
-from ..toolkit import badName
-from ..core import Core
-from .actions import *
-
-
-log = logging.getLogger("AVP.Gui.PresetManager")
-
-
-class PresetManager(QtWidgets.QDialog):
- def __init__(self, parent):
- super().__init__()
- uic.loadUi(os.path.join(Core.wd, "gui", "presetmanager.ui"), self)
- self.parent = parent
- self.core = parent.core
- self.settings = parent.settings
- self.presetDir = parent.presetDir
- if not self.settings.value("presetDir"):
- self.settings.setValue(
- "presetDir", os.path.join(parent.dataDir, "projects")
- )
-
- self.findPresets()
-
- # window
- self.lastFilter = "*"
- self.presetRows = [] # list of (comp, vers, name) tuples
-
- self.setWindowFlags(QtCore.Qt.WindowType.WindowStaysOnTopHint)
-
- # connect button signals
- self.pushButton_delete.clicked.connect(self.openDeletePresetDialog)
- self.pushButton_rename.clicked.connect(self.openRenamePresetDialog)
- self.pushButton_import.clicked.connect(self.openImportDialog)
- self.pushButton_export.clicked.connect(self.openExportDialog)
- self.pushButton_close.clicked.connect(self.close)
-
- # create filter box and preset list
- self.drawFilterList()
- self.comboBox_filter.currentIndexChanged.connect(
- lambda: self.drawPresetList(
- self.comboBox_filter.currentText(), self.lineEdit_search.text()
- )
- )
-
- # make auto-completion for search bar
- self.autocomplete = QtCore.QStringListModel()
- completer = QtWidgets.QCompleter()
- completer.setModel(self.autocomplete)
- self.lineEdit_search.setCompleter(completer)
- self.lineEdit_search.textChanged.connect(
- lambda: self.drawPresetList(
- self.comboBox_filter.currentText(), self.lineEdit_search.text()
- )
- )
- self.drawPresetList("*")
-
- def show_(self):
- """Open a new preset manager window from the mainwindow"""
- self.findPresets()
- self.drawFilterList()
- self.drawPresetList("*")
- self.show()
-
- def findPresets(self):
- log.debug("Searching %s for presets", self.presetDir)
- parseList = []
- for dirpath, dirnames, filenames in os.walk(self.presetDir):
- # anything without a subdirectory must be a preset folder
- if dirnames:
- continue
- for preset in filenames:
- compName = os.path.basename(os.path.dirname(dirpath))
- if compName not in self.core.compNames:
- continue
- compVers = os.path.basename(dirpath)
- try:
- parseList.append((compName, int(compVers), preset))
- except ValueError:
- continue
- self.presets = {
- compName: [
- (vers, preset) for name, vers, preset in parseList if name == compName
- ]
- for compName, _, __ in parseList
- }
-
- def drawPresetList(self, compFilter=None, presetFilter=""):
- self.listWidget_presets.clear()
- if compFilter:
- self.lastFilter = str(compFilter)
- else:
- compFilter = str(self.lastFilter)
- self.presetRows = []
- presetNames = []
- for component, presets in self.presets.items():
- if compFilter != "*" and component != compFilter:
- continue
- for vers, preset in presets:
- if not presetFilter or presetFilter in preset:
- self.listWidget_presets.addItem("%s: %s" % (component, preset))
- self.presetRows.append((component, vers, preset))
- if preset not in presetNames:
- presetNames.append(preset)
- self.autocomplete.setStringList(presetNames)
-
- def drawFilterList(self):
- self.comboBox_filter.clear()
- self.comboBox_filter.addItem("*")
- for component in self.presets:
- self.comboBox_filter.addItem(component)
-
- def clearPreset(self, compI=None):
- """Functions on mainwindow level from the context menu"""
- compI = self.parent.listWidget_componentList.currentRow()
- action = ClearPreset(self.parent, compI)
- self.parent.undoStack.push(action)
-
- def openSavePresetDialog(self):
- """Functions on mainwindow level from the context menu"""
- selectedComponents = self.core.selectedComponents
- componentList = self.parent.listWidget_componentList
-
- if componentList.currentRow() == -1:
- return
- while True:
- index = componentList.currentRow()
- currentPreset = selectedComponents[index].currentPreset
- newName, OK = QtWidgets.QInputDialog.getText(
- self.parent,
- "Audio Visualizer",
- "New Preset Name:",
- QtWidgets.QLineEdit.EchoMode.Normal,
- currentPreset,
- )
- if OK:
- if badName(newName):
- self.warnMessage(self.parent)
- continue
- if newName:
- if index != -1:
- selectedComponents[index].currentPreset = newName
- saveValueStore = selectedComponents[index].savePreset()
- saveValueStore["preset"] = newName
- componentName = str(selectedComponents[index]).strip()
- vers = selectedComponents[index].version
- self.createNewPreset(
- componentName,
- vers,
- newName,
- saveValueStore,
- window=self.parent,
- )
- self.findPresets()
- self.drawPresetList()
- self.openPreset(newName, index)
- break
-
- def createNewPreset(self, compName, vers, filename, saveValueStore, **kwargs):
- path = os.path.join(self.presetDir, compName, str(vers), filename)
- if self.presetExists(path, **kwargs):
- return
- self.core.createPresetFile(compName, vers, filename, saveValueStore)
-
- def presetExists(self, path, **kwargs):
- if os.path.exists(path):
- window = kwargs.get("window", self)
- ch = self.parent.showMessage(
- msg="%s already exists! Overwrite it?" % os.path.basename(path),
- showCancel=True,
- icon="Warning",
- parent=window,
- )
- if not ch:
- # user clicked cancel
- return True
-
- return False
-
- def openPreset(self, presetName, compPos=None):
- componentList = self.parent.listWidget_componentList
- index = compPos if compPos is not None else componentList.currentRow()
- if index == -1:
- return
- action = OpenPreset(self, presetName, index)
- self.parent.undoStack.push(action)
-
- def _openPreset(self, presetName, index):
- selectedComponents = self.core.selectedComponents
-
- componentName = selectedComponents[index].name.strip()
- version = selectedComponents[index].version
- dirname = os.path.join(self.presetDir, componentName, str(version))
- filepath = os.path.join(dirname, presetName)
- self.core.openPreset(filepath, index, presetName)
-
- self.parent.updateComponentTitle(index)
- self.parent.drawPreview()
-
- def openDeletePresetDialog(self):
- row = self.getPresetRow()
- if row == -1:
- return
- comp, vers, name = self.presetRows[row]
- ch = self.parent.showMessage(
- msg="Really delete %s?" % name,
- showCancel=True,
- icon="Warning",
- parent=self,
- )
- if not ch:
- return
- self.deletePreset(comp, vers, name)
-
- def deletePreset(self, comp, vers, name):
- action = DeletePreset(self, comp, vers, name)
- self.parent.undoStack.push(action)
-
- def warnMessage(self, window=None):
- self.parent.showMessage(
- msg="Preset names must contain only letters, " "numbers, and spaces.",
- parent=window if window else self,
- )
-
- def getPresetRow(self):
- row = self.listWidget_presets.currentRow()
- if row > -1:
- return row
-
- # check if component selected in MainWindow has preset loaded
- componentList = self.parent.listWidget_componentList
- compIndex = componentList.currentRow()
- if compIndex == -1:
- return compIndex
-
- preset = self.core.selectedComponents[compIndex].currentPreset
- if preset is None:
- return -1
- else:
- rowTuple = (
- self.core.selectedComponents[compIndex].name,
- self.core.selectedComponents[compIndex].version,
- preset,
- )
- for i, tup in enumerate(self.presetRows):
- if rowTuple == tup:
- index = i
- break
- else:
- return -1
- return index
-
- def openRenamePresetDialog(self):
- presetList = self.listWidget_presets
- index = self.getPresetRow()
- if index == -1:
- return
-
- while True:
- newName, OK = QtWidgets.QInputDialog.getText(
- self,
- "Preset Manager",
- "Rename Preset:",
- QtWidgets.QLineEdit.EchoMode.Normal,
- self.presetRows[index][2],
- )
- if OK:
- if badName(newName):
- self.warnMessage()
- continue
- if newName:
- comp, vers, oldName = self.presetRows[index]
- path = os.path.join(self.presetDir, comp, str(vers))
- newPath = os.path.join(path, newName)
- if self.presetExists(newPath):
- return
- action = RenamePreset(self, path, oldName, newName)
- self.parent.undoStack.push(action)
- break
-
- def renamePreset(self, path, oldName, newName):
- oldPath = os.path.join(path, oldName)
- newPath = os.path.join(path, newName)
- if os.path.exists(newPath):
- os.remove(newPath)
- os.rename(oldPath, newPath)
- self.findPresets()
- self.drawPresetList()
- path = os.path.dirname(newPath)
- for i, comp in enumerate(self.core.selectedComponents):
- if self.core.getPresetDir(comp) == path and comp.currentPreset == oldName:
- self.core.openPreset(newPath, i, newName)
- self.parent.updateComponentTitle(i, False)
- self.parent.drawPreview()
-
- def openImportDialog(self):
- filename, _ = QtWidgets.QFileDialog.getOpenFileName(
- self,
- "Import Preset File",
- self.settings.value("presetDir"),
- "Preset Files (*.avl)",
- )
- if filename:
- # get installed path & ask user to overwrite if needed
- path = ""
- while True:
- if path:
- if self.presetExists(path):
- break
- else:
- if os.path.exists(path):
- os.remove(path)
- success, path = self.core.importPreset(filename)
- if success:
- break
-
- self.findPresets()
- self.drawPresetList()
- self.settings.setValue("presetDir", os.path.dirname(filename))
-
- def openExportDialog(self):
- index = self.getPresetRow()
- if index == -1:
- return
- filename, _ = QtWidgets.QFileDialog.getSaveFileName(
- self,
- "Export Preset",
- self.settings.value("presetDir"),
- "Preset Files (*.avl)",
- )
- if filename:
- comp, vers, name = self.presetRows[index]
- if not self.core.exportPreset(filename, comp, vers, name):
- self.parent.showMessage(
- msg="Couldn't export %s." % filename, parent=self
- )
- self.settings.setValue("presetDir", os.path.dirname(filename))
-
- def clearPresetListSelection(self):
- self.listWidget_presets.setCurrentRow(-1)
diff --git a/src/gui/presetmanager.ui b/src/gui/presetmanager.ui
deleted file mode 100644
index 5257b1c..0000000
--- a/src/gui/presetmanager.ui
+++ /dev/null
@@ -1,150 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>presetmanager</class>
- <widget class="QWidget" name="presetmanager">
- <property name="windowModality">
- <enum>Qt::NonModal</enum>
- </property>
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>497</width>
- <height>377</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Preset Manager</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="QLineEdit" name="lineEdit_search">
- <property name="text">
- <string/>
- </property>
- <property name="placeholderText">
- <string>Filter by name</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="comboBox_filter">
- <property name="minimumSize">
- <size>
- <width>200</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QListWidget" name="listWidget_presets">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="tabKeyNavigation">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
- <item>
- <widget class="QPushButton" name="pushButton_import">
- <property name="text">
- <string>Import</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_export">
- <property name="text">
- <string>Export</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_rename">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="text">
- <string>Rename</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_delete">
- <property name="text">
- <string>Delete</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_4">
- <item alignment="Qt::AlignRight">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt; font-style:italic;&quot;&gt;Right-click components in the main window to create presets&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton_close">
- <property name="text">
- <string>Close</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/gui/preview_thread.py b/src/gui/preview_thread.py
deleted file mode 100644
index 1d78516..0000000
--- a/src/gui/preview_thread.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""
-Thread that runs to create QImages for MainWindow's preview label.
-Processes a queue of component lists.
-"""
-
-from PyQt6 import QtCore, QtGui, uic
-from PyQt6.QtCore import pyqtSignal, pyqtSlot
-from PIL import Image
-from PIL.ImageQt import ImageQt
-from queue import Queue, Empty
-import os
-import logging
-
-from ..toolkit.frame import Checkerboard
-from ..toolkit import disableWhenOpeningProject
-
-
-log = logging.getLogger("AVP.Gui.PreviewThread")
-
-
-class Worker(QtCore.QObject):
-
- imageCreated = pyqtSignal(QtGui.QImage)
- error = pyqtSignal(str)
-
- def __init__(self, core, settings, queue):
- super().__init__()
- self.core = core
- self.settings = settings
- width = int(self.settings.value("outputWidth"))
- height = int(self.settings.value("outputHeight"))
- self.queue = queue
- self.background = Checkerboard(width, height)
-
- @disableWhenOpeningProject
- @pyqtSlot(list)
- def createPreviewImage(self, components):
- dic = {
- "components": components,
- }
- self.queue.put(dic)
- log.debug("Preview thread id: {}".format(int(QtCore.QThread.currentThreadId())))
-
- @pyqtSlot()
- def process(self):
- try:
- nextPreviewInformation = self.queue.get(block=False)
- while self.queue.qsize() >= 2:
- try:
- self.queue.get(block=False)
- except Empty:
- continue
- width = int(self.settings.value("outputWidth"))
- height = int(self.settings.value("outputHeight"))
- if self.background.width != width or self.background.height != height:
- self.background = Checkerboard(width, height)
-
- frame = self.background.copy()
- log.info("Creating new preview frame")
- components = nextPreviewInformation["components"]
- for component in reversed(components):
- try:
- component.lockSize(width, height)
- newFrame = component.previewRender()
- component.unlockSize()
- frame = Image.alpha_composite(frame, newFrame)
-
- except ValueError as e:
- errMsg = (
- "Bad frame returned by %s's preview renderer. "
- "%s. New frame size was %s*%s; should be %s*%s."
- % (
- str(component),
- str(e).capitalize(),
- newFrame.width,
- newFrame.height,
- width,
- height,
- )
- )
- log.critical(errMsg)
- self.error.emit(errMsg)
- break
- except RuntimeError as e:
- log.error(str(e))
- else:
- # We must store a reference to this QImage
- # or else Qt will garbage-collect it on the C++ side
- self.frame = ImageQt(frame)
- self.imageCreated.emit(QtGui.QImage(self.frame))
-
- except Empty:
- True
diff --git a/src/gui/preview_win.py b/src/gui/preview_win.py
deleted file mode 100644
index f52f8a3..0000000
--- a/src/gui/preview_win.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from PyQt6 import QtCore, QtGui, QtWidgets
-import logging
-
-log = logging.getLogger("AVP.Gui.PreviewWindow")
-
-
-class PreviewWindow(QtWidgets.QLabel):
- """
- Paints the preview QLabel in MainWindow and maintains the aspect ratio
- when the window is resized.
- """
-
- def __init__(self, parent, img):
- super().__init__()
- self.parent = parent
- # FIXME
- # self.setFrameStyle(QtWidgets.QFrame.StyledPanel)
- self.pixmap = QtGui.QPixmap(img)
-
- def paintEvent(self, event):
- size = self.size()
- painter = QtGui.QPainter(self)
- point = QtCore.QPoint(0, 0)
- scaledPix = self.pixmap.scaled(
- size,
- QtCore.Qt.AspectRatioMode.KeepAspectRatio,
- transformMode=QtCore.Qt.TransformationMode.SmoothTransformation,
- )
-
- # start painting the label from left upper corner
- point.setX(int((size.width() - scaledPix.width()) / 2))
- point.setY(int((size.height() - scaledPix.height()) / 2))
- painter.drawPixmap(point, scaledPix)
-
- def changePixmap(self, img):
- self.pixmap = QtGui.QPixmap(img)
- self.repaint()
-
- def mousePressEvent(self, event):
- if self.parent.encoding:
- return
-
- i = self.parent.listWidget_componentList.currentRow()
- if i >= 0:
- component = self.parent.core.selectedComponents[i]
- if not hasattr(component, "previewClickEvent"):
- return
- qpoint = event.position().toPoint()
- pos = (qpoint.x(), qpoint.y())
- size = (self.width(), self.height())
- butt = event.button()
- log.info("Click event for #%s: %s button %s" % (i, pos, butt))
- component.previewClickEvent(pos, size, butt)
-
- @QtCore.pyqtSlot(str)
- def threadError(self, msg):
- self.parent.showMessage(msg=msg, icon="Critical", parent=self)
- log.info("%", repr(self.parent))