From e92e9d79f95ad67e83074ef318278c3486601eac Mon Sep 17 00:00:00 2001 From: DH4 Date: Fri, 23 Jun 2017 17:38:05 -0500 Subject: QT5 Conversion + Directory Structure --- src/components/text.ui | 316 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 src/components/text.ui (limited to 'src/components/text.ui') diff --git a/src/components/text.ui b/src/components/text.ui new file mode 100644 index 0000000..05e7f8e --- /dev/null +++ b/src/components/text.ui @@ -0,0 +1,316 @@ + + + Form + + + + 0 + 0 + 586 + 197 + + + + Form + + + + + + 4 + + + + + + + + 0 + 0 + + + + Font + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 0 + 0 + + + + Font Size + + + + + + + 500 + + + + + + + + + + + + 0 + 0 + + + + Text Layout + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Text Color + + + + + + + + 32 + 32 + + + + + + + + 32 + 32 + + + + + + + + + + + + + 0 + + + + + Title + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Testing New GUI + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 0 + 0 + + + + X + + + + + + + + 0 + 0 + + + + + 80 + 16777215 + + + + + 0 + 0 + + + + 0 + + + 999999999 + + + 0 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 0 + 0 + + + + Y + + + + + + + + 0 + 0 + + + + + 80 + 16777215 + + + + 999999999 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + -- cgit v1.2.3 From ae8a547b77a618c793929701f9c1fa72d3300110 Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 3 Aug 2017 18:08:49 -0400 Subject: max spinbox vals scale relatively & less errors when spamming res change w/h attrs are locked during render so preview thread always get correctly-sized frame --- src/component.py | 92 ++++++++++++++++++++++++++++++++++++------------- src/components/image.py | 2 +- src/components/text.ui | 3 ++ src/core.py | 6 ++-- src/preview_thread.py | 2 ++ 5 files changed, 77 insertions(+), 28 deletions(-) (limited to 'src/components/text.ui') diff --git a/src/component.py b/src/component.py index c5bc44b..ea4b5ec 100644 --- a/src/component.py +++ b/src/component.py @@ -179,9 +179,14 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): self._colorWidgets = {} self._colorFuncs = {} self._relativeWidgets = {} + # pixel values stored as floats self._relativeValues = {} + # maximum values of spinBoxes at 1080p (Core.resolutions[0]) + self._relativeMaximums = {} + self._lockedProperties = None self._lockedError = None + self._lockedSize = None # Stop lengthy processes in response to this variable self.canceled = False @@ -190,8 +195,12 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): return self.__class__.name def __repr__(self): + try: + preset = self.savePreset() + except Exception as e: + preset = '%s occured while saving preset' % str(e) return '%s\n%s\n%s' % ( - self.__class__.name, str(self.__class__.version), self.savePreset() + self.__class__.name, str(self.__class__.version), preset ) # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ @@ -304,27 +313,7 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): elif attr in self._relativeWidgets: # Relative widgets: number scales to fit export resolution - dimension = self.width - try: - oldUserValue = getattr(self, attr) - except AttributeError: - oldUserValue = self._trackedWidgets[attr].value() - newUserValue = self._trackedWidgets[attr].value() - newRelativeVal = newUserValue / dimension - - if attr in self._relativeValues: - if oldUserValue == newUserValue: - oldRelativeVal = self._relativeValues[attr] - if oldRelativeVal != newRelativeVal: - # Float changed without pixel value changing, which - # means the pixel value needs to be updated - self._trackedWidgets[attr].blockSignals(True) - self._trackedWidgets[attr].setValue( - math.ceil(dimension * oldRelativeVal)) - self._trackedWidgets[attr].blockSignals(False) - if oldUserValue != newUserValue \ - or attr not in self._relativeValues: - self._relativeValues[attr] = newRelativeVal + self.updateRelativeWidget(attr) setattr(self, attr, self._trackedWidgets[attr].value()) else: @@ -436,6 +425,13 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): "background-color : #FFFFFF; outline: none; }" ) + 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.updateRelativeWidgetMaximum(attr) + def pickColor(self, textWidget, button): '''Use color picker to get color input from the user.''' dialog = QtWidgets.QColorDialog() @@ -455,23 +451,35 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): def lockError(self, msg): self._lockedError = msg + def lockSize(self, w, h): + self._lockedSize = (w, h) + def unlockProperties(self): self._lockedProperties = None def unlockError(self): self._lockedError = None + def unlockSize(self): + self._lockedSize = None + def loadUi(self, filename): '''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): - return int(self.settings.value('outputWidth')) + if self._lockedSize is None: + return int(self.settings.value('outputWidth')) + else: + return self._lockedSize[0] @property def height(self): - return int(self.settings.value('outputHeight')) + if self._lockedSize is None: + return int(self.settings.value('outputHeight')) + else: + return self._lockedSize[1] def cancel(self): '''Stop any lengthy process in response to this variable.''' @@ -482,6 +490,42 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): self.unlockProperties() self.unlockError() + def updateRelativeWidget(self, attr): + dimension = self.width + if 'height' in attr.lower() \ + or 'ypos' in attr.lower() or attr == 'y': + dimension = self.height + try: + oldUserValue = getattr(self, attr) + except AttributeError: + oldUserValue = self._trackedWidgets[attr].value() + newUserValue = self._trackedWidgets[attr].value() + newRelativeVal = newUserValue / dimension + + if attr in self._relativeValues: + oldRelativeVal = self._relativeValues[attr] + if oldUserValue == newUserValue \ + and oldRelativeVal != newRelativeVal: + # Float changed without pixel value changing, which + # means the pixel value needs to be updated + self._trackedWidgets[attr].blockSignals(True) + self.updateRelativeWidgetMaximum(attr) + self._trackedWidgets[attr].setValue( + math.ceil(dimension * oldRelativeVal)) + self._trackedWidgets[attr].blockSignals(False) + + 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 + ) + self._trackedWidgets[attr].setMaximum(int(newMaximumValue)) + class ComponentError(RuntimeError): '''Gives the MainWindow a traceback to display, and cancels the export.''' diff --git a/src/components/image.py b/src/components/image.py index 19c4796..555dfb1 100644 --- a/src/components/image.py +++ b/src/components/image.py @@ -21,8 +21,8 @@ class Component(Component): 'xPosition': self.page.spinBox_x, 'yPosition': self.page.spinBox_y, 'stretched': self.page.checkBox_stretch, - }, presetNames={ 'mirror': self.page.checkBox_mirror, + }, presetNames={ 'imagePath': 'image', 'xPosition': 'x', 'yPosition': 'y', diff --git a/src/components/text.ui b/src/components/text.ui index 05e7f8e..bb5e5af 100644 --- a/src/components/text.ui +++ b/src/components/text.ui @@ -81,6 +81,9 @@ + + 1 + 500 diff --git a/src/core.py b/src/core.py index 24bf097..afb1e45 100644 --- a/src/core.py +++ b/src/core.py @@ -451,8 +451,8 @@ class Core: '1280x720', '854x480', ], - 'windowHasFocus': False, 'FFMPEG_BIN': findFfmpeg(), + 'windowHasFocus': False, 'canceled': False, } @@ -492,7 +492,7 @@ class Core: @classmethod def loadDefaultSettings(cls): - defaultSettings = { + cls.defaultSettings = { "outputWidth": 1280, "outputHeight": 720, "outputFrameRate": 30, @@ -509,7 +509,7 @@ class Core: "pref_genericPreview": True, } - for parm, value in defaultSettings.items(): + for parm, value in cls.defaultSettings.items(): if cls.settings.value(parm) is None: cls.settings.setValue(parm, value) diff --git a/src/preview_thread.py b/src/preview_thread.py index 0a6a856..bb22f0c 100644 --- a/src/preview_thread.py +++ b/src/preview_thread.py @@ -59,7 +59,9 @@ class Worker(QtCore.QObject): components = nextPreviewInformation["components"] for component in reversed(components): try: + component.lockSize(width, height) newFrame = component.previewRender() + component.unlockSize() frame = Image.alpha_composite( frame, newFrame ) -- cgit v1.2.3 From 98a47a21d986ccede574baececd179be7550c9d6 Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 3 Aug 2017 20:43:23 -0400 Subject: save presets as floats so project resolution is not relevant unfortunately this breaks old projects and presets --- src/component.py | 56 ++++++++++++++++++++---- src/components/text.py | 18 ++++---- src/components/text.ui | 114 ++++++++++++++++++++++++++++++------------------- src/core.py | 2 +- 4 files changed, 127 insertions(+), 63 deletions(-) (limited to 'src/components/text.ui') diff --git a/src/component.py b/src/component.py index ea4b5ec..5b38473 100644 --- a/src/component.py +++ b/src/component.py @@ -346,16 +346,29 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): % QColor(*val).name() ) self._colorWidgets[attr].setStyleSheet(btnStyle) + elif attr in self._relativeWidgets: + self._relativeValues[attr] = val + pixelVal = self.pixelValForAttr(attr, val) + setWidgetValue(widget, pixelVal) else: setWidgetValue(widget, val) def savePreset(self): saveValueStore = {} for attr, widget in self._trackedWidgets.items(): - saveValueStore[ + presetAttrName = ( attr if attr not in self._presetNames else self._presetNames[attr] - ] = getattr(self, attr) + ) + if attr in self._relativeWidgets: + try: + val = self._relativeValues[attr] + except AttributeError: + val = self.floatValForAttr(attr) + else: + val = getattr(self, attr) + + saveValueStore[presetAttrName] = val return saveValueStore def commandHelp(self): @@ -490,17 +503,42 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): self.unlockProperties() self.unlockError() + def relativeWidgetAxis(func): + def relativeWidgetAxis(self, attr, *args, **kwargs): + if 'axis' not in kwargs: + axis = self.width + if 'height' in attr.lower() \ + or 'ypos' in attr.lower() or attr == 'y': + axis = self.height + kwargs['axis'] = axis + return func(self, attr, *args, **kwargs) + return relativeWidgetAxis + + @relativeWidgetAxis + def pixelValForAttr(self, attr, val=None, **kwargs): + if val is None: + val = self._relativeValues[attr] + return math.ceil(kwargs['axis'] * val) + + @relativeWidgetAxis + def floatValForAttr(self, attr, val=None, **kwargs): + if val is None: + val = self._trackedWidgets[attr].value() + return val / kwargs['axis'] + + def setRelativeWidget(self, attr, floatVal): + '''Set a relative widget using a float''' + pixelVal = self.pixelValForAttr(attr, floatVal) + self._trackedWidgets[attr].setValue(pixelVal) + + def updateRelativeWidget(self, attr): - dimension = self.width - if 'height' in attr.lower() \ - or 'ypos' in attr.lower() or attr == 'y': - dimension = self.height try: oldUserValue = getattr(self, attr) except AttributeError: oldUserValue = self._trackedWidgets[attr].value() newUserValue = self._trackedWidgets[attr].value() - newRelativeVal = newUserValue / dimension + newRelativeVal = self.floatValForAttr(attr, newUserValue) if attr in self._relativeValues: oldRelativeVal = self._relativeValues[attr] @@ -510,8 +548,8 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): # means the pixel value needs to be updated self._trackedWidgets[attr].blockSignals(True) self.updateRelativeWidgetMaximum(attr) - self._trackedWidgets[attr].setValue( - math.ceil(dimension * oldRelativeVal)) + pixelVal = self.pixelValForAttr(attr, oldRelativeVal) + self._trackedWidgets[attr].setValue(pixelVal) self._trackedWidgets[attr].blockSignals(False) if attr not in self._relativeValues \ diff --git a/src/components/text.py b/src/components/text.py index b7c244e..c3f3bdc 100644 --- a/src/components/text.py +++ b/src/components/text.py @@ -9,7 +9,7 @@ from toolkit.frame import FramePainter class Component(Component): name = 'Title Text' - version = '1.0.0' + version = '1.0.1' def __init__(self, *args): super().__init__(*args) @@ -25,20 +25,17 @@ class Component(Component): self.page.comboBox_textAlign.addItem("Left") self.page.comboBox_textAlign.addItem("Middle") self.page.comboBox_textAlign.addItem("Right") + self.page.comboBox_textAlign.setCurrentIndex(int(self.alignment)) self.page.lineEdit_textColor.setText('%s,%s,%s' % self.textColor) - self.page.lineEdit_title.setText(self.title) - self.page.comboBox_textAlign.setCurrentIndex(int(self.alignment)) self.page.spinBox_fontSize.setValue(int(self.fontSize)) + self.page.lineEdit_title.setText(self.title) - fm = QtGui.QFontMetrics(self.titleFont) - self.page.spinBox_xTextAlign.setValue( - self.width / 2 - fm.width(self.title)/2) - self.page.spinBox_yTextAlign.setValue(self.height / 2 * 1.036) - + self.page.pushButton_center.clicked.connect(self.centerXY) self.page.fontComboBox_titleFont.currentFontChanged.connect( self.update ) + self.trackWidgets({ 'textColor': self.page.lineEdit_textColor, 'title': self.page.lineEdit_title, @@ -51,11 +48,16 @@ class Component(Component): }, relativeWidgets=[ 'xPosition', 'yPosition', 'fontSize', ]) + self.centerXY() def update(self): self.titleFont = self.page.fontComboBox_titleFont.currentFont() super().update() + def centerXY(self): + self.setRelativeWidget('xPosition', 0.5) + self.setRelativeWidget('yPosition', 0.5) + def getXY(self): '''Returns true x, y after considering alignment settings''' fm = QtGui.QFontMetrics(self.titleFont) diff --git a/src/components/text.ui b/src/components/text.ui index bb5e5af..f76979c 100644 --- a/src/components/text.ui +++ b/src/components/text.ui @@ -19,6 +19,36 @@ 4 + + + + + + Title + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Testing New GUI + + + + + @@ -93,38 +123,6 @@ - - - - - 0 - 0 - - - - Text Layout - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 20 - - - - @@ -132,6 +130,9 @@ + + + @@ -152,7 +153,17 @@ - + + + Qt::Horizontal + + + + 40 + 20 + + + @@ -162,28 +173,41 @@ 0 - + + + + 0 + 0 + + - Title + Text Layout - - - - 0 - 0 - + + + + + + Qt::Horizontal - + + QSizePolicy::Fixed + + - 0 - 0 + 5 + 20 + + + + - Testing New GUI + Center diff --git a/src/core.py b/src/core.py index afb1e45..61905eb 100644 --- a/src/core.py +++ b/src/core.py @@ -161,7 +161,7 @@ class Core: for widget, value in data['WindowFields']: widget = eval('loader.window.%s' % widget) widget.blockSignals(True) - widget.setText(value) + toolkit.setWidgetValue(widget, value) widget.blockSignals(False) for key, value in data['Settings']: -- cgit v1.2.3 From 998f74149553ac7a9e27d7c85cebceda2ef32c64 Mon Sep 17 00:00:00 2001 From: tassaron Date: Sun, 6 Aug 2017 21:52:44 -0400 Subject: added stroke and font style options to Text component --- src/component.py | 4 +- src/components/image.ui | 336 +++++++++++++++++----------------- src/components/original.ui | 2 +- src/components/text.py | 65 +++++-- src/components/text.ui | 439 +++++++++++++++++++++++++++++++++++++-------- 5 files changed, 593 insertions(+), 253 deletions(-) (limited to 'src/components/text.ui') diff --git a/src/component.py b/src/component.py index 5b38473..5b6f9a7 100644 --- a/src/component.py +++ b/src/component.py @@ -198,7 +198,7 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): try: preset = self.savePreset() except Exception as e: - preset = '%s occured while saving preset' % str(e) + preset = '%s occurred while saving preset' % str(e) return '%s\n%s\n%s' % ( self.__class__.name, str(self.__class__.version), preset ) @@ -275,7 +275,7 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): 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/colour) and initialize + (e.g., pushButtons to select a file) and initialize ''' self.parent = parent self.settings = parent.settings diff --git a/src/components/image.ui b/src/components/image.ui index e549ed0..1837b64 100644 --- a/src/components/image.ui +++ b/src/components/image.ui @@ -178,177 +178,177 @@ - - - - - - - - Stretch - - - false - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 20 - - - - - - - - Mirror - - - - - - - Rotate - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QAbstractSpinBox::UpDownArrows - - - ° - - - 0 - - - 359 - - - 0 - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - - - - - - 0 - 0 - - - - Scale - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QAbstractSpinBox::UpDownArrows - - - % - - - 10 - - - 400 - - - 100 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + + Stretch + + + false + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Mirror + + + + + + + Rotate + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QAbstractSpinBox::UpDownArrows + + + ° + + + 0 + + + 359 + + + 0 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + + 0 + 0 + + + + Scale + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QAbstractSpinBox::UpDownArrows + + + % + + + 10 + + + 400 + + + 100 + + + + - - - QAbstractSpinBox::UpDownArrows - - - % - - - 0 - - - 999 - - - 1 - - - 100 - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Color + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QAbstractSpinBox::UpDownArrows + + + % + + + 0 + + + 999 + + + 1 + + + 100 + + + + diff --git a/src/components/original.ui b/src/components/original.ui index 8fa9b2b..a4d5119 100644 --- a/src/components/original.ui +++ b/src/components/original.ui @@ -6,7 +6,7 @@ 0 0 - 633 + 586 178 diff --git a/src/components/text.py b/src/components/text.py index c3f3bdc..f88f373 100644 --- a/src/components/text.py +++ b/src/components/text.py @@ -4,22 +4,20 @@ from PyQt5 import QtGui, QtCore, QtWidgets import os from component import Component -from toolkit.frame import FramePainter +from toolkit.frame import FramePainter, PaintColor class Component(Component): name = 'Title Text' version = '1.0.1' - def __init__(self, *args): - super().__init__(*args) - self.titleFont = QFont() - def widget(self, *args): super().widget(*args) self.textColor = (255, 255, 255) + self.strokeColor = (0, 0, 0) self.title = 'Text' self.alignment = 1 + self.titleFont = QFont() self.fontSize = self.height / 13.5 self.page.comboBox_textAlign.addItem("Left") @@ -28,6 +26,7 @@ class Component(Component): self.page.comboBox_textAlign.setCurrentIndex(int(self.alignment)) self.page.lineEdit_textColor.setText('%s,%s,%s' % self.textColor) + self.page.lineEdit_strokeColor.setText('%s,%s,%s' % self.strokeColor) self.page.spinBox_fontSize.setValue(int(self.fontSize)) self.page.lineEdit_title.setText(self.title) @@ -43,8 +42,16 @@ class Component(Component): 'fontSize': self.page.spinBox_fontSize, 'xPosition': self.page.spinBox_xTextAlign, 'yPosition': self.page.spinBox_yTextAlign, + 'fontStyle': self.page.comboBox_fontStyle, + 'stroke': self.page.spinBox_stroke, + 'strokeColor': self.page.lineEdit_strokeColor, + 'shadow': self.page.checkBox_shadow, + 'shadX': self.page.spinBox_shadX, + 'shadY': self.page.spinBox_shadY, + 'shadBlur': self.page.spinBox_shadBlur, }, colorWidgets={ 'textColor': self.page.pushButton_textColor, + 'strokeColor': self.page.pushButton_strokeColor, }, relativeWidgets=[ 'xPosition', 'yPosition', 'fontSize', ]) @@ -52,11 +59,23 @@ class Component(Component): def update(self): self.titleFont = self.page.fontComboBox_titleFont.currentFont() + if self.page.checkBox_shadow.isChecked(): + self.page.label_shadX.setHidden(False) + self.page.spinBox_shadX.setHidden(False) + self.page.spinBox_shadY.setHidden(False) + self.page.label_shadBlur.setHidden(False) + self.page.spinBox_shadBlur.setHidden(False) + else: + self.page.label_shadX.setHidden(True) + self.page.spinBox_shadX.setHidden(True) + self.page.spinBox_shadY.setHidden(True) + self.page.label_shadBlur.setHidden(True) + self.page.spinBox_shadBlur.setHidden(True) super().update() def centerXY(self): self.setRelativeWidget('xPosition', 0.5) - self.setRelativeWidget('yPosition', 0.5) + self.setRelativeWidget('yPosition', 0.521) def getXY(self): '''Returns true x, y after considering alignment settings''' @@ -101,13 +120,39 @@ class Component(Component): return self.addText(self.width, self.height) def addText(self, width, height): + font = self.titleFont + font.setPixelSize(self.fontSize) + font.setStyle(QFont.StyleNormal) + font.setWeight(QFont.Normal) + font.setCapitalization(QFont.MixedCase) + if self.fontStyle == 1: + font.setWeight(QFont.DemiBold) + if self.fontStyle == 2: + font.setWeight(QFont.Bold) + elif self.fontStyle == 3: + font.setStyle(QFont.StyleItalic) + elif self.fontStyle == 4: + font.setWeight(QFont.Bold) + font.setStyle(QFont.StyleItalic) + elif self.fontStyle == 5: + font.setStyle(QFont.StyleOblique) + elif self.fontStyle == 6: + font.setCapitalization(QFont.SmallCaps) + image = FramePainter(width, height) - self.titleFont.setPixelSize(self.fontSize) - image.setFont(self.titleFont) - image.setPen(self.textColor) x, y = self.getXY() + if self.stroke > 0: + outliner = QtGui.QPainterPathStroker() + outliner.setWidth(self.stroke) + path = QtGui.QPainterPath() + path.addText(x, y, font, self.title) + path = outliner.createStroke(path) + image.setBrush(PaintColor(*self.strokeColor)) + image.drawPath(path) + + image.setFont(font) + image.setPen(self.textColor) image.drawText(x, y, self.title) - return image.finalize() def commandHelp(self): diff --git a/src/components/text.ui b/src/components/text.ui index f76979c..5a7e831 100644 --- a/src/components/text.ui +++ b/src/components/text.ui @@ -16,6 +16,12 @@ + + 6 + + + QLayout::SetDefaultConstraint + 4 @@ -31,7 +37,7 @@ - + 0 0 @@ -47,14 +53,10 @@ - - - - - + 0 0 @@ -67,7 +69,7 @@ - + 0 0 @@ -80,8 +82,44 @@ + + + + + + 0 + - + + + + 0 + 0 + + + + Text Layout + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + Qt::Horizontal @@ -97,7 +135,36 @@ - + + + + 0 + 0 + + + + Center Text + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + 0 @@ -105,36 +172,104 @@ - Font Size + X - + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + 0 + 0 + + - 1 + 0 - 500 + 999999999 + + + 0 + + + + + + + + 0 + 0 + + + + Y + + + + + + + + 0 + 0 + + + + + 50 + 16777215 + + + + 999999999 - + + + + 0 + 0 + + + + + 16777215 + 16777215 + + Text Color - - - + + + 0 + 0 + + 32 @@ -153,27 +288,23 @@ - + Qt::Horizontal + + QSizePolicy::Fixed + - 40 + 5 20 - - - - - - 0 - - + 0 @@ -181,15 +312,34 @@ - Text Layout + Font Size - + + + + 0 + 0 + + + + + + + + + + 1 + + + 500 + + - + Qt::Horizontal @@ -205,30 +355,82 @@ - + + + + 0 + 0 + + - Center + Font Style - - - Qt::Horizontal - - - QSizePolicy::Fixed + + + + Normal + + + + + Semi-Bold + + + + + Bold + + + + + Italic + + + + + Bold Italic + + + + + Faux Italic + + + + + Small Caps + + + + + + + + + + + + + 0 + 0 + - + - 5 - 20 + 0 + 16777215 - + + Qt::NoFocus + + - + 0 @@ -236,59 +438,112 @@ - X + Stroke - + - + + 0 + 0 + + + + px + + + + + + + + 0 + 0 + + + + Stroke Color + + + + + + + 0 0 - 80 + 0 16777215 - + + Qt::NoFocus + + + + + + + + 0 + 0 + + + - 0 - 0 + 32 + 32 - - 0 - - - 999999999 + + - - 0 + + + 32 + 32 + - + Qt::Horizontal - - QSizePolicy::Fixed - - 5 + 40 20 + + + + - + + + + 0 + 0 + + + + Shadow + + + + + 0 @@ -296,29 +551,69 @@ - Y + Shadow Offset - + 0 0 - - - 80 - 16777215 - + + + + + + + 0 + 0 + - - 999999999 + + + + + + + 0 + 0 + + + + Shadow Blur + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 40 + 20 + + + + -- cgit v1.2.3 From 060a7dc2d263c0fd0e36e162943b8946df937bbd Mon Sep 17 00:00:00 2001 From: tassaron Date: Mon, 7 Aug 2017 21:03:01 -0400 Subject: dropshadow option for Text component --- src/components/text.py | 16 ++++++++++++++-- src/components/text.ui | 27 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'src/components/text.ui') diff --git a/src/components/text.py b/src/components/text.py index f88f373..c50c812 100644 --- a/src/components/text.py +++ b/src/components/text.py @@ -1,4 +1,4 @@ -from PIL import Image, ImageDraw +from PIL import ImageEnhance, ImageFilter, ImageChops from PyQt5.QtGui import QColor, QFont from PyQt5 import QtGui, QtCore, QtWidgets import os @@ -153,7 +153,19 @@ class Component(Component): image.setFont(font) image.setPen(self.textColor) image.drawText(x, y, self.title) - return image.finalize() + + # turn QImage into Pillow frame + frame = image.finalize() + if self.shadow: + shadImg = ImageEnhance.Contrast(frame).enhance(0.0) + shadImg = shadImg.filter(ImageFilter.GaussianBlur(self.shadBlur)) + shadImg = ImageChops.offset(shadImg, self.shadX, self.shadY) + shadImg.paste(frame, box=(0, 0), mask=frame) + frame = shadImg + + return frame + + def commandHelp(self): print('Enter a string to use as centred white text:') diff --git a/src/components/text.ui b/src/components/text.ui index 5a7e831..13d3467 100644 --- a/src/components/text.ui +++ b/src/components/text.ui @@ -563,6 +563,15 @@ 0 + + -1000 + + + 1000 + + + -4 + @@ -573,6 +582,15 @@ 0 + + -1000 + + + 1000 + + + 8 + @@ -596,6 +614,15 @@ 0 + + 99.000000000000000 + + + 0.100000000000000 + + + 5.000000000000000 + -- cgit v1.2.3 From a1d7cbb984f2a6c2ea976daa8914a2c9845ee21c Mon Sep 17 00:00:00 2001 From: tassaron Date: Tue, 15 Aug 2017 22:20:25 -0400 Subject: undoable edits for normal component settings; TODO: merge small edits --- src/background.png | Bin 45367 -> 0 bytes src/component.py | 77 +++++++++++++++++++++++++++++++++++++++++------- src/components/color.py | 3 -- src/components/color.ui | 6 ++++ src/components/text.py | 4 --- src/components/text.ui | 6 ++++ src/core.py | 20 ++++++++----- src/gui/background.png | Bin 0 -> 45367 bytes src/gui/mainwindow.py | 34 +++++++++++++++------ src/toolkit/common.py | 12 ++++++++ src/toolkit/frame.py | 2 +- 11 files changed, 130 insertions(+), 34 deletions(-) delete mode 100644 src/background.png create mode 100644 src/gui/background.png (limited to 'src/components/text.ui') diff --git a/src/background.png b/src/background.png deleted file mode 100644 index fb58593..0000000 Binary files a/src/background.png and /dev/null differ diff --git a/src/component.py b/src/component.py index 0e5144c..dcba082 100644 --- a/src/component.py +++ b/src/component.py @@ -12,7 +12,7 @@ import logging from toolkit.frame import BlankFrame from toolkit import ( - getWidgetValue, setWidgetValue, connectWidget, rgbFromString + getWidgetValue, setWidgetValue, connectWidget, rgbFromString, blockSignals ) @@ -305,14 +305,46 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): def update(self): ''' - Reads all tracked widget values into instance attributes - and tells the MainWindow that the component was modified. - Call super() at the END if you need to subclass this. + A component update triggered by the user changing a widget value + Call super() at the END when subclassing this. ''' - for attr, widget in self._trackedWidgets.items(): + oldWidgetVals = { + attr: getattr(self, attr) + for attr in self._trackedWidgets + } + newWidgetVals = { + attr: getWidgetValue(widget) + if attr not in self._colorWidgets else rgbFromString(widget.text()) + for attr, widget in self._trackedWidgets.items() + } + if any([val != oldWidgetVals[attr] + for attr, val in newWidgetVals.items() + ]): + action = ComponentUpdate(self, oldWidgetVals, newWidgetVals) + self.parent.undoStack.push(action) + + def _update(self): + '''An internal component update that is not undoable''' + + newWidgetVals = { + attr: getWidgetValue(widget) + for attr, widget in self._trackedWidgets.items() + } + self.setAttrs(newWidgetVals) + self.sendUpdateSignal() + + def setAttrs(self, attrDict): + ''' + Sets attrs (linked to trackedWidgets) in this preset 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: text stored as tuple & update the button color - rgbTuple = rgbFromString(widget.text()) + if type(val) is tuple: + rgbTuple = val + else: + rgbTuple = rgbFromString(val) btnStyle = ( "QPushButton { background-color : %s; outline: none; }" % QColor(*rgbTuple).name()) @@ -322,12 +354,11 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): elif attr in self._relativeWidgets: # Relative widgets: number scales to fit export resolution self.updateRelativeWidget(attr) - setattr(self, attr, self._trackedWidgets[attr].value()) + setattr(self, attr, val) else: # Normal tracked widget - setattr(self, attr, getWidgetValue(widget)) - self.sendUpdateSignal() + setattr(self, attr, val) def sendUpdateSignal(self): if not self.core.openingProject: @@ -541,7 +572,6 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): pixelVal = self.pixelValForAttr(attr, floatVal) self._trackedWidgets[attr].setValue(pixelVal) - def updateRelativeWidget(self, attr): try: oldUserValue = getattr(self, attr) @@ -628,3 +658,30 @@ class ComponentError(RuntimeError): super().__init__(string) caller.lockError(string) caller._error.emit(string, detail) + + +class ComponentUpdate(QtWidgets.QUndoCommand): + '''Command object for making a component action undoable''' + def __init__(self, parent, oldWidgetVals, newWidgetVals): + super().__init__( + 'Changed %s component #%s' % ( + parent.name, parent.compPos + ) + ) + self.parent = parent + self.oldWidgetVals = oldWidgetVals + self.newWidgetVals = newWidgetVals + + def redo(self): + self.parent.setAttrs(self.newWidgetVals) + self.parent.sendUpdateSignal() + + def undo(self): + self.parent.setAttrs(self.oldWidgetVals) + with blockSignals(self.parent): + for attr, widget in self.parent._trackedWidgets.items(): + val = self.oldWidgetVals[attr] + if attr in self.parent._colorWidgets: + val = '%s,%s,%s' % val + setWidgetValue(widget, val) + self.parent.sendUpdateSignal() diff --git a/src/components/color.py b/src/components/color.py index 5d1233e..d09cee8 100644 --- a/src/components/color.py +++ b/src/components/color.py @@ -17,9 +17,6 @@ class Component(Component): self.y = 0 super().widget(*args) - self.page.lineEdit_color1.setText('0,0,0') - self.page.lineEdit_color2.setText('133,133,133') - # disable color #2 until non-default 'fill' option gets changed self.page.lineEdit_color2.setDisabled(True) self.page.pushButton_color2.setDisabled(True) diff --git a/src/components/color.ui b/src/components/color.ui index a9dacea..1865e60 100644 --- a/src/components/color.ui +++ b/src/components/color.ui @@ -73,6 +73,9 @@ 0 + + 0,0,0 + 12 @@ -146,6 +149,9 @@ 0 + + 133,133,133 + 12 diff --git a/src/components/text.py b/src/components/text.py index 4d4f5d3..d3afd5c 100644 --- a/src/components/text.py +++ b/src/components/text.py @@ -13,8 +13,6 @@ class Component(Component): def widget(self, *args): super().widget(*args) - self.textColor = (255, 255, 255) - self.strokeColor = (0, 0, 0) self.title = 'Text' self.alignment = 1 self.titleFont = QFont() @@ -25,8 +23,6 @@ class Component(Component): self.page.comboBox_textAlign.addItem("Right") self.page.comboBox_textAlign.setCurrentIndex(int(self.alignment)) - self.page.lineEdit_textColor.setText('%s,%s,%s' % self.textColor) - self.page.lineEdit_strokeColor.setText('%s,%s,%s' % self.strokeColor) self.page.spinBox_fontSize.setValue(int(self.fontSize)) self.page.lineEdit_title.setText(self.title) diff --git a/src/components/text.ui b/src/components/text.ui index 13d3467..b62e0ed 100644 --- a/src/components/text.ui +++ b/src/components/text.ui @@ -427,6 +427,9 @@ Qt::NoFocus + + 255,255,255 + @@ -485,6 +488,9 @@ Qt::NoFocus + + 0,0,0 + diff --git a/src/core.py b/src/core.py index 20b9c1d..cee0f56 100644 --- a/src/core.py +++ b/src/core.py @@ -94,12 +94,11 @@ class Core: compPos, component ) - self.componentListChanged() - if moduleIndex > -1: - self.updateComponent(compPos) - if hasattr(loader, 'insertComponent'): loader.insertComponent(compPos) + + self.componentListChanged() + self.updateComponent(compPos) return compPos def moveComponent(self, startI, endI): @@ -119,7 +118,7 @@ class Core: def updateComponent(self, i): log.debug('Updating %s #%s' % (self.selectedComponents[i], str(i))) - self.selectedComponents[i].update() + self.selectedComponents[i]._update() def moduleIndexFor(self, compName): try: @@ -540,6 +539,7 @@ class Core: "projectDir": os.path.join(cls.dataDir, 'projects'), "pref_insertCompAtTop": True, "pref_genericPreview": True, + "pref_undoLimit": 10, } for parm, value in cls.defaultSettings.items(): @@ -552,8 +552,14 @@ class Core: if not key.startswith('pref_'): continue val = cls.settings.value(key) - if val in ('true', 'false'): - cls.settings.setValue(key, True if val == 'true' else False) + try: + val = int(val) + except ValueError: + if val == 'true': + val = True + elif val == 'false': + val = False + cls.settings.setValue(key, val) @staticmethod def makeLogger(): diff --git a/src/gui/background.png b/src/gui/background.png new file mode 100644 index 0000000..fb58593 Binary files /dev/null and b/src/gui/background.png differ diff --git a/src/gui/mainwindow.py b/src/gui/mainwindow.py index 2edb750..47111a0 100644 --- a/src/gui/mainwindow.py +++ b/src/gui/mainwindow.py @@ -42,13 +42,22 @@ class MainWindow(QtWidgets.QMainWindow): def __init__(self, window, project): QtWidgets.QMainWindow.__init__(self) + log.debug( + 'Main thread id: {}'.format(int(QtCore.QThread.currentThreadId()))) self.window = window self.core = Core() Core.mode = 'GUI' - log.debug( - 'Main thread id: {}'.format(int(QtCore.QThread.currentThreadId()))) + # 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 = QtWidgets.QUndoStack(self) + undoLimit = self.settings.value("pref_undoLimit") + self.undoStack.setUndoLimit(undoLimit) # widgets of component settings self.pages = [] @@ -58,12 +67,6 @@ class MainWindow(QtWidgets.QMainWindow): 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 - self.presetManager = PresetManager( uic.loadUi( os.path.join(Core.wd, 'gui', 'presetmanager.ui')), self) @@ -302,6 +305,7 @@ class MainWindow(QtWidgets.QMainWindow): QtWidgets.QShortcut("Ctrl+A", self.window, self.openSaveProjectDialog) QtWidgets.QShortcut("Ctrl+O", self.window, self.openOpenProjectDialog) QtWidgets.QShortcut("Ctrl+N", self.window, self.createNewProject) + QtWidgets.QShortcut("Ctrl+Z", self.window, self.undoStack.undo) QtWidgets.QShortcut("Ctrl+Y", self.window, self.undoStack.redo) QtWidgets.QShortcut("Ctrl+Shift+Z", self.window, self.undoStack.redo) @@ -353,6 +357,9 @@ class MainWindow(QtWidgets.QMainWindow): QtWidgets.QShortcut( "Ctrl+Alt+Shift+F", self.window, self.showFfmpegCommand ) + QtWidgets.QShortcut( + "Ctrl+Alt+Shift+U", self.window, self.showUndoStack + ) @QtCore.pyqtSlot() def cleanUp(self, *args): @@ -658,6 +665,14 @@ class MainWindow(QtWidgets.QMainWindow): def showPreviewImage(self, image): self.previewWindow.changePixmap(image) + def showUndoStack(self): + dialog = QtWidgets.QDialog(self.window) + undoView = QtWidgets.QUndoView(self.undoStack) + layout = QtWidgets.QVBoxLayout() + layout.addWidget(undoView) + dialog.setLayout(layout) + dialog.show() + def showFfmpegCommand(self): from textwrap import wrap from toolkit.ffmpeg import createFfmpegCommand @@ -784,6 +799,7 @@ class MainWindow(QtWidgets.QMainWindow): field.blockSignals(False) self.progressBarUpdated(0) self.progressBarSetText('') + self.undoStack.clear() @disableWhenEncoding def createNewProject(self, prompt=True): @@ -847,7 +863,7 @@ class MainWindow(QtWidgets.QMainWindow): def openProject(self, filepath, prompt=True): if not filepath or not os.path.exists(filepath) \ - or not filepath.endswith('.avp'): + or not filepath.endswith('.avp'): return self.clear() diff --git a/src/toolkit/common.py b/src/toolkit/common.py index eba57d9..51ad023 100644 --- a/src/toolkit/common.py +++ b/src/toolkit/common.py @@ -9,6 +9,18 @@ import subprocess from collections import OrderedDict +class blockSignals: + '''A context manager to temporarily block a Qt widget from updating''' + def __init__(self, widget): + self.widget = widget + + def __enter__(self): + self.widget.blockSignals(True) + + def __exit__(self, *args): + self.widget.blockSignals(False) + + def badName(name): '''Returns whether a name contains non-alphanumeric chars''' return any([letter in string.punctuation for letter in name]) diff --git a/src/toolkit/frame.py b/src/toolkit/frame.py index ad8537c..2104978 100644 --- a/src/toolkit/frame.py +++ b/src/toolkit/frame.py @@ -98,7 +98,7 @@ def Checkerboard(width, height): log.debug('Creating new %s*%s checkerboard' % (width, height)) image = FloodFrame(1920, 1080, (0, 0, 0, 0)) image.paste(Image.open( - os.path.join(core.Core.wd, "background.png")), + os.path.join(core.Core.wd, 'gui', "background.png")), (0, 0) ) image = image.resize((width, height)) -- cgit v1.2.3