diff --git a/src/component.py b/src/component.py index b883627..f0a8c6b 100644 --- a/src/component.py +++ b/src/component.py @@ -99,6 +99,23 @@ class ComponentMetaclass(type(QtCore.QObject)): return func(self) return errorWrapper + def presetWrapper(func): + '''Wraps loadPreset to handle the self.openingPreset boolean''' + class openingPreset: + def __init__(self, comp): + self.comp = comp + + def __enter__(self): + self.comp.openingPreset = True + + def __exit__(self, *args): + self.comp.openingPreset = False + + def presetWrapper(self, *args): + with openingPreset(self): + return func(self, *args) + return presetWrapper + def __new__(cls, name, parents, attrs): if 'ui' not in attrs: # Use module name as ui filename by default @@ -111,7 +128,7 @@ class ComponentMetaclass(type(QtCore.QObject)): 'names', # Class methods 'error', 'audio', 'properties', # Properties 'preFrameRender', 'previewRender', - 'frameRender', 'command', + 'frameRender', 'command', 'loadPreset' ) # Auto-decorate methods @@ -140,6 +157,9 @@ class ComponentMetaclass(type(QtCore.QObject)): if key == 'error': attrs[key] = cls.errorWrapper(attrs[key]) + if key == 'loadPreset': + attrs[key] = cls.presetWrapper(attrs[key]) + # Turn version string into a number try: if 'version' not in attrs: @@ -180,6 +200,7 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): self.compPos = compPos self.core = core self.currentPreset = None + self.openingPreset = False self._trackedWidgets = {} self._presetNames = {} @@ -207,7 +228,10 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): preset = self.savePreset() except Exception as e: preset = '%s occurred while saving preset' % str(e) - return '%s\n%s\n%s' % ( + + return 'Component(%s, %s, Core)\n' \ + 'Name: %s v%s\n Preset: %s' % ( + self.moduleIndex, self.compPos, self.__class__.name, str(self.__class__.version), preset ) @@ -308,6 +332,9 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): A component update triggered by the user changing a widget value Call super() at the END when subclassing this. ''' + if self.openingPreset or not hasattr(self.parent, 'undoStack'): + return self._update() + oldWidgetVals = { attr: getattr(self, attr) for attr in self._trackedWidgets @@ -328,7 +355,7 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): self.parent.undoStack.push(action) def _update(self): - '''An internal component update that is not undoable''' + '''A component update that is not undoable''' newWidgetVals = { attr: getWidgetValue(widget) @@ -684,7 +711,7 @@ class ComponentUpdate(QtWidgets.QUndoCommand): self.id_ = -1 if len(self.modifiedVals) == 1: attr, val = self.modifiedVals.popitem() - self.id_ = sum([ord(letter) for letter in attr[:14]]) + self.id_ = sum([ord(letter) for letter in attr[-14:]]) self.modifiedVals[attr] = val else: log.warning( diff --git a/src/gui/actions.py b/src/gui/actions.py index 5a0869d..cdd3dfa 100644 --- a/src/gui/actions.py +++ b/src/gui/actions.py @@ -4,6 +4,23 @@ from PyQt5.QtWidgets import QUndoCommand +class AddComponent(QUndoCommand): + def __init__(self, parent, compI, moduleI): + super().__init__( + "New %s component" % + parent.core.modules[moduleI].Component.name + ) + self.parent = parent + self.moduleI = moduleI + self.compI = compI + + def redo(self): + self.parent.core.insertComponent(self.compI, self.moduleI, self.parent) + + def undo(self): + self.parent._removeComponent(self.compI) + + class RemoveComponent(QUndoCommand): def __init__(self, parent, selectedRows): super().__init__('Remove component') @@ -17,15 +34,7 @@ class RemoveComponent(QUndoCommand): ] def redo(self): - stackedWidget = self.parent.window.stackedWidget - componentList = self.parent.window.listWidget_componentList - for index in self.selectedRows: - stackedWidget.removeWidget(self.parent.pages[index]) - componentList.takeItem(index) - self.parent.core.removeComponent(index) - self.parent.pages.pop(index) - self.parent.changeComponentWidget() - self.parent.drawPreview() + self.parent._removeComponent(self.selectedRows[0]) def undo(self): componentList = self.parent.window.listWidget_componentList @@ -74,3 +83,21 @@ class MoveComponent(QUndoCommand): def undo(self): self.do(self.newRow, self.row) + + +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) diff --git a/src/gui/mainwindow.py b/src/gui/mainwindow.py index 26464a9..8000b3b 100644 --- a/src/gui/mainwindow.py +++ b/src/gui/mainwindow.py @@ -20,7 +20,9 @@ import gui.preview_thread as preview_thread from gui.preview_win import PreviewWindow from gui.presetmanager import PresetManager from gui.actions import * -from toolkit import disableWhenEncoding, disableWhenOpeningProject, checkOutput +from toolkit import ( + disableWhenEncoding, disableWhenOpeningProject, checkOutput, blockSignals +) log = logging.getLogger('AVP.MainWindow') @@ -165,7 +167,7 @@ class MainWindow(QtWidgets.QMainWindow): for i, comp in enumerate(self.core.modules): action = self.compMenu.addAction(comp.Component.name) action.triggered.connect( - lambda _, item=i: self.core.insertComponent(0, item, self) + lambda _, item=i: self.addComponent(0, item) ) self.window.pushButton_addComponent.setMenu(self.compMenu) @@ -686,7 +688,13 @@ class MainWindow(QtWidgets.QMainWindow): msg="Current FFmpeg command:\n\n %s" % " ".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.''' componentList = self.window.listWidget_componentList stackedWidget = self.window.stackedWidget @@ -712,6 +720,16 @@ class MainWindow(QtWidgets.QMainWindow): action = RemoveComponent(self, selected) self.undoStack.push(action) + def _removeComponent(self, index): + stackedWidget = self.window.stackedWidget + componentList = self.window.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''' @@ -786,9 +804,8 @@ class MainWindow(QtWidgets.QMainWindow): self.window.lineEdit_audioFile, self.window.lineEdit_outputFile ): - field.blockSignals(True) - field.setText('') - field.blockSignals(False) + with blockSignals(field): + field.setText('') self.progressBarUpdated(0) self.progressBarSetText('') self.undoStack.clear() @@ -938,8 +955,8 @@ class MainWindow(QtWidgets.QMainWindow): for i, comp in enumerate(self.core.modules): menuItem = self.submenu.addAction(comp.Component.name) menuItem.triggered.connect( - lambda _, item=i: self.core.insertComponent( - 0 if insertCompAtTop else index, item, self + lambda _, item=i: self.addComponent( + 0 if insertCompAtTop else index, item ) ) diff --git a/src/gui/presetmanager.py b/src/gui/presetmanager.py index 1cc0887..79ec539 100644 --- a/src/gui/presetmanager.py +++ b/src/gui/presetmanager.py @@ -8,6 +8,7 @@ import os from toolkit import badName from core import Core +from gui.actions import * class PresetManager(QtWidgets.QDialog): @@ -130,8 +131,8 @@ class PresetManager(QtWidgets.QDialog): def clearPreset(self, compI=None): '''Functions on mainwindow level from the context menu''' compI = self.parent.window.listWidget_componentList.currentRow() - self.core.clearPreset(compI) - self.parent.updateComponentTitle(compI, False) + action = ClearPreset(self.parent, compI) + self.parent.undoStack.push(action) def openSavePresetDialog(self): '''Functions on mainwindow level from the context menu'''