fixed issues with undoing relative widgets
This commit is contained in:
parent
87e762a8aa
commit
c07f2426ce
184
src/component.py
184
src/component.py
|
@ -9,6 +9,7 @@ import sys
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
from toolkit.frame import BlankFrame
|
from toolkit.frame import BlankFrame
|
||||||
from toolkit import (
|
from toolkit import (
|
||||||
|
@ -113,14 +114,20 @@ class ComponentMetaclass(type(QtCore.QObject)):
|
||||||
|
|
||||||
def presetWrapper(self, *args):
|
def presetWrapper(self, *args):
|
||||||
with openingPreset(self):
|
with openingPreset(self):
|
||||||
|
try:
|
||||||
return func(self, *args)
|
return func(self, *args)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
raise ComponentError(self, 'preset loader')
|
||||||
|
except ComponentError:
|
||||||
|
return
|
||||||
return presetWrapper
|
return presetWrapper
|
||||||
|
|
||||||
def updateWrapper(func):
|
def updateWrapper(func):
|
||||||
'''
|
'''
|
||||||
For undoable updates triggered by the user,
|
Calls _preUpdate before every subclass update().
|
||||||
call _userUpdate() after the subclass's update() method.
|
Afterwards, for non-user updates, calls _autoUpdate().
|
||||||
For non-user updates, call _autoUpdate()
|
For undoable updates triggered by the user, calls _userUpdate()
|
||||||
'''
|
'''
|
||||||
class wrap:
|
class wrap:
|
||||||
def __init__(self, comp, auto):
|
def __init__(self, comp, auto):
|
||||||
|
@ -128,24 +135,57 @@ class ComponentMetaclass(type(QtCore.QObject)):
|
||||||
self.auto = auto
|
self.auto = auto
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
pass
|
self.comp._preUpdate()
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
if self.auto or self.comp.openingPreset \
|
if self.auto or self.comp.openingPreset \
|
||||||
or not hasattr(self.comp.parent, 'undoStack'):
|
or not hasattr(self.comp.parent, 'undoStack'):
|
||||||
|
log.verbose('Automatic update')
|
||||||
self.comp._autoUpdate()
|
self.comp._autoUpdate()
|
||||||
else:
|
else:
|
||||||
|
log.verbose('User update')
|
||||||
self.comp._userUpdate()
|
self.comp._userUpdate()
|
||||||
|
|
||||||
def updateWrapper(self, **kwargs):
|
def updateWrapper(self, **kwargs):
|
||||||
auto = False
|
auto = kwargs['auto'] if 'auto' in kwargs else False
|
||||||
if 'auto' in kwargs:
|
|
||||||
auto = kwargs['auto']
|
|
||||||
|
|
||||||
with wrap(self, auto):
|
with wrap(self, auto):
|
||||||
|
try:
|
||||||
return func(self)
|
return func(self)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
raise ComponentError(self, 'update method')
|
||||||
|
except ComponentError:
|
||||||
|
return
|
||||||
return updateWrapper
|
return updateWrapper
|
||||||
|
|
||||||
|
def widgetWrapper(func):
|
||||||
|
'''Connects all widgets to update method after the subclass's method'''
|
||||||
|
class wrap:
|
||||||
|
def __init__(self, comp):
|
||||||
|
self.comp = comp
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
for widgetList in self.comp._allWidgets.values():
|
||||||
|
for widget in widgetList:
|
||||||
|
log.verbose('Connecting %s' % str(
|
||||||
|
widget.__class__.__name__))
|
||||||
|
connectWidget(widget, self.comp.update)
|
||||||
|
|
||||||
|
def widgetWrapper(self, *args, **kwargs):
|
||||||
|
auto = kwargs['auto'] if 'auto' in kwargs else False
|
||||||
|
with wrap(self):
|
||||||
|
try:
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
raise ComponentError(self, 'widget creation')
|
||||||
|
except ComponentError:
|
||||||
|
return
|
||||||
|
return widgetWrapper
|
||||||
|
|
||||||
def __new__(cls, name, parents, attrs):
|
def __new__(cls, name, parents, attrs):
|
||||||
if 'ui' not in attrs:
|
if 'ui' not in attrs:
|
||||||
# Use module name as ui filename by default
|
# Use module name as ui filename by default
|
||||||
|
@ -153,13 +193,12 @@ class ComponentMetaclass(type(QtCore.QObject)):
|
||||||
attrs['__module__'].split('.')[-1]
|
attrs['__module__'].split('.')[-1]
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
# if parents[0] == QtCore.QObject: else:
|
|
||||||
decorate = (
|
decorate = (
|
||||||
'names', # Class methods
|
'names', # Class methods
|
||||||
'error', 'audio', 'properties', # Properties
|
'error', 'audio', 'properties', # Properties
|
||||||
'preFrameRender', 'previewRender',
|
'preFrameRender', 'previewRender',
|
||||||
'frameRender', 'command',
|
'frameRender', 'command',
|
||||||
'loadPreset', 'update'
|
'loadPreset', 'update', 'widget',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Auto-decorate methods
|
# Auto-decorate methods
|
||||||
|
@ -184,6 +223,8 @@ class ComponentMetaclass(type(QtCore.QObject)):
|
||||||
attrs[key] = cls.loadPresetWrapper(attrs[key])
|
attrs[key] = cls.loadPresetWrapper(attrs[key])
|
||||||
elif key == 'update':
|
elif key == 'update':
|
||||||
attrs[key] = cls.updateWrapper(attrs[key])
|
attrs[key] = cls.updateWrapper(attrs[key])
|
||||||
|
elif key == 'widget' and parents[0] != QtCore.QObject:
|
||||||
|
attrs[key] = cls.widgetWrapper(attrs[key])
|
||||||
|
|
||||||
# Turn version string into a number
|
# Turn version string into a number
|
||||||
try:
|
try:
|
||||||
|
@ -224,23 +265,28 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
self.moduleIndex = moduleIndex
|
self.moduleIndex = moduleIndex
|
||||||
self.compPos = compPos
|
self.compPos = compPos
|
||||||
self.core = core
|
self.core = core
|
||||||
self.currentPreset = None
|
|
||||||
self.openingPreset = False
|
|
||||||
|
|
||||||
|
# STATUS VARIABLES
|
||||||
|
self.currentPreset = None
|
||||||
|
self._allWidgets = {}
|
||||||
self._trackedWidgets = {}
|
self._trackedWidgets = {}
|
||||||
self._presetNames = {}
|
self._presetNames = {}
|
||||||
self._commandArgs = {}
|
self._commandArgs = {}
|
||||||
self._colorWidgets = {}
|
self._colorWidgets = {}
|
||||||
self._colorFuncs = {}
|
self._colorFuncs = {}
|
||||||
self._relativeWidgets = {}
|
self._relativeWidgets = {}
|
||||||
# pixel values stored as floats
|
# Pixel values stored as floats
|
||||||
self._relativeValues = {}
|
self._relativeValues = {}
|
||||||
# maximum values of spinBoxes at 1080p (Core.resolutions[0])
|
# Maximum values of spinBoxes at 1080p (Core.resolutions[0])
|
||||||
self._relativeMaximums = {}
|
self._relativeMaximums = {}
|
||||||
|
|
||||||
|
# LOCKING VARIABLES
|
||||||
|
self.openingPreset = False
|
||||||
self._lockedProperties = None
|
self._lockedProperties = None
|
||||||
self._lockedError = None
|
self._lockedError = None
|
||||||
self._lockedSize = None
|
self._lockedSize = None
|
||||||
|
# If set to a dict, values are used as basis to update relative widgets
|
||||||
|
self.oldAttrs = None
|
||||||
|
|
||||||
# Stop lengthy processes in response to this variable
|
# Stop lengthy processes in response to this variable
|
||||||
self.canceled = False
|
self.canceled = False
|
||||||
|
@ -338,21 +384,21 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
'''
|
'''
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.settings = parent.settings
|
self.settings = parent.settings
|
||||||
|
log.verbose('Creating UI for %s #%s\'s widget' % (
|
||||||
|
self.name, self.compPos
|
||||||
|
))
|
||||||
self.page = self.loadUi(self.__class__.ui)
|
self.page = self.loadUi(self.__class__.ui)
|
||||||
|
|
||||||
# Connect widget signals
|
# Find all normal widgets which will be connected after subclass method
|
||||||
widgets = {
|
self._allWidgets = {
|
||||||
'lineEdit': self.page.findChildren(QtWidgets.QLineEdit),
|
'lineEdit': self.page.findChildren(QtWidgets.QLineEdit),
|
||||||
'checkBox': self.page.findChildren(QtWidgets.QCheckBox),
|
'checkBox': self.page.findChildren(QtWidgets.QCheckBox),
|
||||||
'spinBox': self.page.findChildren(QtWidgets.QSpinBox),
|
'spinBox': self.page.findChildren(QtWidgets.QSpinBox),
|
||||||
'comboBox': self.page.findChildren(QtWidgets.QComboBox),
|
'comboBox': self.page.findChildren(QtWidgets.QComboBox),
|
||||||
}
|
}
|
||||||
widgets['spinBox'].extend(
|
self._allWidgets['spinBox'].extend(
|
||||||
self.page.findChildren(QtWidgets.QDoubleSpinBox)
|
self.page.findChildren(QtWidgets.QDoubleSpinBox)
|
||||||
)
|
)
|
||||||
for widgetList in widgets.values():
|
|
||||||
for widget in widgetList:
|
|
||||||
connectWidget(widget, self.update)
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
'''
|
'''
|
||||||
|
@ -427,10 +473,15 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~
|
# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~
|
||||||
# "Private" Methods
|
# "Private" Methods
|
||||||
# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~
|
# =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~
|
||||||
|
def _preUpdate(self):
|
||||||
|
'''Happens before subclass update()'''
|
||||||
|
for attr in self._relativeWidgets:
|
||||||
|
self.updateRelativeWidget(attr)
|
||||||
|
|
||||||
def _userUpdate(self):
|
def _userUpdate(self):
|
||||||
'''An undoable component update triggered by the user'''
|
'''Happens after subclass update() for an undoable update by user.'''
|
||||||
oldWidgetVals = {
|
oldWidgetVals = {
|
||||||
attr: getattr(self, attr)
|
attr: copy(getattr(self, attr))
|
||||||
for attr in self._trackedWidgets
|
for attr in self._trackedWidgets
|
||||||
}
|
}
|
||||||
newWidgetVals = {
|
newWidgetVals = {
|
||||||
|
@ -443,13 +494,12 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
for attr, val in newWidgetVals.items()
|
for attr, val in newWidgetVals.items()
|
||||||
if val != oldWidgetVals[attr]
|
if val != oldWidgetVals[attr]
|
||||||
}
|
}
|
||||||
|
|
||||||
if modifiedWidgets:
|
if modifiedWidgets:
|
||||||
action = ComponentUpdate(self, oldWidgetVals, modifiedWidgets)
|
action = ComponentUpdate(self, oldWidgetVals, modifiedWidgets)
|
||||||
self.parent.undoStack.push(action)
|
self.parent.undoStack.push(action)
|
||||||
|
|
||||||
def _autoUpdate(self):
|
def _autoUpdate(self):
|
||||||
'''An internal component update that is not undoable'''
|
'''Happens after subclass update() for an internal component update.'''
|
||||||
newWidgetVals = {
|
newWidgetVals = {
|
||||||
attr: getWidgetValue(widget)
|
attr: getWidgetValue(widget)
|
||||||
for attr, widget in self._trackedWidgets.items()
|
for attr, widget in self._trackedWidgets.items()
|
||||||
|
@ -459,12 +509,12 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
|
|
||||||
def setAttrs(self, attrDict):
|
def setAttrs(self, attrDict):
|
||||||
'''
|
'''
|
||||||
Sets attrs (linked to trackedWidgets) in this preset to
|
Sets attrs (linked to trackedWidgets) in this component to
|
||||||
the values in the attrDict. Mutates certain widget values if needed
|
the values in the attrDict. Mutates certain widget values if needed
|
||||||
'''
|
'''
|
||||||
for attr, val in attrDict.items():
|
for attr, val in attrDict.items():
|
||||||
if attr in self._colorWidgets:
|
if attr in self._colorWidgets:
|
||||||
# Color Widgets: text stored as tuple & update the button color
|
# Color Widgets must have a tuple & have a button to update
|
||||||
if type(val) is tuple:
|
if type(val) is tuple:
|
||||||
rgbTuple = val
|
rgbTuple = val
|
||||||
else:
|
else:
|
||||||
|
@ -475,15 +525,25 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
self._colorWidgets[attr].setStyleSheet(btnStyle)
|
self._colorWidgets[attr].setStyleSheet(btnStyle)
|
||||||
setattr(self, attr, rgbTuple)
|
setattr(self, attr, rgbTuple)
|
||||||
|
|
||||||
elif attr in self._relativeWidgets:
|
|
||||||
# Relative widgets: number scales to fit export resolution
|
|
||||||
self.updateRelativeWidget(attr)
|
|
||||||
setattr(self, attr, val)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Normal tracked widget
|
# Normal tracked widget
|
||||||
setattr(self, attr, val)
|
setattr(self, attr, val)
|
||||||
|
|
||||||
|
def setWidgetValues(self, attrDict):
|
||||||
|
'''
|
||||||
|
Sets widgets defined by keys in trackedWidgets in this preset to
|
||||||
|
the values in the attrDict.
|
||||||
|
'''
|
||||||
|
affectedWidgets = [
|
||||||
|
self._trackedWidgets[attr] for attr in attrDict
|
||||||
|
]
|
||||||
|
with blockSignals(affectedWidgets):
|
||||||
|
for attr, val in attrDict.items():
|
||||||
|
widget = self._trackedWidgets[attr]
|
||||||
|
if attr in self._colorWidgets:
|
||||||
|
val = '%s,%s,%s' % val
|
||||||
|
setWidgetValue(widget, val)
|
||||||
|
|
||||||
def _sendUpdateSignal(self):
|
def _sendUpdateSignal(self):
|
||||||
if not self.core.openingProject:
|
if not self.core.openingProject:
|
||||||
self.parent.drawPreview()
|
self.parent.drawPreview()
|
||||||
|
@ -499,6 +559,8 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
Optional args:
|
Optional args:
|
||||||
'presetNames': preset variable names to replace attr names
|
'presetNames': preset variable names to replace attr names
|
||||||
'commandArgs': arg keywords that differ from attr names
|
'commandArgs': arg keywords that differ from attr names
|
||||||
|
'colorWidgets': identify attr as RGB tuple & update button CSS
|
||||||
|
'relativeWidgets': change value proportionally to resolution
|
||||||
|
|
||||||
NOTE: Any kwarg key set to None will selectively disable tracking.
|
NOTE: Any kwarg key set to None will selectively disable tracking.
|
||||||
'''
|
'''
|
||||||
|
@ -542,6 +604,8 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
self._relativeMaximums[attr] = \
|
self._relativeMaximums[attr] = \
|
||||||
self._trackedWidgets[attr].maximum()
|
self._trackedWidgets[attr].maximum()
|
||||||
self.updateRelativeWidgetMaximum(attr)
|
self.updateRelativeWidgetMaximum(attr)
|
||||||
|
self._preUpdate()
|
||||||
|
self._autoUpdate()
|
||||||
|
|
||||||
def pickColor(self, textWidget, button):
|
def pickColor(self, textWidget, button):
|
||||||
'''Use color picker to get color input from the user.'''
|
'''Use color picker to get color input from the user.'''
|
||||||
|
@ -627,12 +691,28 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
def setRelativeWidget(self, attr, floatVal):
|
def setRelativeWidget(self, attr, floatVal):
|
||||||
'''Set a relative widget using a float'''
|
'''Set a relative widget using a float'''
|
||||||
pixelVal = self.pixelValForAttr(attr, floatVal)
|
pixelVal = self.pixelValForAttr(attr, floatVal)
|
||||||
|
with blockSignals(self._allWidgets):
|
||||||
self._trackedWidgets[attr].setValue(pixelVal)
|
self._trackedWidgets[attr].setValue(pixelVal)
|
||||||
|
self.update(auto=True)
|
||||||
|
|
||||||
|
def getOldAttr(self, attr):
|
||||||
|
'''
|
||||||
|
Returns previous state of this attr. Used to determine whether
|
||||||
|
a relative widget must be updated. Required because undoing/redoing
|
||||||
|
can make determining the 'previous' value tricky.
|
||||||
|
'''
|
||||||
|
if self.oldAttrs is not None:
|
||||||
|
log.verbose('Using nonstandard oldAttr for %s' % attr)
|
||||||
|
return self.oldAttrs[attr]
|
||||||
|
else:
|
||||||
|
return getattr(self, attr)
|
||||||
|
|
||||||
def updateRelativeWidget(self, attr):
|
def updateRelativeWidget(self, attr):
|
||||||
|
'''Called by _preUpdate() for each relativeWidget before each update'''
|
||||||
try:
|
try:
|
||||||
oldUserValue = getattr(self, attr)
|
oldUserValue = self.getOldAttr(attr)
|
||||||
except AttributeError:
|
except (AttributeError, KeyError):
|
||||||
|
log.info('Using visible values as basis for relative widgets')
|
||||||
oldUserValue = self._trackedWidgets[attr].value()
|
oldUserValue = self._trackedWidgets[attr].value()
|
||||||
newUserValue = self._trackedWidgets[attr].value()
|
newUserValue = self._trackedWidgets[attr].value()
|
||||||
newRelativeVal = self.floatValForAttr(attr, newUserValue)
|
newRelativeVal = self.floatValForAttr(attr, newUserValue)
|
||||||
|
@ -645,11 +725,10 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
|
||||||
# means the pixel value needs to be updated
|
# means the pixel value needs to be updated
|
||||||
log.debug('Updating %s #%s\'s relative widget: %s' % (
|
log.debug('Updating %s #%s\'s relative widget: %s' % (
|
||||||
self.name, self.compPos, attr))
|
self.name, self.compPos, attr))
|
||||||
self._trackedWidgets[attr].blockSignals(True)
|
with blockSignals(self._trackedWidgets[attr]):
|
||||||
self.updateRelativeWidgetMaximum(attr)
|
self.updateRelativeWidgetMaximum(attr)
|
||||||
pixelVal = self.pixelValForAttr(attr, oldRelativeVal)
|
pixelVal = self.pixelValForAttr(attr, oldRelativeVal)
|
||||||
self._trackedWidgets[attr].setValue(pixelVal)
|
self._trackedWidgets[attr].setValue(pixelVal)
|
||||||
self._trackedWidgets[attr].blockSignals(False)
|
|
||||||
|
|
||||||
if attr not in self._relativeValues \
|
if attr not in self._relativeValues \
|
||||||
or oldUserValue != newUserValue:
|
or oldUserValue != newUserValue:
|
||||||
|
@ -725,14 +804,22 @@ class ComponentUpdate(QtWidgets.QUndoCommand):
|
||||||
parent.name, parent.compPos
|
parent.name, parent.compPos
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.undone = False
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.oldWidgetVals = {
|
self.oldWidgetVals = {
|
||||||
attr: val
|
attr: copy(val)
|
||||||
for attr, val in oldWidgetVals.items()
|
for attr, val in oldWidgetVals.items()
|
||||||
if attr in modifiedVals
|
if attr in modifiedVals
|
||||||
}
|
}
|
||||||
self.modifiedVals = modifiedVals
|
self.modifiedVals = modifiedVals
|
||||||
|
|
||||||
|
# Because relative widgets change themselves every update based on
|
||||||
|
# their previous value, we must store ALL their values in case of undo
|
||||||
|
self.redoRelativeWidgetVals = {
|
||||||
|
attr: copy(getattr(self.parent, attr))
|
||||||
|
for attr in self.parent._relativeWidgets
|
||||||
|
}
|
||||||
|
|
||||||
# Determine if this update is mergeable
|
# Determine if this update is mergeable
|
||||||
self.id_ = -1
|
self.id_ = -1
|
||||||
if len(self.modifiedVals) == 1:
|
if len(self.modifiedVals) == 1:
|
||||||
|
@ -755,15 +842,26 @@ class ComponentUpdate(QtWidgets.QUndoCommand):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
|
if self.undone:
|
||||||
|
log.debug('Redoing component update')
|
||||||
|
self.parent.setWidgetValues(self.modifiedVals)
|
||||||
self.parent.setAttrs(self.modifiedVals)
|
self.parent.setAttrs(self.modifiedVals)
|
||||||
|
if self.undone:
|
||||||
|
self.parent.oldAttrs = self.redoRelativeWidgetVals
|
||||||
|
self.parent.update(auto=True)
|
||||||
|
self.parent.oldAttrs = None
|
||||||
|
else:
|
||||||
|
self.undoRelativeWidgetVals = {
|
||||||
|
attr: copy(getattr(self.parent, attr))
|
||||||
|
for attr in self.parent._relativeWidgets
|
||||||
|
}
|
||||||
self.parent._sendUpdateSignal()
|
self.parent._sendUpdateSignal()
|
||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
|
log.debug('Undoing component update')
|
||||||
|
self.undone = True
|
||||||
|
self.parent.oldAttrs = self.undoRelativeWidgetVals
|
||||||
|
self.parent.setWidgetValues(self.oldWidgetVals)
|
||||||
self.parent.setAttrs(self.oldWidgetVals)
|
self.parent.setAttrs(self.oldWidgetVals)
|
||||||
with blockSignals(self.parent):
|
self.parent.update(auto=True)
|
||||||
for attr, val in self.oldWidgetVals.items():
|
self.parent.oldAttrs = None
|
||||||
widget = self.parent._trackedWidgets[attr]
|
|
||||||
if attr in self.parent._colorWidgets:
|
|
||||||
val = '%s,%s,%s' % val
|
|
||||||
setWidgetValue(widget, val)
|
|
||||||
self.parent._sendUpdateSignal()
|
|
||||||
|
|
|
@ -82,8 +82,6 @@ class Component(Component):
|
||||||
self.page.pushButton_color2.setEnabled(False)
|
self.page.pushButton_color2.setEnabled(False)
|
||||||
self.page.fillWidget.setCurrentIndex(fillType)
|
self.page.fillWidget.setCurrentIndex(fillType)
|
||||||
|
|
||||||
super().update()
|
|
||||||
|
|
||||||
def previewRender(self):
|
def previewRender(self):
|
||||||
return self.drawFrame(self.width, self.height)
|
return self.drawFrame(self.width, self.height)
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,6 @@ class Component(Component):
|
||||||
if filename:
|
if filename:
|
||||||
self.settings.setValue("componentDir", os.path.dirname(filename))
|
self.settings.setValue("componentDir", os.path.dirname(filename))
|
||||||
self.page.lineEdit_image.setText(filename)
|
self.page.lineEdit_image.setText(filename)
|
||||||
self.update()
|
|
||||||
|
|
||||||
def command(self, arg):
|
def command(self, arg):
|
||||||
if '=' in arg:
|
if '=' in arg:
|
||||||
|
@ -123,4 +122,3 @@ class Component(Component):
|
||||||
else:
|
else:
|
||||||
scaleBox.setVisible(True)
|
scaleBox.setVisible(True)
|
||||||
stretchScaleBox.setVisible(False)
|
stretchScaleBox.setVisible(False)
|
||||||
super().update()
|
|
||||||
|
|
|
@ -53,7 +53,6 @@ class Component(Component):
|
||||||
if filename:
|
if filename:
|
||||||
self.settings.setValue("componentDir", os.path.dirname(filename))
|
self.settings.setValue("componentDir", os.path.dirname(filename))
|
||||||
self.page.lineEdit_image.setText(filename)
|
self.page.lineEdit_image.setText(filename)
|
||||||
self.update()
|
|
||||||
|
|
||||||
def shiftGrid(self, d):
|
def shiftGrid(self, d):
|
||||||
def newGrid(Xchange, Ychange):
|
def newGrid(Xchange, Ychange):
|
||||||
|
|
|
@ -53,7 +53,6 @@ class Component(Component):
|
||||||
if filename:
|
if filename:
|
||||||
self.settings.setValue("componentDir", os.path.dirname(filename))
|
self.settings.setValue("componentDir", os.path.dirname(filename))
|
||||||
self.page.lineEdit_sound.setText(filename)
|
self.page.lineEdit_sound.setText(filename)
|
||||||
self.update()
|
|
||||||
|
|
||||||
def commandHelp(self):
|
def commandHelp(self):
|
||||||
print('Path to audio file:\n path=/filepath/to/sound.ogg')
|
print('Path to audio file:\n path=/filepath/to/sound.ogg')
|
||||||
|
|
|
@ -76,8 +76,6 @@ class Component(Component):
|
||||||
else:
|
else:
|
||||||
self.page.checkBox_mono.setEnabled(True)
|
self.page.checkBox_mono.setEnabled(True)
|
||||||
|
|
||||||
super().update()
|
|
||||||
|
|
||||||
def previewRender(self):
|
def previewRender(self):
|
||||||
changedSize = self.updateChunksize()
|
changedSize = self.updateChunksize()
|
||||||
if not changedSize \
|
if not changedSize \
|
||||||
|
@ -138,7 +136,7 @@ class Component(Component):
|
||||||
'-r', self.settings.value("outputFrameRate"),
|
'-r', self.settings.value("outputFrameRate"),
|
||||||
'-ss', "{0:.3f}".format(startPt),
|
'-ss', "{0:.3f}".format(startPt),
|
||||||
'-i',
|
'-i',
|
||||||
os.path.join(self.core.wd, 'background.png')
|
self.core.junkStream
|
||||||
if genericPreview else inputFile,
|
if genericPreview else inputFile,
|
||||||
'-f', 'image2pipe',
|
'-f', 'image2pipe',
|
||||||
'-pix_fmt', 'rgba',
|
'-pix_fmt', 'rgba',
|
||||||
|
|
|
@ -68,7 +68,6 @@ class Component(Component):
|
||||||
self.page.spinBox_shadY.setHidden(True)
|
self.page.spinBox_shadY.setHidden(True)
|
||||||
self.page.label_shadBlur.setHidden(True)
|
self.page.label_shadBlur.setHidden(True)
|
||||||
self.page.spinBox_shadBlur.setHidden(True)
|
self.page.spinBox_shadBlur.setHidden(True)
|
||||||
super().update()
|
|
||||||
|
|
||||||
def centerXY(self):
|
def centerXY(self):
|
||||||
self.setRelativeWidget('xPosition', 0.5)
|
self.setRelativeWidget('xPosition', 0.5)
|
||||||
|
|
|
@ -52,7 +52,6 @@ class Component(Component):
|
||||||
else:
|
else:
|
||||||
self.page.label_volume.setEnabled(False)
|
self.page.label_volume.setEnabled(False)
|
||||||
self.page.spinBox_volume.setEnabled(False)
|
self.page.spinBox_volume.setEnabled(False)
|
||||||
super().update()
|
|
||||||
|
|
||||||
def previewRender(self):
|
def previewRender(self):
|
||||||
self.updateChunksize()
|
self.updateChunksize()
|
||||||
|
@ -119,7 +118,6 @@ class Component(Component):
|
||||||
if filename:
|
if filename:
|
||||||
self.settings.setValue("componentDir", os.path.dirname(filename))
|
self.settings.setValue("componentDir", os.path.dirname(filename))
|
||||||
self.page.lineEdit_video.setText(filename)
|
self.page.lineEdit_video.setText(filename)
|
||||||
self.update()
|
|
||||||
|
|
||||||
def getPreviewFrame(self, width, height):
|
def getPreviewFrame(self, width, height):
|
||||||
if not self.videoPath or not os.path.exists(self.videoPath):
|
if not self.videoPath or not os.path.exists(self.videoPath):
|
||||||
|
|
|
@ -98,7 +98,7 @@ class Component(Component):
|
||||||
'-r', self.settings.value("outputFrameRate"),
|
'-r', self.settings.value("outputFrameRate"),
|
||||||
'-ss', "{0:.3f}".format(startPt),
|
'-ss', "{0:.3f}".format(startPt),
|
||||||
'-i',
|
'-i',
|
||||||
os.path.join(self.core.wd, 'background.png')
|
self.core.junkStream
|
||||||
if genericPreview else inputFile,
|
if genericPreview else inputFile,
|
||||||
'-f', 'image2pipe',
|
'-f', 'image2pipe',
|
||||||
'-pix_fmt', 'rgba',
|
'-pix_fmt', 'rgba',
|
||||||
|
|
|
@ -13,7 +13,7 @@ import toolkit
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger('AVP.Core')
|
log = logging.getLogger('AVP.Core')
|
||||||
STDOUT_LOGLVL = logging.WARNING
|
STDOUT_LOGLVL = logging.VERBOSE
|
||||||
FILE_LOGLVL = logging.DEBUG
|
FILE_LOGLVL = logging.DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,10 +81,7 @@ class Core:
|
||||||
component = self.modules[moduleIndex].Component(
|
component = self.modules[moduleIndex].Component(
|
||||||
moduleIndex, compPos, self
|
moduleIndex, compPos, self
|
||||||
)
|
)
|
||||||
# init component's widget for loading/saving presets
|
|
||||||
component.widget(loader)
|
component.widget(loader)
|
||||||
# use autoUpdate() method before update() this 1 time to set attrs
|
|
||||||
component._autoUpdate()
|
|
||||||
else:
|
else:
|
||||||
moduleIndex = -1
|
moduleIndex = -1
|
||||||
log.debug(
|
log.debug(
|
||||||
|
@ -186,9 +183,8 @@ class Core:
|
||||||
if hasattr(loader, 'window'):
|
if hasattr(loader, 'window'):
|
||||||
for widget, value in data['WindowFields']:
|
for widget, value in data['WindowFields']:
|
||||||
widget = eval('loader.window.%s' % widget)
|
widget = eval('loader.window.%s' % widget)
|
||||||
widget.blockSignals(True)
|
with toolkit.blockSignals(widget):
|
||||||
toolkit.setWidgetValue(widget, value)
|
toolkit.setWidgetValue(widget, value)
|
||||||
widget.blockSignals(False)
|
|
||||||
|
|
||||||
for key, value in data['Settings']:
|
for key, value in data['Settings']:
|
||||||
Core.settings.setValue(key, value)
|
Core.settings.setValue(key, value)
|
||||||
|
@ -474,6 +470,7 @@ class Core:
|
||||||
'logDir': os.path.join(dataDir, 'log'),
|
'logDir': os.path.join(dataDir, 'log'),
|
||||||
'presetDir': os.path.join(dataDir, 'presets'),
|
'presetDir': os.path.join(dataDir, 'presets'),
|
||||||
'componentsPath': os.path.join(wd, 'components'),
|
'componentsPath': os.path.join(wd, 'components'),
|
||||||
|
'junkStream': os.path.join(wd, 'gui', 'background.png'),
|
||||||
'encoderOptions': encoderOptions,
|
'encoderOptions': encoderOptions,
|
||||||
'resolutions': [
|
'resolutions': [
|
||||||
'1920x1080',
|
'1920x1080',
|
||||||
|
|
|
@ -20,11 +20,20 @@ class AddComponent(QUndoCommand):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.moduleI = moduleI
|
self.moduleI = moduleI
|
||||||
self.compI = compI
|
self.compI = compI
|
||||||
|
self.comp = None
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
self.parent.core.insertComponent(self.compI, self.moduleI, self.parent)
|
if self.comp is None:
|
||||||
|
self.parent.core.insertComponent(
|
||||||
|
self.compI, self.moduleI, self.parent)
|
||||||
|
else:
|
||||||
|
# inserting previously-created component
|
||||||
|
self.parent.core.insertComponent(
|
||||||
|
self.compI, self.comp, self.parent)
|
||||||
|
|
||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
|
self.comp = self.parent.core.selectedComponents[self.compI]
|
||||||
self.parent._removeComponent(self.compI)
|
self.parent._removeComponent(self.compI)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ from toolkit import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger('AVP.MainWindow')
|
log = logging.getLogger('AVP.Gui.MainWindow')
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
class MainWindow(QtWidgets.QMainWindow):
|
||||||
|
@ -76,7 +76,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
# Create the preview window and its thread, queues, and timers
|
# Create the preview window and its thread, queues, and timers
|
||||||
log.debug('Creating preview window')
|
log.debug('Creating preview window')
|
||||||
self.previewWindow = PreviewWindow(self, os.path.join(
|
self.previewWindow = PreviewWindow(self, os.path.join(
|
||||||
Core.wd, "background.png"))
|
Core.wd, 'gui', "background.png"))
|
||||||
window.verticalLayout_previewWrapper.addWidget(self.previewWindow)
|
window.verticalLayout_previewWrapper.addWidget(self.previewWindow)
|
||||||
|
|
||||||
log.debug('Starting preview thread')
|
log.debug('Starting preview thread')
|
||||||
|
|
|
@ -5,12 +5,16 @@
|
||||||
from PyQt5 import QtCore, QtWidgets
|
from PyQt5 import QtCore, QtWidgets
|
||||||
import string
|
import string
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from toolkit import badName
|
from toolkit import badName
|
||||||
from core import Core
|
from core import Core
|
||||||
from gui.actions import *
|
from gui.actions import *
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger('AVP.Gui.PresetManager')
|
||||||
|
|
||||||
|
|
||||||
class PresetManager(QtWidgets.QDialog):
|
class PresetManager(QtWidgets.QDialog):
|
||||||
def __init__(self, window, parent):
|
def __init__(self, window, parent):
|
||||||
super().__init__(parent.window)
|
super().__init__(parent.window)
|
||||||
|
|
|
@ -14,7 +14,7 @@ from toolkit.frame import Checkerboard
|
||||||
from toolkit import disableWhenOpeningProject
|
from toolkit import disableWhenOpeningProject
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger("AVP.PreviewThread")
|
log = logging.getLogger("AVP.Gui.PreviewThread")
|
||||||
|
|
||||||
|
|
||||||
class Worker(QtCore.QObject):
|
class Worker(QtCore.QObject):
|
||||||
|
|
|
@ -7,7 +7,7 @@ class PreviewWindow(QtWidgets.QLabel):
|
||||||
Paints the preview QLabel in MainWindow and maintains the aspect ratio
|
Paints the preview QLabel in MainWindow and maintains the aspect ratio
|
||||||
when the window is resized.
|
when the window is resized.
|
||||||
'''
|
'''
|
||||||
log = logging.getLogger('AVP.PreviewWindow')
|
log = logging.getLogger('AVP.Gui.PreviewWindow')
|
||||||
|
|
||||||
def __init__(self, parent, img):
|
def __init__(self, parent, img):
|
||||||
super(PreviewWindow, self).__init__()
|
super(PreviewWindow, self).__init__()
|
||||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
||||||
from __init__ import wd
|
from __init__ import wd
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger('AVP.Entrypoint')
|
log = logging.getLogger('AVP.Main')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
@ -6,19 +6,53 @@ import string
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import logging
|
||||||
|
from copy import copy
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger('AVP.Toolkit.Common')
|
||||||
|
|
||||||
|
|
||||||
class blockSignals:
|
class blockSignals:
|
||||||
'''A context manager to temporarily block a Qt widget from updating'''
|
'''
|
||||||
def __init__(self, widget):
|
Context manager to temporarily block list of QtWidgets from updating,
|
||||||
self.widget = widget
|
and guarantee restoring the previous state afterwards.
|
||||||
|
'''
|
||||||
|
def __init__(self, widgets):
|
||||||
|
if type(widgets) is dict:
|
||||||
|
self.widgets = concatDictVals(widgets)
|
||||||
|
else:
|
||||||
|
self.widgets = (
|
||||||
|
widgets if hasattr(widgets, '__iter__')
|
||||||
|
else [widgets]
|
||||||
|
)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.widget.blockSignals(True)
|
log.verbose('Blocking signals for %s' % ", ".join([
|
||||||
|
str(w.__class__.__name__) for w in self.widgets
|
||||||
|
]))
|
||||||
|
self.oldStates = [w.signalsBlocked() for w in self.widgets]
|
||||||
|
for w in self.widgets:
|
||||||
|
w.blockSignals(True)
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self.widget.blockSignals(False)
|
log.verbose('Resetting blockSignals to %s' % sum(self.oldStates))
|
||||||
|
for w, state in zip(self.widgets, self.oldStates):
|
||||||
|
w.blockSignals(state)
|
||||||
|
|
||||||
|
|
||||||
|
def concatDictVals(d):
|
||||||
|
'''Concatenates all values in given dict into one list.'''
|
||||||
|
key, value = d.popitem()
|
||||||
|
d[key] = value
|
||||||
|
final = copy(value)
|
||||||
|
if type(final) is not list:
|
||||||
|
final = [final]
|
||||||
|
final.extend([val for val in d.values()])
|
||||||
|
else:
|
||||||
|
value.extend([item for val in d.values() for item in val])
|
||||||
|
return final
|
||||||
|
|
||||||
|
|
||||||
def badName(name):
|
def badName(name):
|
||||||
|
@ -119,12 +153,14 @@ def connectWidget(widget, func):
|
||||||
elif type(widget) == QtWidgets.QComboBox:
|
elif type(widget) == QtWidgets.QComboBox:
|
||||||
widget.currentIndexChanged.connect(func)
|
widget.currentIndexChanged.connect(func)
|
||||||
else:
|
else:
|
||||||
|
log.warning('Failed to connect %s ' % str(widget.__class__.__name__))
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setWidgetValue(widget, val):
|
def setWidgetValue(widget, val):
|
||||||
'''Generic setValue method for use with any typical QtWidget'''
|
'''Generic setValue method for use with any typical QtWidget'''
|
||||||
|
log.verbose('Setting %s to %s' % (str(widget.__class__.__name__), val))
|
||||||
if type(widget) == QtWidgets.QLineEdit:
|
if type(widget) == QtWidgets.QLineEdit:
|
||||||
widget.setText(val)
|
widget.setText(val)
|
||||||
elif type(widget) == QtWidgets.QSpinBox \
|
elif type(widget) == QtWidgets.QSpinBox \
|
||||||
|
@ -135,6 +171,7 @@ def setWidgetValue(widget, val):
|
||||||
elif type(widget) == QtWidgets.QComboBox:
|
elif type(widget) == QtWidgets.QComboBox:
|
||||||
widget.setCurrentIndex(val)
|
widget.setCurrentIndex(val)
|
||||||
else:
|
else:
|
||||||
|
log.warning('Failed to set %s ' % str(widget.__class__.__name__))
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
Reference in New Issue