diff options
| author | Aeliton G. Silva | 2026-01-12 22:39:55 -0300 |
|---|---|---|
| committer | Aeliton G. Silva | 2026-01-13 04:22:25 -0300 |
| commit | f975144f25d34f97329b2d4e52891061573cea08 (patch) | |
| tree | 226fe223b31af6f217b1dd413629ab2cf26964d4 /src/components | |
| parent | b8703752ffc7768b0275897b3c2a869ff41504e5 (diff) | |
Use pyproject.toml + uv_build
This replaces setup.py by a modern pyproject.toml using uv_build
backend.
Dependencies are being also managed by uv, so to install dependencies
and run the project one can execute:
```
uv sync
uv run pytest # optional
python -m avp
```
To build the both source and binary (wheel) distribution package run:
```
uv build
```
Uv can be installed with `pip install uv`.
The directory structure has been changed to reflect best practices.
- src/* -> src/avp/
- src/tests -> ../tests
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/__init__.py | 1 | ||||
| -rw-r--r-- | src/components/__template__.ui | 119 | ||||
| -rw-r--r-- | src/components/color.py | 176 | ||||
| -rw-r--r-- | src/components/color.ui | 666 | ||||
| -rw-r--r-- | src/components/image.py | 129 | ||||
| -rw-r--r-- | src/components/image.ui | 388 | ||||
| -rw-r--r-- | src/components/life.py | 520 | ||||
| -rw-r--r-- | src/components/life.ui | 405 | ||||
| -rw-r--r-- | src/components/original.py | 243 | ||||
| -rw-r--r-- | src/components/original.ui | 243 | ||||
| -rw-r--r-- | src/components/sound.py | 77 | ||||
| -rw-r--r-- | src/components/sound.ui | 172 | ||||
| -rw-r--r-- | src/components/spectrum.py | 368 | ||||
| -rw-r--r-- | src/components/spectrum.ui | 946 | ||||
| -rw-r--r-- | src/components/text.py | 218 | ||||
| -rw-r--r-- | src/components/text.ui | 671 | ||||
| -rw-r--r-- | src/components/video.py | 254 | ||||
| -rw-r--r-- | src/components/video.ui | 328 | ||||
| -rw-r--r-- | src/components/waveform.py | 230 | ||||
| -rw-r--r-- | src/components/waveform.ui | 383 |
20 files changed, 0 insertions, 6537 deletions
diff --git a/src/components/__init__.py b/src/components/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/src/components/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/components/__template__.ui b/src/components/__template__.ui deleted file mode 100644 index 301a2b7..0000000 --- a/src/components/__template__.ui +++ /dev/null @@ -1,119 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <item> - <spacer name="horizontalSpacer_6"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <item> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <item> - <spacer name="horizontalSpacer_4"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/color.py b/src/components/color.py deleted file mode 100644 index 1f32c23..0000000 --- a/src/components/color.py +++ /dev/null @@ -1,176 +0,0 @@ -from PyQt6 import QtGui -import logging - -from ..component import Component -from ..toolkit.frame import BlankFrame, FloodFrame, FramePainter, PaintColor - - -log = logging.getLogger("AVP.Components.Color") - - -class Component(Component): - name = "Color" - version = "1.0.0" - - def widget(self, *args): - self.x = 0 - self.y = 0 - super().widget(*args) - - # disable color #2 until non-default 'fill' option gets changed - self.page.lineEdit_color2.setDisabled(True) - self.page.pushButton_color2.setDisabled(True) - self.page.spinBox_width.setValue(int(self.settings.value("outputWidth"))) - self.page.spinBox_height.setValue(int(self.settings.value("outputHeight"))) - - self.fillLabels = [ - "Solid", - "Linear Gradient", - "Radial Gradient", - ] - for label in self.fillLabels: - self.page.comboBox_fill.addItem(label) - self.page.comboBox_fill.setCurrentIndex(0) - - self.trackWidgets( - { - "x": self.page.spinBox_x, - "y": self.page.spinBox_y, - "sizeWidth": self.page.spinBox_width, - "sizeHeight": self.page.spinBox_height, - "trans": self.page.checkBox_trans, - "spread": self.page.comboBox_spread, - "stretch": self.page.checkBox_stretch, - "RG_start": self.page.spinBox_radialGradient_start, - "LG_start": self.page.spinBox_linearGradient_start, - "RG_end": self.page.spinBox_radialGradient_end, - "LG_end": self.page.spinBox_linearGradient_end, - "RG_centre": self.page.spinBox_radialGradient_spread, - "fillType": self.page.comboBox_fill, - "color1": self.page.lineEdit_color1, - "color2": self.page.lineEdit_color2, - }, - presetNames={ - "sizeWidth": "width", - "sizeHeight": "height", - }, - colorWidgets={ - "color1": self.page.pushButton_color1, - "color2": self.page.pushButton_color2, - }, - relativeWidgets=[ - "x", - "y", - "sizeWidth", - "sizeHeight", - "LG_start", - "LG_end", - "RG_start", - "RG_end", - "RG_centre", - ], - ) - - def update(self): - fillType = self.page.comboBox_fill.currentIndex() - if fillType == 0: - self.page.lineEdit_color2.setEnabled(False) - self.page.pushButton_color2.setEnabled(False) - self.page.checkBox_trans.setEnabled(False) - self.page.checkBox_stretch.setEnabled(False) - self.page.comboBox_spread.setEnabled(False) - else: - self.page.lineEdit_color2.setEnabled(True) - self.page.pushButton_color2.setEnabled(True) - self.page.checkBox_trans.setEnabled(True) - self.page.checkBox_stretch.setEnabled(True) - self.page.comboBox_spread.setEnabled(True) - if self.page.checkBox_trans.isChecked(): - self.page.lineEdit_color2.setEnabled(False) - self.page.pushButton_color2.setEnabled(False) - self.page.fillWidget.setCurrentIndex(fillType) - - def previewRender(self): - return self.drawFrame(self.width, self.height) - - def properties(self): - return ["static"] - - def frameRender(self, frameNo): - log.debug("Color component is drawing frame #%s", frameNo) - return self.drawFrame(self.width, self.height) - - def drawFrame(self, width, height): - r, g, b = self.color1 - shapeSize = (self.sizeWidth, self.sizeHeight) - # in default state, skip all this logic and return a plain fill - if ( - self.fillType == 0 - and shapeSize == (width, height) - and self.x == 0 - and self.y == 0 - ): - return FloodFrame(width, height, (r, g, b, 255)) - - # Return a solid image at x, y - if self.fillType == 0: - frame = BlankFrame(width, height) - image = FloodFrame(self.sizeWidth, self.sizeHeight, (r, g, b, 255)) - frame.paste(image, box=(self.x, self.y)) - return frame - - # Now fills that require using Qt... - elif self.fillType > 0: - image = FramePainter(width, height) - - if self.stretch: - w = width - h = height - else: - w = self.sizeWidth - h = self.sizeWidth - - if self.fillType == 1: # Linear Gradient - brush = QtGui.QLinearGradient( - float(self.LG_start), - float(self.LG_start), - float(self.LG_end + width / 3), - float(self.LG_end), - ) - - elif self.fillType == 2: # Radial Gradient - brush = QtGui.QRadialGradient( - float(self.RG_start), - float(self.RG_end), - float(w), - float(h), - float(self.RG_centre), - ) - spread = QtGui.QGradient.Spread.PadSpread - if self.spread == 1: - spread = QtGui.QGradient.Spread.ReflectSpread - elif self.spread == 2: - spread = QtGui.QGradient.Spread.RepeatSpread - brush.setSpread(spread) - brush.setColorAt(0.0, PaintColor(*self.color1)) - if self.trans: - brush.setColorAt(1.0, PaintColor(0, 0, 0, 0)) - elif self.fillType == 1 and self.stretch: - brush.setColorAt(0.2, PaintColor(*self.color2)) - else: - brush.setColorAt(1.0, PaintColor(*self.color2)) - image.setBrush(brush) - image.drawRect(self.x, self.y, self.sizeWidth, self.sizeHeight) - - return image.finalize() - - def commandHelp(self): - print("Specify a color:\n color=255,255,255") - - def command(self, arg): - if "=" in arg: - key, arg = arg.split("=", 1) - if key == "color": - self.page.lineEdit_color1.setText(arg) - return - super().command(arg) diff --git a/src/components/color.ui b/src/components/color.ui deleted file mode 100644 index c1713fb..0000000 --- a/src/components/color.ui +++ /dev/null @@ -1,666 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>31</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Color #1</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_color1"> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="MaximumSize" stdset="0"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_color1"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>1</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>0,0,0</string> - </property> - <property name="maxLength"> - <number>12</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_9"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_textColor_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>31</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Color #2</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_color2"> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="MaximumSize" stdset="0"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_color2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>1</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>133,133,133</string> - </property> - <property name="maxLength"> - <number>12</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <property name="leftMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="label_xTitleAlign_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Width</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_width"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>19200</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_yTitleAlign_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Height</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_height"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="maximum"> - <number>10800</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_7"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_xTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>X</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_x"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_yTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Y</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_y"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <property name="leftMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="label_textLayout"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Fill </string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_fill"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="currentIndex"> - <number>-1</number> - </property> - <property name="sizeAdjustPolicy"> - <enum>QComboBox::AdjustToContentsOnFirstShow</enum> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_trans"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Transparent</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_stretch"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Stretch</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_spread"> - <item> - <property name="text"> - <string>Pad</string> - </property> - </item> - <item> - <property name="text"> - <string>Reflect</string> - </property> - </item> - <item> - <property name="text"> - <string>Repeat</string> - </property> - </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Minimum</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QStackedWidget" name="fillWidget"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="lineWidth"> - <number>0</number> - </property> - <property name="currentIndex"> - <number>2</number> - </property> - <widget class="QWidget" name="blank"/> - <widget class="QWidget" name="linearGradient"> - <widget class="QWidget" name="horizontalLayoutWidget"> - <property name="geometry"> - <rect> - <x>-1</x> - <y>0</y> - <width>561</width> - <height>31</height> - </rect> - </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QLabel" name="label_xTitleAlign_4"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Start</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_linearGradient_start"> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="singleStep"> - <number>10</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>End</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_linearGradient_end"> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="singleStep"> - <number>10</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - </widget> - <widget class="QWidget" name="radialGradient"> - <widget class="QWidget" name="horizontalLayoutWidget_3"> - <property name="geometry"> - <rect> - <x>-1</x> - <y>-1</y> - <width>561</width> - <height>31</height> - </rect> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <item> - <widget class="QLabel" name="label_xTitleAlign_6"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Start</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_radialGradient_start"> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="singleStep"> - <number>10</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_3"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>End</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_radialGradient_end"> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="singleStep"> - <number>10</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_4"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Centre</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_radialGradient_spread"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::PlusMinus</enum> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="value"> - <number>3</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - </widget> - </widget> - </item> - </layout> - </item> - </layout> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/image.py b/src/components/image.py deleted file mode 100644 index 2393611..0000000 --- a/src/components/image.py +++ /dev/null @@ -1,129 +0,0 @@ -from PIL import Image, ImageDraw, ImageEnhance -from PyQt6 import QtGui, QtCore, QtWidgets -import os - -from ..component import Component -from ..toolkit.frame import BlankFrame - - -class Component(Component): - name = "Image" - version = "1.0.1" - - def widget(self, *args): - super().widget(*args) - self.page.pushButton_image.clicked.connect(self.pickImage) - self.trackWidgets( - { - "imagePath": self.page.lineEdit_image, - "scale": self.page.spinBox_scale, - "stretchScale": self.page.spinBox_scale_stretch, - "rotate": self.page.spinBox_rotate, - "color": self.page.spinBox_color, - "xPosition": self.page.spinBox_x, - "yPosition": self.page.spinBox_y, - "stretched": self.page.checkBox_stretch, - "mirror": self.page.checkBox_mirror, - }, - presetNames={ - "imagePath": "image", - "xPosition": "x", - "yPosition": "y", - }, - relativeWidgets=["xPosition", "yPosition", "scale"], - ) - - def previewRender(self): - return self.drawFrame(self.width, self.height) - - def properties(self): - props = ["static"] - if not os.path.exists(self.imagePath): - props.append("error") - return props - - def error(self): - if not self.imagePath: - return "There is no image selected." - if not os.path.exists(self.imagePath): - return "The image selected does not exist!" - - def frameRender(self, frameNo): - return self.drawFrame(self.width, self.height) - - def drawFrame(self, width, height): - frame = BlankFrame(width, height) - if self.imagePath and os.path.exists(self.imagePath): - scale = self.scale if not self.stretched else self.stretchScale - image = Image.open(self.imagePath) - - # Modify image's appearance - if self.color != 100: - image = ImageEnhance.Color(image).enhance(float(self.color / 100)) - if self.mirror: - image = image.transpose(Image.Transpose.FLIP_LEFT_RIGHT) - if self.stretched and image.size != (width, height): - image = image.resize((width, height), Image.Resampling.LANCZOS) - if scale != 100: - newHeight = int((image.height / 100) * scale) - newWidth = int((image.width / 100) * scale) - image = image.resize((newWidth, newHeight), Image.Resampling.LANCZOS) - - # Paste image at correct position - frame.paste(image, box=(self.xPosition, self.yPosition)) - if self.rotate != 0: - frame = frame.rotate(self.rotate) - - return frame - - def pickImage(self): - imgDir = self.settings.value("componentDir", os.path.expanduser("~")) - filename, _ = QtWidgets.QFileDialog.getOpenFileName( - self.page, - "Choose Image", - imgDir, - "Image Files (%s)" % " ".join(self.core.imageFormats), - ) - if filename: - self.settings.setValue("componentDir", os.path.dirname(filename)) - self.mergeUndo = False - self.page.lineEdit_image.setText(filename) - self.mergeUndo = True - - def command(self, arg): - if "=" in arg: - key, arg = arg.split("=", 1) - if key == "path" and os.path.exists(arg): - try: - Image.open(arg) - self.page.lineEdit_image.setText(arg) - self.page.checkBox_stretch.setChecked(True) - return - except OSError as e: - print("Not a supported image format") - quit(1) - super().command(arg) - - def commandHelp(self): - print("Load an image:\n path=/filepath/to/image.png") - - def savePreset(self): - # Maintain the illusion that the scale spinbox is one widget - scaleBox = self.page.spinBox_scale - stretchScaleBox = self.page.spinBox_scale_stretch - if self.page.checkBox_stretch.isChecked(): - scaleBox.setValue(stretchScaleBox.value()) - else: - stretchScaleBox.setValue(scaleBox.value()) - return super().savePreset() - - def update(self): - # Maintain the illusion that the scale spinbox is one widget - scaleBox = self.page.spinBox_scale - stretchScaleBox = self.page.spinBox_scale_stretch - if self.page.checkBox_stretch.isChecked(): - scaleBox.setVisible(False) - stretchScaleBox.setVisible(True) - else: - scaleBox.setVisible(True) - stretchScaleBox.setVisible(False) diff --git a/src/components/image.ui b/src/components/image.ui deleted file mode 100644 index 2dad127..0000000 --- a/src/components/image.ui +++ /dev/null @@ -1,388 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>31</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Image</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_image"> - <property name="minimumSize"> - <size> - <width>1</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_image"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>1</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="MaximumSize" stdset="0"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_9"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_xTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>X</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_x"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_yTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Y</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_y"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="minimum"> - <number>-1000</number> - </property> - <property name="maximum"> - <number>1000</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <item> - <widget class="QCheckBox" name="checkBox_stretch"> - <property name="text"> - <string>Stretch</string> - </property> - <property name="checked"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_10"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="checkBox_mirror"> - <property name="text"> - <string>Mirror</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Rotate</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_rotate"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::UpDownArrows</enum> - </property> - <property name="suffix"> - <string notr="true">°</string> - </property> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>359</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>10</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Scale</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_scale"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::UpDownArrows</enum> - </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>10</number> - </property> - <property name="maximum"> - <number>400</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_scale_stretch"> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>10</number> - </property> - <property name="maximum"> - <number>400</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_3"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Color</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_color"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::UpDownArrows</enum> - </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>999</number> - </property> - <property name="singleStep"> - <number>1</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/life.py b/src/components/life.py deleted file mode 100644 index 5b719d1..0000000 --- a/src/components/life.py +++ /dev/null @@ -1,520 +0,0 @@ -from PyQt6 import QtGui, QtCore, QtWidgets -from PyQt6.QtGui import QUndoCommand -from PIL import Image, ImageDraw, ImageEnhance, ImageChops, ImageFilter -import os -import math -import logging - - -from ..component import Component -from ..toolkit.frame import BlankFrame, scale - - -log = logging.getLogger("AVP.Component.Life") - - -class Component(Component): - name = "Conway's Game of Life" - version = "1.0.0" - - def widget(self, *args): - super().widget(*args) - self.scale = 32 - self.updateGridSize() - # The initial grid: a "Queen Bee Shuttle" - # https://conwaylife.com/wiki/Queen_bee_shuttle - self.startingGrid = set( - [ - (3, 7), - (3, 8), - (4, 7), - (4, 8), - (8, 7), - (9, 6), - (9, 8), - (10, 5), - (10, 9), - (11, 6), - (11, 7), - (11, 8), - (12, 4), - (12, 5), - (12, 9), - (12, 10), - (23, 6), - (23, 7), - (24, 6), - (24, 7), - ] - ) - - # Amount of 'bleed' (off-canvas coordinates) on each side of the grid - self.bleedSize = 40 - - self.page.pushButton_pickImage.clicked.connect(self.pickImage) - self.trackWidgets( - { - "tickRate": self.page.spinBox_tickRate, - "scale": self.page.spinBox_scale, - "color": self.page.lineEdit_color, - "shapeType": self.page.comboBox_shapeType, - "shadow": self.page.checkBox_shadow, - "customImg": self.page.checkBox_customImg, - "showGrid": self.page.checkBox_showGrid, - "image": self.page.lineEdit_image, - }, - colorWidgets={ - "color": self.page.pushButton_color, - }, - ) - self.shiftButtons = ( - self.page.toolButton_up, - self.page.toolButton_down, - self.page.toolButton_left, - self.page.toolButton_right, - ) - - def shiftFunc(i): - def shift(): - self.shiftGrid(i) - - return shift - - shiftFuncs = [shiftFunc(i) for i in range(len(self.shiftButtons))] - for i, widget in enumerate(self.shiftButtons): - widget.clicked.connect(shiftFuncs[i]) - self.page.spinBox_scale.setValue(self.scale) - self.page.spinBox_scale.valueChanged.connect(self.updateGridSize) - - def pickImage(self): - imgDir = self.settings.value("componentDir", os.path.expanduser("~")) - filename, _ = QtWidgets.QFileDialog.getOpenFileName( - self.page, - "Choose Image", - imgDir, - "Image Files (%s)" % " ".join(self.core.imageFormats), - ) - if filename: - self.settings.setValue("componentDir", os.path.dirname(filename)) - self.mergeUndo = False - self.page.lineEdit_image.setText(filename) - self.mergeUndo = True - - def shiftGrid(self, d): - action = ShiftGrid(self, d) - self.parent.undoStack.push(action) - - def update(self): - self.updateGridSize() - if self.page.checkBox_customImg.isChecked(): - self.page.label_color.setVisible(False) - self.page.lineEdit_color.setVisible(False) - self.page.pushButton_color.setVisible(False) - self.page.label_shape.setVisible(False) - self.page.comboBox_shapeType.setVisible(False) - self.page.label_image.setVisible(True) - self.page.lineEdit_image.setVisible(True) - self.page.pushButton_pickImage.setVisible(True) - else: - self.page.label_color.setVisible(True) - self.page.lineEdit_color.setVisible(True) - self.page.pushButton_color.setVisible(True) - self.page.label_shape.setVisible(True) - self.page.comboBox_shapeType.setVisible(True) - self.page.label_image.setVisible(False) - self.page.lineEdit_image.setVisible(False) - self.page.pushButton_pickImage.setVisible(False) - enabled = len(self.startingGrid) > 0 - for widget in self.shiftButtons: - widget.setEnabled(enabled) - - def previewClickEvent(self, pos, size, button): - pos = ( - math.ceil((pos[0] / size[0]) * self.gridWidth) - 1, - math.ceil((pos[1] / size[1]) * self.gridHeight) - 1, - ) - action = ClickGrid(self, pos, button) - self.parent.undoStack.push(action) - - def updateGridSize(self): - w, h = self.core.resolutions[-1].split("x") - self.gridWidth = int(int(w) / self.scale) - self.gridHeight = int(int(h) / self.scale) - self.pxWidth = math.ceil(self.width / self.gridWidth) - self.pxHeight = math.ceil(self.height / self.gridHeight) - - def previewRender(self): - return self.drawGrid(self.startingGrid) - - def preFrameRender(self, *args, **kwargs): - super().preFrameRender(*args, **kwargs) - self.tickGrids = {0: self.startingGrid} - - def properties(self): - if self.customImg and (not self.image or not os.path.exists(self.image)): - return ["error"] - return [] - - def error(self): - return "No image selected to represent life." - - def frameRender(self, frameNo): - tick = math.floor(frameNo / self.tickRate) - - # Compute grid evolution on this frame if it hasn't been computed yet - if tick not in self.tickGrids: - self.tickGrids[tick] = self.gridForTick(tick) - grid = self.tickGrids[tick] - - # Delete old evolution data which we shouldn't need anymore - if tick - 60 in self.tickGrids: - del self.tickGrids[tick - 60] - return self.drawGrid(grid) - - def drawGrid(self, grid): - frame = BlankFrame(self.width, self.height) - - def drawCustomImg(): - try: - img = Image.open(self.image) - except Exception: - return - img = img.resize((self.pxWidth, self.pxHeight), Image.Resampling.LANCZOS) - frame.paste(img, box=(drawPtX, drawPtY)) - - def drawShape(): - drawer = ImageDraw.Draw(frame) - rect = ( - (drawPtX, drawPtY), - (drawPtX + self.pxWidth, drawPtY + self.pxHeight), - ) - shape = self.page.comboBox_shapeType.currentText().lower() - - # Rectangle - if shape == "rectangle": - drawer.rectangle(rect, fill=self.color) - - # Elliptical - elif shape == "elliptical": - drawer.ellipse(rect, fill=self.color) - - tenthX, tenthY = scale(10, self.pxWidth, self.pxHeight, int) - smallerShape = ( - ( - drawPtX + tenthX + int(tenthX / 4), - drawPtY + tenthY + int(tenthY / 2), - ), - ( - drawPtX + self.pxWidth - tenthX - int(tenthX / 4), - drawPtY + self.pxHeight - (tenthY + int(tenthY / 2)), - ), - ) - outlineShape = ( - (drawPtX + int(tenthX / 4), drawPtY + int(tenthY / 2)), - ( - drawPtX + self.pxWidth - int(tenthX / 4), - drawPtY + self.pxHeight - int(tenthY / 2), - ), - ) - # Circle - if shape == "circle": - drawer.ellipse(outlineShape, fill=self.color) - drawer.ellipse(smallerShape, fill=(0, 0, 0, 0)) - - # Lilypad - elif shape == "lilypad": - drawer.pieslice(smallerShape, 290, 250, fill=self.color) - - # Pie - elif shape == "pie": - drawer.pieslice(outlineShape, 35, 320, fill=self.color) - - hX, hY = scale(50, self.pxWidth, self.pxHeight, int) # halfline - tX, tY = scale(33, self.pxWidth, self.pxHeight, int) # thirdline - qX, qY = scale(20, self.pxWidth, self.pxHeight, int) # quarterline - - # Path - if shape == "path": - drawer.ellipse(rect, fill=self.color) - rects = { - direction: False - for direction in ( - "up", - "down", - "left", - "right", - ) - } - for cell in self.nearbyCoords(x, y): - if cell not in grid: - continue - if cell[0] == x: - if cell[1] < y: - rects["up"] = True - if cell[1] > y: - rects["down"] = True - if cell[1] == y: - if cell[0] < x: - rects["left"] = True - if cell[0] > x: - rects["right"] = True - - for direction, rect in rects.items(): - if rect: - if direction == "up": - sect = ( - (drawPtX, drawPtY), - (drawPtX + self.pxWidth, drawPtY + hY), - ) - elif direction == "down": - sect = ( - (drawPtX, drawPtY + hY), - ( - drawPtX + self.pxWidth, - drawPtY + self.pxHeight, - ), - ) - elif direction == "left": - sect = ( - (drawPtX, drawPtY), - (drawPtX + hX, drawPtY + self.pxHeight), - ) - elif direction == "right": - sect = ( - (drawPtX + hX, drawPtY), - ( - drawPtX + self.pxWidth, - drawPtY + self.pxHeight, - ), - ) - drawer.rectangle(sect, fill=self.color) - - # Duck - elif shape == "duck": - duckHead = ( - (drawPtX + qX, drawPtY + qY), - (drawPtX + int(qX * 3), drawPtY + int(tY * 2)), - ) - duckBeak = ( - (drawPtX + hX, drawPtY + qY), - (drawPtX + self.pxWidth + qX, drawPtY + int(qY * 3)), - ) - duckWing = ((drawPtX, drawPtY + hY), rect[1]) - duckBody = ( - (drawPtX + int(qX / 4), drawPtY + int(qY * 3)), - (drawPtX + int(tX * 2), drawPtY + self.pxHeight), - ) - drawer.ellipse(duckBody, fill=self.color) - drawer.ellipse(duckHead, fill=self.color) - drawer.pieslice(duckWing, 130, 200, fill=self.color) - drawer.pieslice(duckBeak, 145, 200, fill=self.color) - - # Peace - elif shape == "peace": - line = ( - ( - drawPtX + hX - int(tenthX / 2), - drawPtY + int(tenthY / 2), - ), - ( - drawPtX + hX + int(tenthX / 2), - drawPtY + self.pxHeight - int(tenthY / 2), - ), - ) - drawer.ellipse(outlineShape, fill=self.color) - drawer.ellipse(smallerShape, fill=(0, 0, 0, 0)) - drawer.rectangle(line, fill=self.color) - - def slantLine(difference): - return ( - (drawPtX + difference), - (drawPtY + self.pxHeight - qY), - ), ( - (drawPtX + hX), - (drawPtY + hY), - ) - - drawer.line(slantLine(qX), fill=self.color, width=tenthX) - drawer.line(slantLine(self.pxWidth - qX), fill=self.color, width=tenthX) - - for x, y in grid: - drawPtX = x * self.pxWidth - if drawPtX > self.width: - continue - drawPtY = y * self.pxHeight - if drawPtY > self.height: - continue - - if self.customImg: - drawCustomImg() - else: - drawShape() - - if self.shadow: - shadImg = ImageEnhance.Contrast(frame).enhance(0.0) - shadImg = shadImg.filter(ImageFilter.GaussianBlur(5.00)) - shadImg = ImageChops.offset(shadImg, -2, 2) - shadImg.paste(frame, box=(0, 0), mask=frame) - frame = shadImg - if self.showGrid: - drawer = ImageDraw.Draw(frame) - w, h = scale(0.05, self.width, self.height, int) - for x in range(self.pxWidth, self.width, self.pxWidth): - drawer.rectangle( - ((x, 0), (x + w, self.height)), - fill=self.color, - ) - for y in range(self.pxHeight, self.height, self.pxHeight): - drawer.rectangle( - ((0, y), (self.width, y + h)), - fill=self.color, - ) - - return frame - - def gridForTick(self, tick): - """ - Given a tick number over 0, returns a new grid (a set of tuples). - This must compute the previous ticks' grids if not already computed - """ - if tick - 1 not in self.tickGrids: - self.tickGrids[tick - 1] = self.gridForTick(tick - 1) - - lastGrid = self.tickGrids[tick - 1] - - def neighbours(x, y): - return {cell for cell in self.nearbyCoords(x, y) if cell in lastGrid} - - newGrid = set() - # Copy cells from the previous grid if they have 2 or 3 neighbouring cells - # and if they are within the grid or its bleed area (off-canvas area) - for x, y in lastGrid: - if ( - -self.bleedSize > x > self.gridWidth + self.bleedSize - or -self.bleedSize > y > self.gridHeight + self.bleedSize - ): - continue - surrounding = len(neighbours(x, y)) - if surrounding == 2 or surrounding == 3: - newGrid.add((x, y)) - - # Find positions around living cells which must be checked for reproduction - potentialNewCells = { - coordTup - for origin in lastGrid - for coordTup in list(self.nearbyCoords(*origin)) - } - # Check for reproduction - for x, y in potentialNewCells: - if (x, y) in newGrid: - # Ignore non-empty cell - continue - surrounding = len(neighbours(x, y)) - if surrounding == 3: - newGrid.add((x, y)) - - return newGrid - - def savePreset(self): - pr = super().savePreset() - pr["GRID"] = sorted(self.startingGrid) - return pr - - def loadPreset(self, pr, *args): - self.startingGrid = set(pr["GRID"]) - if self.startingGrid: - for widget in self.shiftButtons: - widget.setEnabled(True) - super().loadPreset(pr, *args) - - def nearbyCoords(self, x, y): - yield x + 1, y + 1 - yield x + 1, y - 1 - yield x - 1, y + 1 - yield x - 1, y - 1 - yield x, y + 1 - yield x, y - 1 - yield x + 1, y - yield x - 1, y - - -class ClickGrid(QUndoCommand): - def __init__(self, comp, pos, button): - super().__init__("click %s component #%s" % (comp.name, comp.compPos)) - self.comp = comp - self.pos = [pos] - if button == QtCore.Qt.MouseButton.RightButton: - self.button = 2 - else: - self.button = 1 - - def id(self): - return self.button - - def mergeWith(self, other): - self.pos.extend(other.pos) - return True - - def add(self): - for pos in self.pos[:]: - self.comp.startingGrid.add(pos) - self.comp.update(auto=True) - - def remove(self): - for pos in self.pos[:]: - self.comp.startingGrid.discard(pos) - self.comp.update(auto=True) - - def redo(self): - if self.button == 1: # Left-click - self.add() - elif self.button == 2: # Right-click - self.remove() - - def undo(self): - if self.button == 1: # Left-click - self.remove() - elif self.button == 2: # Right-click - self.add() - - -class ShiftGrid(QUndoCommand): - def __init__(self, comp, direction): - super().__init__("change %s component #%s" % (comp.name, comp.compPos)) - self.comp = comp - self.direction = direction - self.distance = 1 - - def id(self): - return self.direction - - def mergeWith(self, other): - self.distance += other.distance - return True - - def newGrid(self, Xchange, Ychange): - return {(x + Xchange, y + Ychange) for x, y in self.comp.startingGrid} - - def redo(self): - if self.direction == 0: - newGrid = self.newGrid(0, -self.distance) - elif self.direction == 1: - newGrid = self.newGrid(0, self.distance) - elif self.direction == 2: - newGrid = self.newGrid(-self.distance, 0) - elif self.direction == 3: - newGrid = self.newGrid(self.distance, 0) - self.comp.startingGrid = newGrid - self.comp._sendUpdateSignal() - - def undo(self): - if self.direction == 0: - newGrid = self.newGrid(0, self.distance) - elif self.direction == 1: - newGrid = self.newGrid(0, -self.distance) - elif self.direction == 2: - newGrid = self.newGrid(self.distance, 0) - elif self.direction == 3: - newGrid = self.newGrid(-self.distance, 0) - self.comp.startingGrid = newGrid - self.comp._sendUpdateSignal() diff --git a/src/components/life.ui b/src/components/life.ui deleted file mode 100644 index 30cf9d0..0000000 --- a/src/components/life.ui +++ /dev/null @@ -1,405 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <item> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string>Simulation Speed</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_tickRate"> - <property name="suffix"> - <string> frames per tick</string> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>30</number> - </property> - <property name="value"> - <number>5</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_color"> - <property name="maximumSize"> - <size> - <width>0</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string>255,255,255</string> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <item> - <widget class="QLabel" name="label_7"> - <property name="text"> - <string>Grid Scale</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_scale"> - <property name="minimum"> - <number>22</number> - </property> - <property name="maximum"> - <number>128</number> - </property> - <property name="value"> - <number>32</number> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_customImg"> - <property name="text"> - <string>Custom Image</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_image"> - <property name="text"> - <string>Image</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_image"/> - </item> - <item> - <widget class="QPushButton" name="pushButton_pickImage"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string>...</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_color"> - <property name="text"> - <string>Color</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_color_3"> - <property name="maximumSize"> - <size> - <width>0</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string>0,0,0</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_color"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="default"> - <bool>false</bool> - </property> - <property name="flat"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_shape"> - <property name="text"> - <string>Shape</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_shapeType"> - <item> - <property name="text"> - <string>Path</string> - </property> - </item> - <item> - <property name="text"> - <string>Rectangle</string> - </property> - </item> - <item> - <property name="text"> - <string>Elliptical</string> - </property> - </item> - <item> - <property name="text"> - <string>Circle</string> - </property> - </item> - <item> - <property name="text"> - <string>Lilypad</string> - </property> - </item> - <item> - <property name="text"> - <string>Pie</string> - </property> - </item> - <item> - <property name="text"> - <string>Duck</string> - </property> - </item> - <item> - <property name="text"> - <string>Peace</string> - </property> - </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_8"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <item> - <widget class="QCheckBox" name="checkBox_shadow"> - <property name="text"> - <string>Shadow</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_showGrid"> - <property name="text"> - <string>Show Grid</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_6"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <item> - <widget class="QToolButton" name="toolButton_up"> - <property name="text"> - <string>Up</string> - </property> - <property name="arrowType"> - <enum>Qt::UpArrow</enum> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="toolButton_down"> - <property name="text"> - <string>Down</string> - </property> - <property name="arrowType"> - <enum>Qt::DownArrow</enum> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="toolButton_left"> - <property name="text"> - <string>Left</string> - </property> - <property name="arrowType"> - <enum>Qt::LeftArrow</enum> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="toolButton_right"> - <property name="text"> - <string>Right</string> - </property> - <property name="arrowType"> - <enum>Qt::RightArrow</enum> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_9"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - </layout> - </item> - <item> - <widget class="QTextBrowser" name="textBrowser"> - <property name="html"> - <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Click the preview window to place a cell. Right-click to remove.</span></p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- A cell with less than 2 neighbours will die from underpopulation</p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- A cell with more than 3 neighbours will die from overpopulation.</p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- An empty space surrounded by 3 live cells will cause reproduction.</p></body></html></string> - </property> - <property name="tabStopDistance"> - <number>80</number> - </property> - <property name="textInteractionFlags"> - <set>Qt::NoTextInteraction</set> - </property> - <property name="openLinks"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/original.py b/src/components/original.py deleted file mode 100644 index fad797b..0000000 --- a/src/components/original.py +++ /dev/null @@ -1,243 +0,0 @@ -import numpy -from PIL import Image, ImageDraw -from copy import copy - -from ..component import Component -from ..toolkit.frame import BlankFrame - - -class Component(Component): - name = "Classic Visualizer" - version = "1.0.0" - - def names(*args): - return ["Original Audio Visualization"] - - def properties(self): - return ["pcm"] - - def widget(self, *args): - self.scale = 20 - self.y = 0 - super().widget(*args) - - self.page.comboBox_visLayout.addItem("Classic") - self.page.comboBox_visLayout.addItem("Split") - self.page.comboBox_visLayout.addItem("Bottom") - self.page.comboBox_visLayout.addItem("Top") - self.page.comboBox_visLayout.setCurrentIndex(0) - - self.page.lineEdit_visColor.setText("255,255,255") - - self.trackWidgets( - { - "visColor": self.page.lineEdit_visColor, - "layout": self.page.comboBox_visLayout, - "scale": self.page.spinBox_scale, - "y": self.page.spinBox_y, - "smooth": self.page.spinBox_smooth, - }, - colorWidgets={ - "visColor": self.page.pushButton_visColor, - }, - relativeWidgets=[ - "y", - ], - ) - - def previewRender(self): - spectrum = numpy.fromfunction( - lambda x: float(self.scale) / 2500 * (x - 128) ** 2, - (255,), - dtype="int16", - ) - return self.drawBars( - self.width, self.height, spectrum, self.visColor, self.layout - ) - - def preFrameRender(self, **kwargs): - super().preFrameRender(**kwargs) - self.smoothConstantDown = 0.08 + 0 if not self.smooth else self.smooth / 15 - self.smoothConstantUp = 0.8 - 0 if not self.smooth else self.smooth / 15 - self.lastSpectrum = None - self.spectrumArray = {} - - for i in range(0, len(self.completeAudioArray), self.sampleSize): - if self.canceled: - break - self.lastSpectrum = self.transformData( - i, - self.completeAudioArray, - self.sampleSize, - self.smoothConstantDown, - self.smoothConstantUp, - self.lastSpectrum, - ) - self.spectrumArray[i] = copy(self.lastSpectrum) - - progress = int(100 * (i / len(self.completeAudioArray))) - if progress >= 100: - progress = 100 - pStr = "Analyzing audio: " + str(progress) + "%" - self.progressBarSetText.emit(pStr) - self.progressBarUpdate.emit(int(progress)) - - def frameRender(self, frameNo): - arrayNo = frameNo * self.sampleSize - return self.drawBars( - self.width, - self.height, - self.spectrumArray[arrayNo], - self.visColor, - self.layout, - ) - - def transformData( - self, - i, - completeAudioArray, - sampleSize, - smoothConstantDown, - smoothConstantUp, - lastSpectrum, - ): - if len(completeAudioArray) < (i + sampleSize): - sampleSize = len(completeAudioArray) - i - - window = numpy.hanning(sampleSize) - data = completeAudioArray[i : i + sampleSize][::1] * window - paddedSampleSize = 2048 - paddedData = numpy.pad(data, (0, paddedSampleSize - sampleSize), "constant") - spectrum = numpy.fft.fft(paddedData) - sample_rate = 44100 - frequencies = numpy.fft.fftfreq(len(spectrum), 1.0 / sample_rate) - - y = abs(spectrum[0 : int(paddedSampleSize / 2) - 1]) - - # filter the noise away - # y[y<80] = 0 - - y = self.scale * numpy.log10(y) - y[numpy.isinf(y)] = 0 - - if lastSpectrum is not None: - lastSpectrum[y < lastSpectrum] = y[ - y < lastSpectrum - ] * smoothConstantDown + lastSpectrum[y < lastSpectrum] * ( - 1 - smoothConstantDown - ) - - lastSpectrum[y >= lastSpectrum] = y[ - y >= lastSpectrum - ] * smoothConstantUp + lastSpectrum[y >= lastSpectrum] * ( - 1 - smoothConstantUp - ) - else: - lastSpectrum = y - - x = frequencies[0 : int(paddedSampleSize / 2) - 1] - - return lastSpectrum - - def drawBars(self, width, height, spectrum, color, layout): - vH = height - height / 8 - bF = width / 64 - bH = bF / 2 - bQ = bF / 4 - imTop = BlankFrame(width, height) - draw = ImageDraw.Draw(imTop) - r, g, b = color - color2 = (r, g, b, 125) - - bP = height / 1200 - - for j in range(0, 63): - x0 = bH + j * bF - y0 = vH + bQ - y1 = vH + bQ - spectrum[j * 4] * bP - bH - x1 = bH + j * bF + bF - draw.rectangle( - ( - x0, - y0 if y0 < y1 else y1, - x1 if x1 > x0 else x0, - y1 if y0 < y1 else y0, - ), - fill=color2, - ) - - x0 = bH + bQ + j * bF - y0 = vH - x1 = bH + bQ + j * bF + bH - y1 = vH - spectrum[j * 4] * bP - draw.rectangle( - ( - x0, - y0 if y0 < y1 else y1, - x1 if x1 > x0 else x0, - y1 if y0 < y1 else y0, - ), - fill=color, - ) - - imBottom = imTop.transpose(Image.Transpose.FLIP_TOP_BOTTOM) - - im = BlankFrame(width, height) - - if layout == 0: # Classic - y = self.y - int(height / 100 * 43) - im.paste(imTop, (0, y), mask=imTop) - y = self.y + int(height / 100 * 43) - im.paste(imBottom, (0, y), mask=imBottom) - - if layout == 1: # Split - y = self.y + int(height / 100 * 10) - im.paste(imTop, (0, y), mask=imTop) - y = self.y - int(height / 100 * 10) - im.paste(imBottom, (0, y), mask=imBottom) - - if layout == 2: # Bottom - y = self.y + int(height / 100 * 10) - im.paste(imTop, (0, y), mask=imTop) - - if layout == 3: # Top - y = self.y - int(height / 100 * 10) - im.paste(imBottom, (0, y), mask=imBottom) - - return im - - def command(self, arg): - if "=" in arg: - key, arg = arg.split("=", 1) - try: - if key == "color": - self.page.lineEdit_visColor.setText(arg) - return - elif key == "layout": - if arg == "classic": - self.page.comboBox_visLayout.setCurrentIndex(0) - elif arg == "split": - self.page.comboBox_visLayout.setCurrentIndex(1) - elif arg == "bottom": - self.page.comboBox_visLayout.setCurrentIndex(2) - elif arg == "top": - self.page.comboBox_visLayout.setCurrentIndex(3) - return - elif key == "scale": - arg = int(arg) - self.page.spinBox_scale.setValue(arg) - return - elif key == "y": - arg = int(arg) - self.page.spinBox_y.setValue(arg) - return - except ValueError: - print("You must enter a number.") - quit(1) - super().command(arg) - - def commandHelp(self): - print("Give a layout name:\n layout=[classic/split/bottom/top]") - print("Specify a color:\n color=255,255,255") - print("Visualizer scale (20 is default):\n scale=number") - print("Y position:\n y=number") diff --git a/src/components/original.ui b/src/components/original.ui deleted file mode 100644 index c7b7e22..0000000 --- a/src/components/original.ui +++ /dev/null @@ -1,243 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>178</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>180</width> - <height>0</height> - </size> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <widget class="QLabel" name="label_visLayout"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Layout</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_visLayout"/> - </item> - <item> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_visColor"> - <property name="text"> - <string>Color</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_visColor"> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="MaximumSize" stdset="0"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_visColor"/> - </item> - <item> - <spacer name="horizontalSpacer_4"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_Y"> - <property name="text"> - <string>Y</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_y"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::UpDownArrows</enum> - </property> - <property name="minimum"> - <number>-5000</number> - </property> - <property name="maximum"> - <number>5000</number> - </property> - <property name="singleStep"> - <number>10</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <widget class="QLabel" name="label_scale"> - <property name="text"> - <string>Scale</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_scale"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::PlusMinus</enum> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="value"> - <number>20</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <widget class="QLabel" name="label_smooth"> - <property name="text"> - <string>Sensitivity</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_smooth"> - <property name="maximum"> - <number>5</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/sound.py b/src/components/sound.py deleted file mode 100644 index 2df8e38..0000000 --- a/src/components/sound.py +++ /dev/null @@ -1,77 +0,0 @@ -from PyQt6 import QtGui, QtCore, QtWidgets -import os - -from ..component import Component -from ..toolkit.frame import BlankFrame - - -class Component(Component): - name = "Sound" - version = "1.0.0" - - def widget(self, *args): - super().widget(*args) - self.page.pushButton_sound.clicked.connect(self.pickSound) - self.trackWidgets( - { - "sound": self.page.lineEdit_sound, - "chorus": self.page.checkBox_chorus, - "delay": self.page.spinBox_delay, - "volume": self.page.spinBox_volume, - }, - commandArgs={ - "sound": None, - }, - ) - - def properties(self): - props = ["static", "audio"] - if not os.path.exists(self.sound): - props.append("error") - return props - - def error(self): - if not self.sound: - return "No audio file selected." - if not os.path.exists(self.sound): - return "The audio file selected no longer exists!" - - def audio(self): - params = {} - if self.delay != 0.0: - params["adelay"] = "=%s" % str(int(self.delay * 1000.00)) - if self.chorus: - params["chorus"] = "=0.5:0.9:50|60|40:0.4|0.32|0.3:0.25|0.4|0.3:2|2.3|1.3" - if self.volume != 1.0: - params["volume"] = "=%s:replaygain_noclip=0" % str(self.volume) - - return (self.sound, params) - - def pickSound(self): - sndDir = self.settings.value("componentDir", os.path.expanduser("~")) - filename, _ = QtWidgets.QFileDialog.getOpenFileName( - self.page, - "Choose Sound", - sndDir, - "Audio Files (%s)" % " ".join(self.core.audioFormats), - ) - if filename: - self.settings.setValue("componentDir", os.path.dirname(filename)) - self.mergeUndo = False - self.page.lineEdit_sound.setText(filename) - self.mergeUndo = True - - def commandHelp(self): - print("Path to audio file:\n path=/filepath/to/sound.ogg") - - def command(self, arg): - if "=" in arg: - key, arg = arg.split("=", 1) - if key == "path": - if "*%s" % os.path.splitext(arg)[1] not in self.core.audioFormats: - print("Not a supported audio format") - quit(1) - self.page.lineEdit_sound.setText(arg) - return - - super().command(arg) diff --git a/src/components/sound.ui b/src/components/sound.ui deleted file mode 100644 index 4c11332..0000000 --- a/src/components/sound.ui +++ /dev/null @@ -1,172 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>31</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Audio File</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_sound"> - <property name="minimumSize"> - <size> - <width>1</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_sound"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>1</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="MaximumSize" stdset="0"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Volume</string> - </property> - </widget> - </item> - <item> - <widget class="QDoubleSpinBox" name="spinBox_volume"> - <property name="suffix"> - <string>x</string> - </property> - <property name="maximum"> - <double>10.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.100000000000000</double> - </property> - <property name="value"> - <double>1.000000000000000</double> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Delay</string> - </property> - </widget> - </item> - <item> - <widget class="QDoubleSpinBox" name="spinBox_delay"> - <property name="suffix"> - <string>s</string> - </property> - <property name="maximum"> - <double>9999999.990000000223517</double> - </property> - <property name="singleStep"> - <double>0.500000000000000</double> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_chorus"> - <property name="text"> - <string>Chorus</string> - </property> - </widget> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/spectrum.py b/src/components/spectrum.py deleted file mode 100644 index 062ebc7..0000000 --- a/src/components/spectrum.py +++ /dev/null @@ -1,368 +0,0 @@ -from PIL import Image -from PyQt6 import QtGui, QtCore, QtWidgets -import os -import math -import subprocess -import time -import logging - -from ..component import Component -from ..toolkit.frame import BlankFrame, scale -from ..toolkit import checkOutput, connectWidget -from ..toolkit.ffmpeg import ( - openPipe, - closePipe, - getAudioDuration, - FfmpegVideo, - exampleSound, -) - - -log = logging.getLogger("AVP.Components.Spectrum") - - -class Component(Component): - name = "Spectrum" - version = "1.0.1" - - def widget(self, *args): - self.previewFrame = None - super().widget(*args) - self._image = BlankFrame(self.width, self.height) - self.chunkSize = 4 * self.width * self.height - self.changedOptions = True - self.previewSize = (214, 120) - self.previewPipe = None - - if hasattr(self.parent, "lineEdit_audioFile"): - # update preview when audio file changes (if genericPreview is off) - self.parent.lineEdit_audioFile.textChanged.connect(self.update) - - self.trackWidgets( - { - "filterType": self.page.comboBox_filterType, - "window": self.page.comboBox_window, - "mode": self.page.comboBox_mode, - "amplitude": self.page.comboBox_amplitude0, - "amplitude1": self.page.comboBox_amplitude1, - "amplitude2": self.page.comboBox_amplitude2, - "display": self.page.comboBox_display, - "zoom": self.page.spinBox_zoom, - "tc": self.page.spinBox_tc, - "x": self.page.spinBox_x, - "y": self.page.spinBox_y, - "mirror": self.page.checkBox_mirror, - "draw": self.page.checkBox_draw, - "scale": self.page.spinBox_scale, - "color": self.page.comboBox_color, - "compress": self.page.checkBox_compress, - "mono": self.page.checkBox_mono, - "hue": self.page.spinBox_hue, - }, - relativeWidgets=[ - "x", - "y", - ], - ) - for widget in self._trackedWidgets.values(): - connectWidget(widget, lambda: self.changed()) - - def changed(self): - self.changedOptions = True - - def update(self): - filterType = self.page.comboBox_filterType.currentIndex() - self.page.stackedWidget.setCurrentIndex(filterType) - if filterType == 3: - self.page.spinBox_hue.setEnabled(False) - else: - self.page.spinBox_hue.setEnabled(True) - if filterType == 2 or filterType == 4: - self.page.checkBox_mono.setEnabled(False) - else: - self.page.checkBox_mono.setEnabled(True) - - def previewRender(self): - changedSize = self.updateChunksize() - if ( - not changedSize - and not self.changedOptions - and self.previewFrame is not None - ): - log.debug("Spectrum #%s is reusing old preview frame" % self.compPos) - return self.previewFrame - - frame = self.getPreviewFrame() - self.changedOptions = False - if not frame: - log.warning("Spectrum #%s failed to create a preview frame" % self.compPos) - self.previewFrame = None - return BlankFrame(self.width, self.height) - else: - self.previewFrame = frame - return frame - - def preFrameRender(self, **kwargs): - super().preFrameRender(**kwargs) - if self.previewPipe is not None: - self.previewPipe.wait() - self.updateChunksize() - w, h = scale(self.scale, self.width, self.height, str) - self.video = FfmpegVideo( - inputPath=self.audioFile, - filter_=self.makeFfmpegFilter(), - width=w, - height=h, - chunkSize=self.chunkSize, - frameRate=int(self.settings.value("outputFrameRate")), - parent=self.parent, - component=self, - ) - - def frameRender(self, frameNo): - if FfmpegVideo.threadError is not None: - raise FfmpegVideo.threadError - return self.finalizeFrame(self.video.frame(frameNo)) - - def postFrameRender(self): - closePipe(self.video.pipe) - - def getPreviewFrame(self): - genericPreview = self.settings.value("pref_genericPreview") - startPt = 0 - if not genericPreview: - inputFile = self.parent.lineEdit_audioFile.text() - if not inputFile or not os.path.exists(inputFile): - return - duration = getAudioDuration(inputFile) - if not duration: - return - startPt = duration / 3 - - command = [ - self.core.FFMPEG_BIN, - "-thread_queue_size", - "512", - "-r", - str(self.settings.value("outputFrameRate")), - "-ss", - "{0:.3f}".format(startPt), - "-i", - self.core.junkStream if genericPreview else inputFile, - "-f", - "image2pipe", - "-pix_fmt", - "rgba", - ] - command.extend(self.makeFfmpegFilter(preview=True, startPt=startPt)) - command.extend( - [ - "-an", - "-s:v", - "%sx%s" % scale(self.scale, self.width, self.height, str), - "-codec:v", - "rawvideo", - "-", - "-frames:v", - "1", - ] - ) - - if self.core.logEnabled: - logFilename = os.path.join( - self.core.logDir, "preview_%s.log" % str(self.compPos) - ) - log.debug("Creating FFmpeg process (log at %s)" % logFilename) - with open(logFilename, "w") as logf: - logf.write(" ".join(command) + "\n\n") - with open(logFilename, "a") as logf: - self.previewPipe = openPipe( - command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=logf, - bufsize=10**8, - ) - else: - self.previewPipe = openPipe( - command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL, - bufsize=10**8, - ) - byteFrame = self.previewPipe.stdout.read(self.chunkSize) - closePipe(self.previewPipe) - - frame = self.finalizeFrame(byteFrame) - return frame - - def makeFfmpegFilter(self, preview=False, startPt=0): - """Makes final FFmpeg filter command""" - - def getFilterComplexCommand(): - """Inner function that creates the final, complex part of the filter command""" - nonlocal self - genericPreview = self.settings.value("pref_genericPreview") - - def getFilterComplexCommandForType(): - """Determine portion of filter command that changes depending on selected type""" - nonlocal self - if preview: - w, h = self.previewSize - else: - w, h = (self.width, self.height) - color = self.page.comboBox_color.currentText().lower() - - if self.filterType == 0: # Spectrum - if self.amplitude == 0: - amplitude = "sqrt" - elif self.amplitude == 1: - amplitude = "cbrt" - elif self.amplitude == 2: - amplitude = "4thrt" - elif self.amplitude == 3: - amplitude = "5thrt" - elif self.amplitude == 4: - amplitude = "lin" - elif self.amplitude == 5: - amplitude = "log" - filter_ = ( - f"showspectrum=s={w}x{h}:" - "slide=scroll:" - f"win_func={self.page.comboBox_window.currentText()}:" - f"color={color}:" - f"scale={amplitude}," - "colorkey=color=black:" - "similarity=0.1:blend=0.5" - ) - elif self.filterType == 1: # Histogram - if self.amplitude1 == 0: - amplitude = "log" - elif self.amplitude1 == 1: - amplitude = "lin" - if self.display == 0: - display = "log" - elif self.display == 1: - display = "sqrt" - elif self.display == 2: - display = "cbrt" - elif self.display == 3: - display = "lin" - elif self.display == 4: - display = "rlog" - filter_ = ( - f'ahistogram=r={str(self.settings.value("outputFrameRate"))}:' - f"s={w}x{h}:" - "dmode=separate:" - f"ascale={amplitude}:" - f"scale={display}" - ) - elif self.filterType == 2: # Vector Scope - if self.amplitude2 == 0: - amplitude = "log" - elif self.amplitude2 == 1: - amplitude = "sqrt" - elif self.amplitude2 == 2: - amplitude = "cbrt" - elif self.amplitude2 == 3: - amplitude = "lin" - m = self.page.comboBox_mode.currentText() - filter_ = ( - f"avectorscope=s={w}x{h}:" - f'draw={"line" if self.draw else "dot"}:' - f"m={m}:" - f"scale={amplitude}:" - f"zoom={str(self.zoom)}" - ) - elif self.filterType == 3: # Musical Scale - filter_ = ( - f'showcqt=r={str(self.settings.value("outputFrameRate"))}:' - f"s={w}x{h}:" - "count=30:" - "text=0:" - f"tc={str(self.tc)}," - "colorkey=color=black:" - "similarity=0.1:blend=0.5" - ) - elif self.filterType == 4: # Phase - filter_ = ( - f'aphasemeter=r={str(self.settings.value("outputFrameRate"))}:' - f"s={w}x{h}:" - "video=1 [atrash][vtmp1]; " - "[atrash] anullsink; " - "[vtmp1] colorkey=color=black:" - "similarity=0.1:blend=0.5, " - "crop=in_w/8:in_h:(in_w/8)*7:0 " - ) - return filter_ - - if self.filterType < 2: - exampleSnd = exampleSound("freq") - elif self.filterType == 2 or self.filterType == 4: - exampleSnd = exampleSound("stereo") - elif self.filterType == 3: - exampleSnd = exampleSound("white") - compression = "compand=gain=4," if self.compress else "" - aformat = ( - "aformat=channel_layouts=mono," - if self.mono and self.filterType not in (2, 4) - else "" - ) - filter_ = getFilterComplexCommandForType() - hflip = "hflip, " if self.mirror else "" - trim = ( - "trim=start=%s:end=%s, " - % ( - "{0:.3f}".format(startPt + 12), - "{0:.3f}".format(startPt + 12.5), - ) - if preview - else "" - ) - scale_ = "scale=%sx%s" % scale(self.scale, self.width, self.height, str) - hue = ( - ", hue=h=%s:s=10" % str(self.hue) - if self.hue > 0 and self.filterType != 3 - else "" - ) - convolution = ( - ", convolution=-2 -1 0 -1 1 1 0 1 2:-2 -1 0 -1 1 1 0 1 2:-2 -1 0 -1 1 1 0 1 2:-2 -1 0 -1 1 1 0 1 2" - if self.filterType == 3 - else "" - ) - - return ( - f"{exampleSnd if preview and genericPreview else '[0:a] '}" - f"{compression}{aformat}{filter_} [v1]; " - f"[v1] {hflip}{trim}{scale_}{hue}{convolution} [v]" - ) - - return [ - "-filter_complex", - getFilterComplexCommand(), - "-map", - "[v]", - ] - - def updateChunksize(self): - width, height = scale(self.scale, self.width, self.height, int) - oldChunkSize = int(self.chunkSize) - self.chunkSize = 4 * width * height - changed = self.chunkSize != oldChunkSize - return changed - - def finalizeFrame(self, imageData): - try: - image = Image.frombytes( - "RGBA", - scale(self.scale, self.width, self.height, int), - imageData, - ) - self._image = image - except ValueError: - image = self._image - - frame = BlankFrame(self.width, self.height) - frame.paste(image, box=(self.x, self.y)) - return frame diff --git a/src/components/spectrum.ui b/src/components/spectrum.ui deleted file mode 100644 index c6a8a15..0000000 --- a/src/components/spectrum.ui +++ /dev/null @@ -1,946 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>197</height> - </size> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_5"/> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_4"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Type</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_filterType"> - <item> - <property name="text"> - <string>Spectrum</string> - </property> - </item> - <item> - <property name="text"> - <string>Histogram</string> - </property> - </item> - <item> - <property name="text"> - <string>Vector Scope</string> - </property> - </item> - <item> - <property name="text"> - <string>Musical Scale</string> - </property> - </item> - <item> - <property name="text"> - <string>Phase</string> - </property> - </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_9"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_xTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>X</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_x"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_yTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Y</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_y"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <item> - <widget class="QCheckBox" name="checkBox_compress"> - <property name="text"> - <string>Compress</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_mono"> - <property name="text"> - <string>Mono</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_mirror"> - <property name="text"> - <string>Mirror</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_11"> - <property name="text"> - <string>Hue</string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_hue"> - <property name="suffix"> - <string>° </string> - </property> - <property name="maximum"> - <number>359</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Scale</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_scale"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::UpDownArrows</enum> - </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>10</number> - </property> - <property name="maximum"> - <number>400</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QStackedWidget" name="stackedWidget"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="currentIndex"> - <number>0</number> - </property> - <widget class="QWidget" name="page"> - <widget class="QWidget" name="verticalLayoutWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>561</width> - <height>66</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <property name="sizeConstraint"> - <enum>QLayout::SetMaximumSize</enum> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <item> - <widget class="QLabel" name="label_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>31</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Window</string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_window"> - <item> - <property name="text"> - <string>hann</string> - </property> - </item> - <item> - <property name="text"> - <string>gauss</string> - </property> - </item> - <item> - <property name="text"> - <string>tukey</string> - </property> - </item> - <item> - <property name="text"> - <string>dolph</string> - </property> - </item> - <item> - <property name="text"> - <string>cauchy</string> - </property> - </item> - <item> - <property name="text"> - <string>parzen</string> - </property> - </item> - <item> - <property name="text"> - <string>poisson</string> - </property> - </item> - <item> - <property name="text"> - <string>rect</string> - </property> - </item> - <item> - <property name="text"> - <string>bartlett</string> - </property> - </item> - <item> - <property name="text"> - <string>hanning</string> - </property> - </item> - <item> - <property name="text"> - <string>hamming</string> - </property> - </item> - <item> - <property name="text"> - <string>blackman</string> - </property> - </item> - <item> - <property name="text"> - <string>welch</string> - </property> - </item> - <item> - <property name="text"> - <string>flattop</string> - </property> - </item> - <item> - <property name="text"> - <string>bharris</string> - </property> - </item> - <item> - <property name="text"> - <string>bnuttall</string> - </property> - </item> - <item> - <property name="text"> - <string>lanczos</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QLabel" name="label_3"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Amplitude</string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_amplitude0"> - <item> - <property name="text"> - <string>Square root</string> - </property> - </item> - <item> - <property name="text"> - <string>Cubic root</string> - </property> - </item> - <item> - <property name="text"> - <string>4thrt</string> - </property> - </item> - <item> - <property name="text"> - <string>5thrt</string> - </property> - </item> - <item> - <property name="text"> - <string>Linear</string> - </property> - </item> - <item> - <property name="text"> - <string>Logarithmic</string> - </property> - </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_4"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::MinimumExpanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>10</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QLabel" name="label_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Color </string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_color"> - <item> - <property name="text"> - <string>Channel</string> - </property> - </item> - <item> - <property name="text"> - <string>Intensity</string> - </property> - </item> - <item> - <property name="text"> - <string>Rainbow</string> - </property> - </item> - <item> - <property name="text"> - <string>Moreland</string> - </property> - </item> - <item> - <property name="text"> - <string>Nebulae</string> - </property> - </item> - <item> - <property name="text"> - <string>Fire</string> - </property> - </item> - <item> - <property name="text"> - <string>Fiery</string> - </property> - </item> - <item> - <property name="text"> - <string>Fruit</string> - </property> - </item> - <item> - <property name="text"> - <string>Cool</string> - </property> - </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::MinimumExpanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>10</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - </layout> - </widget> - </widget> - <widget class="QWidget" name="page_2"> - <widget class="QWidget" name="verticalLayoutWidget_2"> - <property name="geometry"> - <rect> - <x>-1</x> - <y>-1</y> - <width>561</width> - <height>31</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QLabel" name="label_6"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Display Scale</string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_display"> - <item> - <property name="text"> - <string>Logarithmic</string> - </property> - </item> - <item> - <property name="text"> - <string>Square root</string> - </property> - </item> - <item> - <property name="text"> - <string>Cubic root</string> - </property> - </item> - <item> - <property name="text"> - <string>Linear</string> - </property> - </item> - <item> - <property name="text"> - <string>Reverse Log</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QLabel" name="label_5"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Amplitude</string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_amplitude1"> - <item> - <property name="text"> - <string>Logarithmic</string> - </property> - </item> - <item> - <property name="text"> - <string>Linear</string> - </property> - </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Minimum</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - </layout> - </widget> - </widget> - <widget class="QWidget" name="page_3"> - <widget class="QWidget" name="verticalLayoutWidget_3"> - <property name="geometry"> - <rect> - <x>-1</x> - <y>-1</y> - <width>585</width> - <height>64</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <item> - <widget class="QLabel" name="label_9"> - <property name="text"> - <string>Mode</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_mode"> - <item> - <property name="text"> - <string>lissajous</string> - </property> - </item> - <item> - <property name="text"> - <string>lissajous_xy</string> - </property> - </item> - <item> - <property name="text"> - <string>polar</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QLabel" name="label_7"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Amplitude</string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_amplitude2"> - <item> - <property name="text"> - <string>Linear</string> - </property> - </item> - <item> - <property name="text"> - <string>Square root</string> - </property> - </item> - <item> - <property name="text"> - <string>Cubic root</string> - </property> - </item> - <item> - <property name="text"> - <string>Logarithmic</string> - </property> - </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <item> - <widget class="QLabel" name="label_8"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Zoom</string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_zoom"> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>10</number> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_draw"> - <property name="text"> - <string>Line</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_6"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - </layout> - </widget> - </widget> - <widget class="QWidget" name="page_4"> - <widget class="QWidget" name="verticalLayoutWidget_4"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>561</width> - <height>31</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout_6"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <item> - <widget class="QLabel" name="label_10"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Timeclamp</string> - </property> - <property name="margin"> - <number>4</number> - </property> - </widget> - </item> - <item> - <widget class="QDoubleSpinBox" name="spinBox_tc"> - <property name="suffix"> - <string>s</string> - </property> - <property name="decimals"> - <number>3</number> - </property> - <property name="minimum"> - <double>0.002000000000000</double> - </property> - <property name="maximum"> - <double>1.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.010000000000000</double> - </property> - <property name="value"> - <double>0.017000000000000</double> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_7"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - </layout> - </widget> - </widget> - <widget class="QWidget" name="page_5"> - <widget class="QWidget" name="verticalLayoutWidget_5"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>551</width> - <height>31</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout_7"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_11"/> - </item> - </layout> - </widget> - </widget> - </widget> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>10</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/text.py b/src/components/text.py deleted file mode 100644 index 40c981a..0000000 --- a/src/components/text.py +++ /dev/null @@ -1,218 +0,0 @@ -from PIL import ImageEnhance, ImageFilter, ImageChops -from PyQt6.QtGui import QColor, QFont -from PyQt6 import QtGui, QtCore, QtWidgets -import os -import logging - -from ..component import Component -from ..toolkit.frame import FramePainter, PaintColor - -log = logging.getLogger("AVP.Components.Text") - - -class Component(Component): - name = "Title Text" - version = "1.0.1" - - def widget(self, *args): - super().widget(*args) - self.title = "Text" - self.alignment = 1 - self.titleFont = QFont() - self.fontSize = self.height / 13.5 - - 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.spinBox_fontSize.setValue(int(self.fontSize)) - self.page.lineEdit_title.setText(self.title) - self.page.pushButton_center.clicked.connect(self.centerXY) - - self.page.fontComboBox_titleFont.currentFontChanged.connect( - self._sendUpdateSignal - ) - # The QFontComboBox must be connected directly to the Qt Signal - # which triggers the preview to update. - # This unfortunately makes changing the font into a non-undoable action. - # Must be something broken in the conversion to a ComponentAction - - self.trackWidgets( - { - "textColor": self.page.lineEdit_textColor, - "title": self.page.lineEdit_title, - "alignment": self.page.comboBox_textAlign, - "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", - "stroke", - "shadX", - "shadY", - "shadBlur", - ], - ) - self.centerXY() - - 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) - - def centerXY(self): - self.setRelativeWidget("xPosition", 0.5) - self.setRelativeWidget("yPosition", 0.521) - - def getXY(self): - """Returns true x, y after considering alignment settings""" - fm = QtGui.QFontMetrics(self.titleFont) - text_width = fm.boundingRect(self.title).width() - x = self.pixelValForAttr("xPosition") - - if self.alignment == 1: # Middle - offset = int(text_width / 2) - elif self.alignment == 2: # Right - offset = text_width - else: - raise ValueError(f"Alignment value {self.alignment} unknown") - - x -= offset - - return x, self.yPosition - - def loadPreset(self, pr, *args): - super().loadPreset(pr, *args) - - font = QFont() - font.fromString(pr["titleFont"]) - self.page.fontComboBox_titleFont.setCurrentFont(font) - - def savePreset(self): - saveValueStore = super().savePreset() - saveValueStore["titleFont"] = self.titleFont.toString() - return saveValueStore - - def previewRender(self): - return self.addText(self.width, self.height) - - def properties(self): - props = ["static"] - if not self.title: - props.append("error") - return props - - def error(self): - return "No text provided." - - def frameRender(self, frameNo): - return self.addText(self.width, self.height) - - def addText(self, width, height): - font = self.titleFont - font.setPixelSize(self.fontSize) - font.setStyle(QFont.Style.StyleNormal) - font.setWeight(QFont.Weight.Normal) - font.setCapitalization(QFont.Capitalization.MixedCase) - if self.fontStyle == 1: - font.setWeight(QFont.Weight.DemiBold) - if self.fontStyle == 2: - font.setWeight(QFont.Weight.Bold) - elif self.fontStyle == 3: - font.setStyle(QFont.Style.StyleItalic) - elif self.fontStyle == 4: - font.setWeight(QFont.Weight.Bold) - font.setStyle(QFont.Style.StyleItalic) - elif self.fontStyle == 5: - font.setStyle(QFont.Style.StyleOblique) - elif self.fontStyle == 6: - font.setCapitalization(QFont.Capitalization.SmallCaps) - - image = FramePainter(width, height) - x, y = self.getXY() - log.debug("Text position translates to %s, %s", x, y) - if self.stroke > 0: - outliner = QtGui.QPainterPathStroker() - outliner.setWidth(self.stroke) - path = QtGui.QPainterPath() - if self.fontStyle == 6: - # PathStroker ignores smallcaps so we need this weird hack - path.addText(x, y, font, self.title[0]) - fm = QtGui.QFontMetrics(font) - newX = x + fm.boundingRect(self.title[0]).width() - strokeFont = self.page.fontComboBox_titleFont.currentFont() - strokeFont.setCapitalization(QFont.Capitalization.SmallCaps) - strokeFont.setPixelSize(int((self.fontSize / 7) * 5)) - strokeFont.setLetterSpacing(QFont.SpacingType.PercentageSpacing, 139) - path.addText(newX, y, strokeFont, self.title[1:]) - else: - path.addText(x, y, font, self.title) - path = outliner.createStroke(path) - image.setPen(QtCore.Qt.PenStyle.NoPen) - image.setBrush(PaintColor(*self.strokeColor)) - image.drawPath(path) - - image.setFont(font) - image.setPen(self.textColor) - image.drawText(x, y, self.title) - - # 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:") - print(' "title=User Error"') - print("Specify a text color:\n color=255,255,255") - print("Set custom x, y position:\n x=500 y=500") - - def command(self, arg): - if "=" in arg: - key, arg = arg.split("=", 1) - if key == "color": - self.page.lineEdit_textColor.setText(arg) - return - elif key == "size": - self.page.spinBox_fontSize.setValue(int(arg)) - return - elif key == "x": - self.page.spinBox_xTextAlign.setValue(int(arg)) - return - elif key == "y": - self.page.spinBox_yTextAlign.setValue(int(arg)) - return - elif key == "title": - self.page.lineEdit_title.setText(arg) - return - super().command(arg) diff --git a/src/components/text.ui b/src/components/text.ui deleted file mode 100644 index b62e0ed..0000000 --- a/src/components/text.ui +++ /dev/null @@ -1,671 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="spacing"> - <number>6</number> - </property> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QLabel" name="label_title"> - <property name="text"> - <string>Title</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_title"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Testing New GUI</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Font</string> - </property> - </widget> - </item> - <item> - <widget class="QFontComboBox" name="fontComboBox_titleFont"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <property name="leftMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="label_textLayout"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Text Layout</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_textAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>100</width> - <height>16777215</height> - </size> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pushButton_center"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Center Text</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_6"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_xTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>X</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_xTextAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>50</width> - <height>16777215</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>999999999</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_yTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Y</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_yTextAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>50</width> - <height>16777215</height> - </size> - </property> - <property name="maximum"> - <number>999999999</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string>Text Color</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="MaximumSize" stdset="0"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_8"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_fontSize"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Font Size</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_fontSize"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="suffix"> - <string/> - </property> - <property name="prefix"> - <string/> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>500</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_7"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_3"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Font Style</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_fontStyle"> - <item> - <property name="text"> - <string>Normal</string> - </property> - </item> - <item> - <property name="text"> - <string>Semi-Bold</string> - </property> - </item> - <item> - <property name="text"> - <string>Bold</string> - </property> - </item> - <item> - <property name="text"> - <string>Italic</string> - </property> - </item> - <item> - <property name="text"> - <string>Bold Italic</string> - </property> - </item> - <item> - <property name="text"> - <string>Faux Italic</string> - </property> - </item> - <item> - <property name="text"> - <string>Small Caps</string> - </property> - </item> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_12"> - <item> - <widget class="QLineEdit" name="lineEdit_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>0</width> - <height>16777215</height> - </size> - </property> - <property name="focusPolicy"> - <enum>Qt::NoFocus</enum> - </property> - <property name="text"> - <string>255,255,255</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Stroke</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_stroke"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="suffix"> - <string>px</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_5"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Stroke Color</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_strokeColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>0</width> - <height>16777215</height> - </size> - </property> - <property name="focusPolicy"> - <enum>Qt::NoFocus</enum> - </property> - <property name="text"> - <string>0,0,0</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_strokeColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="MaximumSize" stdset="0"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QCheckBox" name="checkBox_shadow"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Shadow</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_shadX"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Shadow Offset</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_shadX"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimum"> - <number>-1000</number> - </property> - <property name="maximum"> - <number>1000</number> - </property> - <property name="value"> - <number>-4</number> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_shadY"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimum"> - <number>-1000</number> - </property> - <property name="maximum"> - <number>1000</number> - </property> - <property name="value"> - <number>8</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_shadBlur"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Shadow Blur</string> - </property> - </widget> - </item> - <item> - <widget class="QDoubleSpinBox" name="spinBox_shadBlur"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximum"> - <double>99.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.100000000000000</double> - </property> - <property name="value"> - <double>5.000000000000000</double> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Minimum</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/video.py b/src/components/video.py deleted file mode 100644 index 65a05af..0000000 --- a/src/components/video.py +++ /dev/null @@ -1,254 +0,0 @@ -from PIL import Image -from PyQt6 import QtGui, QtCore, QtWidgets -import os -import math -import subprocess -import logging - -from ..component import Component -from ..toolkit.frame import BlankFrame, scale -from ..toolkit.ffmpeg import openPipe, closePipe, testAudioStream, FfmpegVideo -from ..toolkit import checkOutput - - -log = logging.getLogger("AVP.Components.Video") - - -class Component(Component): - name = "Video" - version = "1.0.0" - - def widget(self, *args): - self.videoPath = "" - self.badAudio = False - self.x = 0 - self.y = 0 - self.loopVideo = False - super().widget(*args) - self._image = BlankFrame(self.width, self.height) - self.page.pushButton_video.clicked.connect(self.pickVideo) - self.trackWidgets( - { - "videoPath": self.page.lineEdit_video, - "loopVideo": self.page.checkBox_loop, - "useAudio": self.page.checkBox_useAudio, - "distort": self.page.checkBox_distort, - "scale": self.page.spinBox_scale, - "volume": self.page.spinBox_volume, - "xPosition": self.page.spinBox_x, - "yPosition": self.page.spinBox_y, - }, - presetNames={ - "videoPath": "video", - "loopVideo": "loop", - "xPosition": "x", - "yPosition": "y", - }, - relativeWidgets=[ - "xPosition", - "yPosition", - ], - ) - - def update(self): - if self.page.checkBox_useAudio.isChecked(): - self.page.label_volume.setEnabled(True) - self.page.spinBox_volume.setEnabled(True) - else: - self.page.label_volume.setEnabled(False) - self.page.spinBox_volume.setEnabled(False) - - def previewRender(self): - self.updateChunksize() - frame = self.getPreviewFrame(self.width, self.height) - if not frame: - return BlankFrame(self.width, self.height) - else: - return frame - - def properties(self): - props = [] - outputFile = None - if hasattr(self.parent, "lineEdit_outputFile"): - # check only happens in GUI mode - outputFile = self.parent.lineEdit_outputFile.text() - - if not self.videoPath: - self.lockError("There is no video selected.") - elif not os.path.exists(self.videoPath): - self.lockError("The video selected does not exist!") - elif outputFile and os.path.realpath(self.videoPath) == os.path.realpath( - outputFile - ): - self.lockError("Input and output paths match.") - - if self.useAudio: - props.append("audio") - if not testAudioStream(self.videoPath) and self.error() is None: - self.lockError("Could not identify an audio stream in this video.") - - return props - - def audio(self): - params = {} - if self.volume != 1.0: - params["volume"] = "=%s:replaygain_noclip=0" % str(self.volume) - return (self.videoPath, params) - - def preFrameRender(self, **kwargs): - super().preFrameRender(**kwargs) - self.updateChunksize() - self.video = ( - FfmpegVideo( - inputPath=self.videoPath, - filter_=self.makeFfmpegFilter(), - width=self.width, - height=self.height, - chunkSize=self.chunkSize, - frameRate=int(self.settings.value("outputFrameRate")), - parent=self.parent, - loopVideo=self.loopVideo, - component=self, - ) - if os.path.exists(self.videoPath) - else None - ) - - def frameRender(self, frameNo): - if FfmpegVideo.threadError is not None: - raise FfmpegVideo.threadError - return self.finalizeFrame(self.video.frame(frameNo)) - - def postFrameRender(self): - closePipe(self.video.pipe) - - def pickVideo(self): - imgDir = self.settings.value("componentDir", os.path.expanduser("~")) - filename, _ = QtWidgets.QFileDialog.getOpenFileName( - self.page, - "Choose Video", - imgDir, - "Video Files (%s)" % " ".join(self.core.videoFormats), - ) - if filename: - self.settings.setValue("componentDir", os.path.dirname(filename)) - self.mergeUndo = False - self.page.lineEdit_video.setText(filename) - self.mergeUndo = True - - def getPreviewFrame(self, width, height): - if not self.videoPath or not os.path.exists(self.videoPath): - return - - command = [ - self.core.FFMPEG_BIN, - "-thread_queue_size", - "512", - "-i", - self.videoPath, - "-f", - "image2pipe", - "-pix_fmt", - "rgba", - ] - command.extend(self.makeFfmpegFilter()) - command.extend( - [ - "-codec:v", - "rawvideo", - "-", - "-ss", - "90", - "-frames:v", - "1", - ] - ) - - if self.core.logEnabled: - logFilename = os.path.join( - self.core.logDir, "preview_%s.log" % str(self.compPos) - ) - log.debug("Creating ffmpeg process (log at %s)" % logFilename) - with open(logFilename, "w") as logf: - logf.write(" ".join(command) + "\n\n") - with open(logFilename, "a") as logf: - pipe = openPipe( - command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=logf, - bufsize=10**8, - ) - else: - pipe = openPipe( - command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL, - bufsize=10**8, - ) - - byteFrame = pipe.stdout.read(self.chunkSize) - closePipe(pipe) - - frame = self.finalizeFrame(byteFrame) - return frame - - def makeFfmpegFilter(self): - return [ - "-filter_complex", - "[0:v] scale=%s:%s" % scale(self.scale, self.width, self.height, str), - ] - - def updateChunksize(self): - if self.scale != 100 and not self.distort: - width, height = scale(self.scale, self.width, self.height, int) - else: - width, height = self.width, self.height - self.chunkSize = 4 * width * height - - def command(self, arg): - if "=" in arg: - key, arg = arg.split("=", 1) - if key == "path" and os.path.exists(arg): - if "*%s" % os.path.splitext(arg)[1] in self.core.videoFormats: - self.page.lineEdit_video.setText(arg) - self.page.spinBox_scale.setValue(100) - self.page.checkBox_loop.setChecked(True) - return - else: - print("Not a supported video format") - quit(1) - elif arg == "audio": - if not self.page.lineEdit_video.text(): - print("'audio' option must follow a video selection") - quit(1) - self.page.checkBox_useAudio.setChecked(True) - return - super().command(arg) - - def commandHelp(self): - print("Load a video:\n path=/filepath/to/video.mp4") - print("Using audio:\n path=/filepath/to/video.mp4 audio") - - def finalizeFrame(self, imageData): - try: - if self.distort: - image = Image.frombytes("RGBA", (self.width, self.height), imageData) - else: - image = Image.frombytes( - "RGBA", - scale(self.scale, self.width, self.height, int), - imageData, - ) - self._image = image - except ValueError: - # use last good frame - image = self._image - - if self.scale != 100 or self.xPosition != 0 or self.yPosition != 0: - frame = BlankFrame(self.width, self.height) - frame.paste(image, box=(self.xPosition, self.yPosition)) - else: - frame = image - return frame diff --git a/src/components/video.ui b/src/components/video.ui deleted file mode 100644 index 08d15d3..0000000 --- a/src/components/video.ui +++ /dev/null @@ -1,328 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>197</height> - </size> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>31</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Video</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_video"> - <property name="minimumSize"> - <size> - <width>1</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_video"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>1</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="MaximumSize" stdset="0"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_9"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_xTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>X</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_x"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_yTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Y</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_y"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <item> - <widget class="QCheckBox" name="checkBox_loop"> - <property name="text"> - <string>Loop</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="checkBox_distort"> - <property name="text"> - <string>Distort by scale</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Scale</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_scale"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::UpDownArrows</enum> - </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>10</number> - </property> - <property name="maximum"> - <number>400</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <item> - <widget class="QCheckBox" name="checkBox_useAudio"> - <property name="text"> - <string>Use Audio</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_volume"> - <property name="text"> - <string>Volume</string> - </property> - </widget> - </item> - <item> - <widget class="QDoubleSpinBox" name="spinBox_volume"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="suffix"> - <string>x</string> - </property> - <property name="minimum"> - <double>0.000000000000000</double> - </property> - <property name="maximum"> - <double>10.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.100000000000000</double> - </property> - <property name="value"> - <double>1.000000000000000</double> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/components/waveform.py b/src/components/waveform.py deleted file mode 100644 index 7dc0b99..0000000 --- a/src/components/waveform.py +++ /dev/null @@ -1,230 +0,0 @@ -from PIL import Image -from PyQt6 import QtGui, QtCore, QtWidgets -from PyQt6.QtGui import QColor -import os -import math -import subprocess -import logging - -from ..component import Component -from ..toolkit.frame import BlankFrame, scale -from ..toolkit import checkOutput -from ..toolkit.ffmpeg import ( - openPipe, - closePipe, - getAudioDuration, - FfmpegVideo, - exampleSound, -) - - -log = logging.getLogger("AVP.Components.Waveform") - - -class Component(Component): - name = "Waveform" - version = "1.0.0" - - def widget(self, *args): - super().widget(*args) - self._image = BlankFrame(self.width, self.height) - - self.page.lineEdit_color.setText("255,255,255") - - if hasattr(self.parent, "lineEdit_audioFile"): - self.parent.lineEdit_audioFile.textChanged.connect(self.update) - - self.trackWidgets( - { - "color": self.page.lineEdit_color, - "mode": self.page.comboBox_mode, - "amplitude": self.page.comboBox_amplitude, - "x": self.page.spinBox_x, - "y": self.page.spinBox_y, - "mirror": self.page.checkBox_mirror, - "scale": self.page.spinBox_scale, - "opacity": self.page.spinBox_opacity, - "compress": self.page.checkBox_compress, - "mono": self.page.checkBox_mono, - }, - colorWidgets={ - "color": self.page.pushButton_color, - }, - relativeWidgets=[ - "x", - "y", - ], - ) - - def previewRender(self): - self.updateChunksize() - frame = self.getPreviewFrame(self.width, self.height) - if not frame: - return BlankFrame(self.width, self.height) - else: - return frame - - def preFrameRender(self, **kwargs): - super().preFrameRender(**kwargs) - self.updateChunksize() - w, h = scale(self.scale, self.width, self.height, str) - self.video = FfmpegVideo( - inputPath=self.audioFile, - filter_=self.makeFfmpegFilter(), - width=w, - height=h, - chunkSize=self.chunkSize, - frameRate=int(self.settings.value("outputFrameRate")), - parent=self.parent, - component=self, - debug=True, - ) - - def frameRender(self, frameNo): - if FfmpegVideo.threadError is not None: - raise FfmpegVideo.threadError - return self.finalizeFrame(self.video.frame(frameNo)) - - def postFrameRender(self): - closePipe(self.video.pipe) - - def getPreviewFrame(self, width, height): - genericPreview = self.settings.value("pref_genericPreview") - startPt = 0 - if not genericPreview: - inputFile = self.parent.lineEdit_audioFile.text() - if not inputFile or not os.path.exists(inputFile): - return - duration = getAudioDuration(inputFile) - if not duration: - return - startPt = duration / 3 - if startPt + 3 > duration: - startPt += startPt - 3 - - command = [ - self.core.FFMPEG_BIN, - "-thread_queue_size", - "512", - "-r", - str(self.settings.value("outputFrameRate")), - "-ss", - "{0:.3f}".format(startPt), - "-i", - self.core.junkStream if genericPreview else inputFile, - "-f", - "image2pipe", - "-pix_fmt", - "rgba", - ] - command.extend(self.makeFfmpegFilter(preview=True, startPt=startPt)) - command.extend( - [ - "-an", - "-s:v", - "%sx%s" % scale(self.scale, self.width, self.height, str), - "-codec:v", - "rawvideo", - "-", - "-frames:v", - "1", - ] - ) - if self.core.logEnabled: - logFilename = os.path.join( - self.core.logDir, "preview_%s.log" % str(self.compPos) - ) - log.debug("Creating ffmpeg log at %s", logFilename) - with open(logFilename, "w") as logf: - logf.write(" ".join(command) + "\n\n") - with open(logFilename, "a") as logf: - pipe = openPipe( - command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=logf, - bufsize=10**8, - ) - else: - pipe = openPipe( - command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL, - bufsize=10**8, - ) - byteFrame = pipe.stdout.read(self.chunkSize) - closePipe(pipe) - - frame = self.finalizeFrame(byteFrame) - return frame - - def makeFfmpegFilter(self, preview=False, startPt=0): - w, h = scale(self.scale, self.width, self.height, str) - if self.amplitude == 0: - amplitude = "lin" - elif self.amplitude == 1: - amplitude = "log" - elif self.amplitude == 2: - amplitude = "sqrt" - elif self.amplitude == 3: - amplitude = "cbrt" - hexcolor = QColor(*self.color).name() - opacity = "{0:.1f}".format(self.opacity / 100) - genericPreview = self.settings.value("pref_genericPreview") - if self.mode < 3: - filter_ = ( - "showwaves=" - f'r={str(self.settings.value("outputFrameRate"))}:' - f's={self.settings.value("outputWidth")}x{self.settings.value("outputHeight")}:' - f'mode={self.page.comboBox_mode.currentText().lower() if self.mode != 3 else "p2p"}:' - f"colors={hexcolor}@{opacity}:scale={amplitude}" - ) - elif self.mode > 2: - filter_ = ( - f'showfreqs=s={str(self.settings.value("outputWidth"))}x{str(self.settings.value("outputHeight"))}:' - f'mode={"line" if self.mode == 4 else "bar"}:' - f"colors={hexcolor}@{opacity}" - f":ascale={amplitude}:fscale={'log' if self.mono else 'lin'}" - ) - - baselineHeight = int(self.height * (4 / 1080)) - return [ - "-filter_complex", - f"{exampleSound('wave', extra='') if preview and genericPreview else '[0:a] '}" - f"{'compand=gain=4,' if self.compress else ''}" - f"{'aformat=channel_layouts=mono,' if self.mono and self.mode < 3 else ''}" - f"{filter_}" - f"{', drawbox=x=(iw-w)/2:y=(ih-h)/2:w=iw:h=%s:color=%s@%s' % (baselineHeight, hexcolor, opacity) if self.mode < 2 else ''}" - f"{', hflip' if self.mirror else''}" - " [v1]; " - "[v1] scale=%s:%s%s [v]" - % ( - w, - h, - ", trim=duration=%s" % "{0:.3f}".format(startPt + 3) if preview else "", - ), - "-map", - "[v]", - ] - - def updateChunksize(self): - width, height = scale(self.scale, self.width, self.height, int) - self.chunkSize = 4 * width * height - - def finalizeFrame(self, imageData): - try: - image = Image.frombytes( - "RGBA", - scale(self.scale, self.width, self.height, int), - imageData, - ) - self._image = image - except ValueError: - image = self._image - if self.scale != 100 or self.x != 0 or self.y != 0: - frame = BlankFrame(self.width, self.height) - frame.paste(image, box=(self.x, self.y)) - else: - frame = image - return frame diff --git a/src/components/waveform.ui b/src/components/waveform.ui deleted file mode 100644 index 5473f33..0000000 --- a/src/components/waveform.ui +++ /dev/null @@ -1,383 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Form</class> - <widget class="QWidget" name="Form"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>586</width> - <height>197</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>197</height> - </size> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="leftMargin"> - <number>4</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_textColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>31</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Mode</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_mode"> - <item> - <property name="text"> - <string>Cline</string> - </property> - </item> - <item> - <property name="text"> - <string>Line</string> - </property> - </item> - <item> - <property name="text"> - <string>Point</string> - </property> - </item> - <item> - <property name="text"> - <string>Frequency Bar</string> - </property> - </item> - <item> - <property name="text"> - <string>Frequency Line</string> - </property> - </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_9"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>5</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_xTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>X</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_x"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_yTitleAlign"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Y</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_y"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="minimum"> - <number>-10000</number> - </property> - <property name="maximum"> - <number>10000</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <item> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Color</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="lineEdit_color"> - <property name="inputMethodHints"> - <set>Qt::ImhNone</set> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_color"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="default"> - <bool>false</bool> - </property> - <property name="flat"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>Opacity</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_opacity"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::UpDownArrows</enum> - </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Scale</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_scale"> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::UpDownArrows</enum> - </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>10</number> - </property> - <property name="maximum"> - <number>400</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <item> - <widget class="QCheckBox" name="checkBox_compress"> - <property name="text"> - <string>Compress</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_mono"> - <property name="text"> - <string>Mono</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkBox_mirror"> - <property name="text"> - <string>Mirror</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Amplitude</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBox_amplitude"> - <item> - <property name="text"> - <string>Linear</string> - </property> - </item> - <item> - <property name="text"> - <string>Logarithmic</string> - </property> - </item> - <item> - <property name="text"> - <string>Square root</string> - </property> - </item> - <item> - <property name="text"> - <string>Cubic root</string> - </property> - </item> - </widget> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> |
