From 669756b391d26661cf2e2a97a304e73343ef6655 Mon Sep 17 00:00:00 2001 From: tassaron Date: Sun, 11 Jan 2026 14:29:58 -0500 Subject: update to Qt 6 and Pillow 12 and yeah, I accidentally ran black on the codebase. I don't want to spend more free time fixing that. All of these changes are simple renames or removals, nothing too major. --- src/component.py | 657 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 350 insertions(+), 307 deletions(-) (limited to 'src/component.py') diff --git a/src/component.py b/src/component.py index 1e10f66..01d4e44 100644 --- a/src/component.py +++ b/src/component.py @@ -1,9 +1,10 @@ -''' - Base classes for components to import. Read comments for some documentation - on making a valid component. -''' -from PyQt5 import uic, QtCore, QtWidgets -from PyQt5.QtGui import QColor +""" +Base classes for components to import. Read comments for some documentation +on making a valid component. +""" + +from PyQt6 import uic, QtCore, QtWidgets +from PyQt6.QtGui import QColor, QUndoCommand import os import sys import math @@ -13,18 +14,22 @@ from copy import copy from .toolkit.frame import BlankFrame from .toolkit import ( - getWidgetValue, setWidgetValue, connectWidget, rgbFromString, blockSignals + getWidgetValue, + setWidgetValue, + connectWidget, + rgbFromString, + blockSignals, ) -log = logging.getLogger('AVP.ComponentHandler') +log = logging.getLogger("AVP.ComponentHandler") class ComponentMetaclass(type(QtCore.QObject)): - ''' - Checks the validity of each Component class and mutates some attrs. - E.g., takes only major version from version string & decorates methods - ''' + """ + Checks the validity of each Component class and mutates some attrs. + E.g., takes only major version from version string & decorates methods + """ def initializationWrapper(func): def initializationWrapper(self, *args, **kwargs): @@ -32,51 +37,55 @@ class ComponentMetaclass(type(QtCore.QObject)): return func(self, *args, **kwargs) except Exception: try: - raise ComponentError(self, 'initialization process') + raise ComponentError(self, "initialization process") except ComponentError: return + return initializationWrapper def renderWrapper(func): def renderWrapper(self, *args, **kwargs): try: log.verbose( - '### %s #%s renders a preview frame ###', - self.__class__.name, str(self.compPos), + "### %s #%s renders a preview frame ###", + self.__class__.name, + str(self.compPos), ) return func(self, *args, **kwargs) except Exception as e: try: - if e.__class__.__name__.startswith('Component'): + if e.__class__.__name__.startswith("Component"): raise else: - raise ComponentError(self, 'renderer') + raise ComponentError(self, "renderer") except ComponentError: return BlankFrame() + return renderWrapper def commandWrapper(func): - '''Intercepts the command() method to check for global args''' + """Intercepts the command() method to check for global args""" + def commandWrapper(self, arg): - if arg.startswith('preset='): - _, preset = arg.split('=', 1) + if arg.startswith("preset="): + _, preset = arg.split("=", 1) path = os.path.join(self.core.getPresetDir(self), preset) if not os.path.exists(path): print('Couldn\'t locate preset "%s"' % preset) quit(1) else: - print('Opening "%s" preset on layer %s' % ( - preset, self.compPos) - ) + print('Opening "%s" preset on layer %s' % (preset, self.compPos)) self.core.openPreset(path, self.compPos, preset) # Don't call the component's command() method return else: return func(self, arg) + return commandWrapper def propertiesWrapper(func): - '''Intercepts the usual properties if the properties are locked.''' + """Intercepts the usual properties if the properties are locked.""" + def propertiesWrapper(self): if self._lockedProperties is not None: return self._lockedProperties @@ -85,22 +94,26 @@ class ComponentMetaclass(type(QtCore.QObject)): return func(self) except Exception: try: - raise ComponentError(self, 'properties') + raise ComponentError(self, "properties") except ComponentError: return [] + return propertiesWrapper def errorWrapper(func): - '''Intercepts the usual error message if it is locked.''' + """Intercepts the usual error message if it is locked.""" + def errorWrapper(self): if self._lockedError is not None: return self._lockedError else: return func(self) + return errorWrapper def loadPresetWrapper(func): - '''Wraps loadPreset to handle the self.openingPreset boolean''' + """Wraps loadPreset to handle the self.openingPreset boolean""" + class openingPreset: def __init__(self, comp): self.comp = comp @@ -117,17 +130,19 @@ class ComponentMetaclass(type(QtCore.QObject)): return func(self, *args) except Exception: try: - raise ComponentError(self, 'preset loader') + raise ComponentError(self, "preset loader") except ComponentError: return + return presetWrapper def updateWrapper(func): - ''' - Calls _preUpdate before every subclass update(). - Afterwards, for non-user updates, calls _autoUpdate(). - For undoable updates triggered by the user, calls _userUpdate() - ''' + """ + Calls _preUpdate before every subclass update(). + Afterwards, for non-user updates, calls _autoUpdate(). + For undoable updates triggered by the user, calls _userUpdate() + """ + class wrap: def __init__(self, comp, auto): self.comp = comp @@ -137,28 +152,33 @@ class ComponentMetaclass(type(QtCore.QObject)): self.comp._preUpdate() def __exit__(self, *args): - if self.auto or self.comp.openingPreset \ - or not hasattr(self.comp.parent, 'undoStack'): - log.verbose('Automatic update') + if ( + self.auto + or self.comp.openingPreset + or not hasattr(self.comp.parent, "undoStack") + ): + log.verbose("Automatic update") self.comp._autoUpdate() else: - log.verbose('User update') + log.verbose("User update") self.comp._userUpdate() def updateWrapper(self, **kwargs): - auto = kwargs['auto'] if 'auto' in kwargs else False + auto = kwargs["auto"] if "auto" in kwargs else False with wrap(self, auto): try: return func(self) except Exception: try: - raise ComponentError(self, 'update method') + raise ComponentError(self, "update method") except ComponentError: return + return updateWrapper def widgetWrapper(func): - '''Connects all widgets to update method after the subclass's method''' + """Connects all widgets to update method after the subclass's method""" + class wrap: def __init__(self, comp): self.comp = comp @@ -169,92 +189,99 @@ class ComponentMetaclass(type(QtCore.QObject)): def __exit__(self, *args): for widgetList in self.comp._allWidgets.values(): for widget in widgetList: - log.verbose('Connecting %s', str( - widget.__class__.__name__)) + 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 + 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') + raise ComponentError(self, "widget creation") except ComponentError: return + return widgetWrapper def __new__(cls, name, parents, attrs): - if 'ui' not in attrs: + if "ui" not in attrs: # Use module name as ui filename by default - attrs['ui'] = '%s.ui' % os.path.splitext( - attrs['__module__'].split('.')[-1] - )[0] + attrs["ui"] = ( + "%s.ui" % os.path.splitext(attrs["__module__"].split(".")[-1])[0] + ) decorate = ( - 'names', # Class methods - 'error', 'audio', 'properties', # Properties - 'preFrameRender', 'previewRender', - 'loadPreset', 'command', - 'update', 'widget', + "names", # Class methods + "error", + "audio", + "properties", # Properties + "preFrameRender", + "previewRender", + "loadPreset", + "command", + "update", + "widget", ) # Auto-decorate methods for key in decorate: if key not in attrs: continue - if key in ('names'): + if key in ("names"): attrs[key] = classmethod(attrs[key]) - elif key in ('audio'): + elif key in ("audio"): attrs[key] = property(attrs[key]) - elif key == 'command': + elif key == "command": attrs[key] = cls.commandWrapper(attrs[key]) - elif key == 'previewRender': + elif key == "previewRender": attrs[key] = cls.renderWrapper(attrs[key]) - elif key == 'preFrameRender': + elif key == "preFrameRender": attrs[key] = cls.initializationWrapper(attrs[key]) - elif key == 'properties': + elif key == "properties": attrs[key] = cls.propertiesWrapper(attrs[key]) - elif key == 'error': + elif key == "error": attrs[key] = cls.errorWrapper(attrs[key]) - elif key == 'loadPreset': + elif key == "loadPreset": attrs[key] = cls.loadPresetWrapper(attrs[key]) - elif key == 'update': + elif key == "update": attrs[key] = cls.updateWrapper(attrs[key]) - elif key == 'widget' and parents[0] != QtCore.QObject: + elif key == "widget" and parents[0] != QtCore.QObject: attrs[key] = cls.widgetWrapper(attrs[key]) # Turn version string into a number try: - if 'version' not in attrs: + if "version" not in attrs: log.error( - 'No version attribute in %s. Defaulting to 1', - attrs['name']) - attrs['version'] = 1 + "No version attribute in %s. Defaulting to 1", + attrs["name"], + ) + attrs["version"] = 1 else: - attrs['version'] = int(attrs['version'].split('.')[0]) + attrs["version"] = int(attrs["version"].split(".")[0]) except ValueError: log.critical( - '%s component has an invalid version string:\n%s', - attrs['name'], str(attrs['version']) + "%s component has an invalid version string:\n%s", + attrs["name"], + str(attrs["version"]), ) except KeyError: - log.critical('%s component has no version string.', attrs['name']) + log.critical("%s component has no version string.", attrs["name"]) else: return super().__new__(cls, name, parents, attrs) quit(1) class Component(QtCore.QObject, metaclass=ComponentMetaclass): - ''' - The base class for components to inherit. - ''' + """ + The base class for components to inherit. + """ - name = 'Component' + name = "Component" # ui = 'name_Of_Non_Default_Ui_File' - version = '1.0.0' + version = "1.0.0" # The major version (before the first dot) is used to determine # preset compatibility; the rest is ignored so it can be non-numeric. @@ -297,19 +324,19 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): def __repr__(self): import pprint + try: preset = self.savePreset() except Exception as e: - preset = '%s occurred while saving preset' % str(e) - - return ( - 'Component(module %s, pos %s) (%s)\n' - 'Name: %s v%s\nPreset: %s' % ( - self.moduleIndex, self.compPos, - object.__repr__(self), - self.__class__.name, str(self.__class__.version), - pprint.pformat(preset) - ) + preset = "%s occurred while saving preset" % str(e) + + return "Component(module %s, pos %s) (%s)\n" "Name: %s v%s\nPreset: %s" % ( + self.moduleIndex, + self.compPos, + object.__repr__(self), + self.__class__.name, + str(self.__class__.version), + pprint.pformat(preset), ) # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ @@ -321,17 +348,17 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): return image def preFrameRender(self, **kwargs): - ''' - Must call super() when subclassing - Triggered only before a video is exported (video_thread.py) - self.audioFile = filepath to the main input audio file - self.completeAudioArray = a list of audio samples - self.sampleSize = number of audio samples per video frame - self.progressBarUpdate = signal to set progress bar number - self.progressBarSetText = signal to set progress bar text - Use the latter two signals to update the MainWindow if needed - for a long initialization procedure (i.e., for a visualizer) - ''' + """ + Must call super() when subclassing + Triggered only before a video is exported (video_thread.py) + self.audioFile = filepath to the main input audio file + self.completeAudioArray = a list of audio samples + self.sampleSize = number of audio samples per video frame + self.progressBarUpdate = signal to set progress bar number + self.progressBarSetText = signal to set progress bar text + Use the latter two signals to update the MainWindow if needed + for a long initialization procedure (i.e., for a visualizer) + """ for key, value in kwargs.items(): setattr(self, key, value) @@ -348,92 +375,94 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ def properties(self): - ''' - Return a list of properties to signify if your component is - non-animated ('static'), returns sound ('audio'), or has - encountered an error in configuration ('error'). - ''' + """ + Return a list of properties to signify if your component is + non-animated ('static'), returns sound ('audio'), or has + encountered an error in configuration ('error'). + """ return [] def error(self): - ''' - Return a string containing an error message, or None for a default. - Or tuple of two strings for a message with details. - Alternatively use lockError(msgString) within properties() - to skip this method entirely. - ''' + """ + Return a string containing an error message, or None for a default. + Or tuple of two strings for a message with details. + Alternatively use lockError(msgString) within properties() + to skip this method entirely. + """ return def audio(self): - ''' - Return audio to mix into master as a tuple with two elements: - The first element can be: - - A string (path to audio file), - - Or an object that returns audio data through a pipe - The second element must be a dictionary of ffmpeg filters/options - to apply to the input stream. See the filter docs for ideas: - https://ffmpeg.org/ffmpeg-filters.html - ''' + """ + Return audio to mix into master as a tuple with two elements: + The first element can be: + - A string (path to audio file), + - Or an object that returns audio data through a pipe + The second element must be a dictionary of ffmpeg filters/options + to apply to the input stream. See the filter docs for ideas: + https://ffmpeg.org/ffmpeg-filters.html + """ # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ # Idle Methods # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ def widget(self, parent): - ''' - Call super().widget(*args) to create the component widget - which also auto-connects any common widgets (e.g., checkBoxes) - to self.update(). Then in a subclass connect special actions - (e.g., pushButtons to select a file) and initialize - ''' + """ + Call super().widget(*args) to create the component widget + which also auto-connects any common widgets (e.g., checkBoxes) + to self.update(). Then in a subclass connect special actions + (e.g., pushButtons to select a file) and initialize + """ self.parent = parent self.settings = parent.settings log.verbose( - 'Creating UI for %s #%s\'s widget', - self.__class__.name, self.compPos + "Creating UI for %s #%s's widget", + self.__class__.name, + self.compPos, ) self.page = self.loadUi(self.__class__.ui) # Find all normal widgets which will be connected after subclass method self._allWidgets = { - 'lineEdit': self.page.findChildren(QtWidgets.QLineEdit), - 'checkBox': self.page.findChildren(QtWidgets.QCheckBox), - 'spinBox': self.page.findChildren(QtWidgets.QSpinBox), - 'comboBox': self.page.findChildren(QtWidgets.QComboBox), + "lineEdit": self.page.findChildren(QtWidgets.QLineEdit), + "checkBox": self.page.findChildren(QtWidgets.QCheckBox), + "spinBox": self.page.findChildren(QtWidgets.QSpinBox), + "comboBox": self.page.findChildren(QtWidgets.QComboBox), } - self._allWidgets['spinBox'].extend( + self._allWidgets["spinBox"].extend( self.page.findChildren(QtWidgets.QDoubleSpinBox) ) def update(self): - ''' - Starting point for a component update. A subclass should override - this method, and the base class will then magically insert a call - to either _autoUpdate() or _userUpdate() at the end. - ''' + """ + Starting point for a component update. A subclass should override + this method, and the base class will then magically insert a call + to either _autoUpdate() or _userUpdate() at the end. + """ def loadPreset(self, presetDict, presetName=None): - ''' - Subclasses should take (presetDict, *args) as args. - Must use super().loadPreset(presetDict, *args) first, - then update self.page widgets using the preset dict. - ''' - self.currentPreset = presetName \ - if presetName is not None else presetDict['preset'] + """ + Subclasses should take (presetDict, *args) as args. + Must use super().loadPreset(presetDict, *args) first, + then update self.page widgets using the preset dict. + """ + self.currentPreset = ( + presetName if presetName is not None else presetDict["preset"] + ) for attr, widget in self._trackedWidgets.items(): - key = attr if attr not in self._presetNames \ - else self._presetNames[attr] + key = attr if attr not in self._presetNames else self._presetNames[attr] try: val = presetDict[key] except KeyError as e: log.info( - '%s missing value %s. Outdated preset?', - self.currentPreset, str(e) + "%s missing value %s. Outdated preset?", + self.currentPreset, + str(e), ) val = getattr(self, key) if attr in self._colorWidgets: - widget.setText('%s,%s,%s' % val) + widget.setText("%s,%s,%s" % val) btnStyle = ( "QPushButton { background-color : %s; outline: none; }" % QColor(*val).name() @@ -450,8 +479,7 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): saveValueStore = {} for attr, widget in self._trackedWidgets.items(): presetAttrName = ( - attr if attr not in self._presetNames - else self._presetNames[attr] + attr if attr not in self._presetNames else self._presetNames[attr] ) if attr in self._relativeWidgets: try: @@ -465,19 +493,18 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): return saveValueStore def commandHelp(self): - '''Help text as string for this component's commandline arguments''' - - def command(self, arg=''): - ''' - Configure a component using an arg from the commandline. This is - never called if global args like 'preset=' are found in the arg. - So simply check for any non-global args in your component and - call super().command() at the end to get a Help message. - ''' + """Help text as string for this component's commandline arguments""" + + def command(self, arg=""): + """ + Configure a component using an arg from the commandline. This is + never called if global args like 'preset=' are found in the arg. + So simply check for any non-global args in your component and + call super().command() at the end to get a Help message. + """ print( - self.__class__.name, 'Usage:\n' - 'Open a preset for this component:\n' - ' "preset=Preset Name"' + self.__class__.name, + "Usage:\n" "Open a preset for this component:\n" ' "preset=Preset Name"', ) self.commandHelp() quit(0) @@ -486,19 +513,21 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): # "Private" Methods # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ def _preUpdate(self): - '''Happens before subclass update()''' + """Happens before subclass update()""" for attr in self._relativeWidgets: self.updateRelativeWidget(attr) def _userUpdate(self): - '''Happens after subclass update() for an undoable update by user.''' + """Happens after subclass update() for an undoable update by user.""" oldWidgetVals = { - attr: copy(getattr(self, attr)) - for attr in self._trackedWidgets + attr: copy(getattr(self, attr)) for attr in self._trackedWidgets } newWidgetVals = { - attr: getWidgetValue(widget) - if attr not in self._colorWidgets else rgbFromString(widget.text()) + attr: ( + getWidgetValue(widget) + if attr not in self._colorWidgets + else rgbFromString(widget.text()) + ) for attr, widget in self._trackedWidgets.items() } modifiedWidgets = { @@ -511,7 +540,7 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): self.parent.undoStack.push(action) def _autoUpdate(self): - '''Happens after subclass update() for an internal component update.''' + """Happens after subclass update() for an internal component update.""" newWidgetVals = { attr: getWidgetValue(widget) for attr, widget in self._trackedWidgets.items() @@ -520,10 +549,10 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): self._sendUpdateSignal() def setAttrs(self, attrDict): - ''' - Sets attrs (linked to trackedWidgets) in this component to - the values in the attrDict. Mutates certain widget values if needed - ''' + """ + Sets attrs (linked to trackedWidgets) in this component to + the values in the attrDict. Mutates certain widget values if needed + """ for attr, val in attrDict.items(): if attr in self._colorWidgets: # Color Widgets must have a tuple & have a button to update @@ -533,110 +562,111 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): rgbTuple = rgbFromString(val) btnStyle = ( "QPushButton { background-color : %s; outline: none; }" - % QColor(*rgbTuple).name()) + % QColor(*rgbTuple).name() + ) self._colorWidgets[attr].setStyleSheet(btnStyle) setattr(self, attr, rgbTuple) else: # Normal tracked widget setattr(self, attr, val) - log.verbose('Setting %s self.%s to %s' % ( - self.__class__.name, attr, val)) + log.verbose("Setting %s self.%s to %s" % (self.__class__.name, 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 - ] + """ + 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 + val = "%s,%s,%s" % val setWidgetValue(widget, val) def _sendUpdateSignal(self): if not self.core.openingProject: self.parent.drawPreview() saveValueStore = self.savePreset() - saveValueStore['preset'] = self.currentPreset + saveValueStore["preset"] = self.currentPreset self.modified.emit(self.compPos, saveValueStore) def trackWidgets(self, trackDict, **kwargs): - ''' - Name widgets to track in update(), savePreset(), loadPreset(), and - command(). Requires a dict of attr names as keys, widgets as values - - Optional args: - 'presetNames': preset variable names to replace 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. - ''' + """ + Name widgets to track in update(), savePreset(), loadPreset(), and + command(). Requires a dict of attr names as keys, widgets as values + + Optional args: + 'presetNames': preset variable names to replace 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. + """ self._trackedWidgets = trackDict for kwarg in kwargs: try: if kwarg in ( - 'presetNames', - 'commandArgs', - 'colorWidgets', - 'relativeWidgets', - ): - setattr(self, '_{}'.format(kwarg), kwargs[kwarg]) + "presetNames", + "commandArgs", + "colorWidgets", + "relativeWidgets", + ): + setattr(self, "_{}".format(kwarg), kwargs[kwarg]) else: - raise ComponentError( - self, 'Nonsensical keywords to trackWidgets.') + raise ComponentError(self, "Nonsensical keywords to trackWidgets.") except ComponentError: continue - if kwarg == 'colorWidgets': + if kwarg == "colorWidgets": + def makeColorFunc(attr): def pickColor_(): self.mergeUndo = False self.pickColor( self._trackedWidgets[attr], - self._colorWidgets[attr] + self._colorWidgets[attr], ) self.mergeUndo = True + return pickColor_ - self._colorFuncs = { - attr: makeColorFunc(attr) for attr in kwargs[kwarg] - } + + self._colorFuncs = {attr: makeColorFunc(attr) for attr in kwargs[kwarg]} for attr, func in self._colorFuncs.items(): self._colorWidgets[attr].clicked.connect(func) self._colorWidgets[attr].setStyleSheet( - "QPushButton {" - "background-color : #FFFFFF; outline: none; }" + "QPushButton {" "background-color : #FFFFFF; outline: none; }" ) - if kwarg == 'relativeWidgets': + if kwarg == "relativeWidgets": # store maximum values of spinBoxes to be scaled appropriately for attr in kwargs[kwarg]: - self._relativeMaximums[attr] = \ - self._trackedWidgets[attr].maximum() + self._relativeMaximums[attr] = self._trackedWidgets[attr].maximum() self.updateRelativeWidgetMaximum(attr) - setattr( - self, attr, getWidgetValue(self._trackedWidgets[attr]) - ) + setattr(self, attr, getWidgetValue(self._trackedWidgets[attr])) self._preUpdate() self._autoUpdate() 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.""" dialog = QtWidgets.QColorDialog() - dialog.setOption(QtWidgets.QColorDialog.ShowAlphaChannel, True) + # TODO alpha channel is not actually shown in most color picker widgets? + dialog.setOption( + QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel, True + ) color = dialog.getColor() if color.isValid(): - RGBstring = '%s,%s,%s' % ( - str(color.red()), str(color.green()), str(color.blue())) - btnStyle = "QPushButton{background-color: %s; outline: none;}" \ - % color.name() + RGBstring = "%s,%s,%s" % ( + str(color.red()), + str(color.green()), + str(color.blue()), + ) + btnStyle = ( + "QPushButton{background-color: %s; outline: none;}" % color.name() + ) textWidget.setText(RGBstring) button.setStyleSheet(btnStyle) @@ -659,25 +689,25 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): self._lockedSize = None def loadUi(self, filename): - '''Load a Qt Designer ui file to use for this component's widget''' + """Load a Qt Designer ui file to use for this component's widget""" return uic.loadUi(os.path.join(self.core.componentsPath, filename)) @property def width(self): if self._lockedSize is None: - return int(self.settings.value('outputWidth')) + return int(self.settings.value("outputWidth")) else: return self._lockedSize[0] @property def height(self): if self._lockedSize is None: - return int(self.settings.value('outputHeight')) + return int(self.settings.value("outputHeight")) else: return self._lockedSize[1] def cancel(self): - '''Stop any lengthy process in response to this variable.''' + """Stop any lengthy process in response to this variable.""" self.canceled = True def reset(self): @@ -688,22 +718,22 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): def relativeWidgetAxis(func): def relativeWidgetAxis(self, attr, *args, **kwargs): hasVerticalWords = ( - lambda attr: - 'height' in attr.lower() or - 'ypos' in attr.lower() or - attr == 'y' + lambda attr: "height" in attr.lower() + or "ypos" in attr.lower() + or attr == "y" ) - if 'axis' not in kwargs: + if "axis" not in kwargs: axis = self.width if hasVerticalWords(attr): axis = self.height - kwargs['axis'] = axis - if 'axis' in kwargs and type(kwargs['axis']) is tuple: - axis = kwargs['axis'][0] + kwargs["axis"] = axis + if "axis" in kwargs and type(kwargs["axis"]) is tuple: + axis = kwargs["axis"][0] if hasVerticalWords(attr): - axis = kwargs['axis'][1] - kwargs['axis'] = axis + axis = kwargs["axis"][1] + kwargs["axis"] = axis return func(self, attr, *args, **kwargs) + return relativeWidgetAxis @relativeWidgetAxis @@ -712,14 +742,20 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): val = self._relativeValues[attr] if val > 50.0: log.warning( - '%s #%s attempted to set %s to dangerously high number %s', - self.__class__.name, self.compPos, attr, val + "%s #%s attempted to set %s to dangerously high number %s", + self.__class__.name, + self.compPos, + attr, + val, ) val = 50.0 - result = math.ceil(kwargs['axis'] * val) + result = math.ceil(kwargs["axis"] * val) log.verbose( - 'Converting %s: f%s to px%s using axis %s', - attr, val, result, kwargs['axis'] + "Converting %s: f%s to px%s using axis %s", + attr, + val, + result, + kwargs["axis"], ) return result @@ -727,65 +763,63 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): def floatValForAttr(self, attr, val=None, **kwargs): if val is None: val = self._trackedWidgets[attr].value() - return val / kwargs['axis'] + return val / kwargs["axis"] def setRelativeWidget(self, attr, floatVal): - '''Set a relative widget using a float''' + """Set a relative widget using a float""" pixelVal = self.pixelValForAttr(attr, floatVal) with blockSignals(self._trackedWidgets[attr]): 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. - ''' + """ + 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: return self.oldAttrs[attr] else: try: return getattr(self, attr) except AttributeError: - log.error('Using visible values instead of oldAttrs') + log.error("Using visible values instead of oldAttrs") return self._trackedWidgets[attr].value() def updateRelativeWidget(self, attr): - '''Called by _preUpdate() for each relativeWidget before each update''' + """Called by _preUpdate() for each relativeWidget before each update""" oldUserValue = self.getOldAttr(attr) newUserValue = self._trackedWidgets[attr].value() newRelativeVal = self.floatValForAttr(attr, newUserValue) if attr in self._relativeValues: oldRelativeVal = self._relativeValues[attr] - if oldUserValue == newUserValue \ - and oldRelativeVal != newRelativeVal: + if oldUserValue == newUserValue and oldRelativeVal != newRelativeVal: # Float changed without pixel value changing, which # means the pixel value needs to be updated log.debug( - 'Updating %s #%s\'s relative widget: %s', - self.__class__.name, self.compPos, attr) + "Updating %s #%s's relative widget: %s", + self.__class__.name, + self.compPos, + attr, + ) with blockSignals(self._trackedWidgets[attr]): self.updateRelativeWidgetMaximum(attr) pixelVal = self.pixelValForAttr(attr, oldRelativeVal) self._trackedWidgets[attr].setValue(pixelVal) - if attr not in self._relativeValues \ - or oldUserValue != newUserValue: + if attr not in self._relativeValues or oldUserValue != newUserValue: self._relativeValues[attr] = newRelativeVal def updateRelativeWidgetMaximum(self, attr): - maxRes = int(self.core.resolutions[0].split('x')[0]) - newMaximumValue = self.width * ( - self._relativeMaximums[attr] / - maxRes - ) + maxRes = int(self.core.resolutions[0].split("x")[0]) + newMaximumValue = self.width * (self._relativeMaximums[attr] / maxRes) self._trackedWidgets[attr].setMaximum(int(newMaximumValue)) class ComponentError(RuntimeError): - '''Gives the MainWindow a traceback to display, and cancels the export.''' + """Gives the MainWindow a traceback to display, and cancels the export.""" prevErrors = [] lastTime = time.time() @@ -794,42 +828,46 @@ class ComponentError(RuntimeError): if msg is None and sys.exc_info()[0] is not None: msg = str(sys.exc_info()[1]) else: - msg = 'Unknown error.' - log.error("ComponentError by %s's %s: %s" % ( - caller.name, name, msg)) + msg = "Unknown error." + log.error("ComponentError by %s's %s: %s" % (caller.name, name, msg)) # Don't create multiple windows for quickly repeated messages if len(ComponentError.prevErrors) > 1: ComponentError.prevErrors.pop() ComponentError.prevErrors.insert(0, name) curTime = time.time() - if name in ComponentError.prevErrors[1:] \ - and curTime - ComponentError.lastTime < 1.0: + if ( + name in ComponentError.prevErrors[1:] + and curTime - ComponentError.lastTime < 1.0 + ): return ComponentError.lastTime = time.time() from .toolkit import formatTraceback + if sys.exc_info()[0] is not None: - string = ( - "%s component (#%s): %s encountered %s %s: %s" % ( - caller.__class__.name, - str(caller.compPos), - name, - 'an' if any([ - sys.exc_info()[0].__name__.startswith(vowel) - for vowel in ('A', 'I', 'U', 'O', 'E') - ]) else 'a', - sys.exc_info()[0].__name__, - str(sys.exc_info()[1]) - ) + string = "%s component (#%s): %s encountered %s %s: %s" % ( + caller.__class__.name, + str(caller.compPos), + name, + ( + "an" + if any( + [ + sys.exc_info()[0].__name__.startswith(vowel) + for vowel in ("A", "I", "U", "O", "E") + ] + ) + else "a" + ), + sys.exc_info()[0].__name__, + str(sys.exc_info()[1]), ) detail = formatTraceback(sys.exc_info()[2]) else: string = name detail = "Attributes:\n%s" % ( - "\n".join( - [m for m in dir(caller) if not m.startswith('_')] - ) + "\n".join([m for m in dir(caller) if not m.startswith("_")]) ) super().__init__(string) @@ -837,28 +875,29 @@ class ComponentError(RuntimeError): caller._error.emit(string, detail) -class ComponentUpdate(QtWidgets.QUndoCommand): - '''Command object for making a component action undoable''' +class ComponentUpdate(QUndoCommand): + """Command object for making a component action undoable""" + def __init__(self, parent, oldWidgetVals, modifiedVals): - super().__init__( - 'change %s component #%s' % ( - parent.name, parent.compPos - ) - ) + super().__init__("change %s component #%s" % (parent.name, parent.compPos)) self.undone = False self.res = (int(parent.width), int(parent.height)) self.parent = parent self.oldWidgetVals = { - attr: copy(val) - if attr not in self.parent._relativeWidgets - else self.parent.floatValForAttr(attr, val, axis=self.res) + attr: ( + copy(val) + if attr not in self.parent._relativeWidgets + else self.parent.floatValForAttr(attr, val, axis=self.res) + ) for attr, val in oldWidgetVals.items() if attr in modifiedVals } self.modifiedVals = { - attr: val - if attr not in self.parent._relativeWidgets - else self.parent.floatValForAttr(attr, val, axis=self.res) + attr: ( + val + if attr not in self.parent._relativeWidgets + else self.parent.floatValForAttr(attr, val, axis=self.res) + ) for attr, val in modifiedVals.items() } @@ -877,12 +916,13 @@ class ComponentUpdate(QtWidgets.QUndoCommand): self.modifiedVals[attr] = val else: log.warning( - '%s component settings changed at once. (%s)', - len(self.modifiedVals), repr(self.modifiedVals) + "%s component settings changed at once. (%s)", + len(self.modifiedVals), + repr(self.modifiedVals), ) def id(self): - '''If 2 consecutive updates have same id, Qt will call mergeWith()''' + """If 2 consecutive updates have same id, Qt will call mergeWith()""" return self.id_ def mergeWith(self, other): @@ -890,20 +930,23 @@ class ComponentUpdate(QtWidgets.QUndoCommand): return True def setWidgetValues(self, attrDict): - ''' - Mask the component's usual method to handle our - relative widgets in case the resolution has changed. - ''' + """ + Mask the component's usual method to handle our + relative widgets in case the resolution has changed. + """ newAttrDict = { - attr: val if attr not in self.parent._relativeWidgets - else self.parent.pixelValForAttr(attr, val) + attr: ( + val + if attr not in self.parent._relativeWidgets + else self.parent.pixelValForAttr(attr, val) + ) for attr, val in attrDict.items() } self.parent.setWidgetValues(newAttrDict) def redo(self): if self.undone: - log.info('Redoing component update') + log.info("Redoing component update") self.parent.oldAttrs = self.relativeWidgetValsAfterUndo self.setWidgetValues(self.modifiedVals) self.parent.update(auto=True) @@ -916,7 +959,7 @@ class ComponentUpdate(QtWidgets.QUndoCommand): self.parent._sendUpdateSignal() def undo(self): - log.info('Undoing component update') + log.info("Undoing component update") self.undone = True self.parent.oldAttrs = self.relativeWidgetValsAfterRedo self.setWidgetValues(self.oldWidgetVals) -- cgit v1.2.3