diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/command.py | 5 | ||||
| -rw-r--r-- | src/components/image.py | 21 | ||||
| -rw-r--r-- | src/components/image.ui | 64 | ||||
| -rw-r--r-- | src/core.py | 2 | ||||
| -rw-r--r-- | src/mainwindow.py | 9 | ||||
| -rw-r--r-- | src/presetmanager.py | 4 | ||||
| -rw-r--r-- | src/preview_thread.py | 18 | ||||
| -rw-r--r-- | src/video_thread.py | 7 |
8 files changed, 121 insertions, 9 deletions
diff --git a/src/command.py b/src/command.py index ee0e48d..be194d8 100644 --- a/src/command.py +++ b/src/command.py @@ -1,3 +1,8 @@ +''' + When using commandline mode, this module's object handles interpreting + the arguments and giving them to Core, which tracks the main program state. + Then it immediately exports a video. +''' from PyQt5 import QtCore import argparse import os diff --git a/src/components/image.py b/src/components/image.py index 4ccfc80..c9da137 100644 --- a/src/components/image.py +++ b/src/components/image.py @@ -1,4 +1,4 @@ -from PIL import Image, ImageDraw +from PIL import Image, ImageDraw, ImageEnhance from PyQt5 import QtGui, QtCore, QtWidgets import os @@ -20,7 +20,9 @@ class Component(Component): page.pushButton_image.clicked.connect(self.pickImage) page.spinBox_scale.valueChanged.connect(self.update) page.spinBox_rotate.valueChanged.connect(self.update) + page.spinBox_color.valueChanged.connect(self.update) page.checkBox_stretch.stateChanged.connect(self.update) + page.checkBox_mirror.stateChanged.connect(self.update) page.spinBox_x.valueChanged.connect(self.update) page.spinBox_y.valueChanged.connect(self.update) @@ -31,9 +33,11 @@ class Component(Component): self.imagePath = self.page.lineEdit_image.text() self.scale = self.page.spinBox_scale.value() self.rotate = self.page.spinBox_rotate.value() + self.color = self.page.spinBox_color.value() self.xPosition = self.page.spinBox_x.value() self.yPosition = self.page.spinBox_y.value() self.stretched = self.page.checkBox_stretch.isChecked() + self.mirror = self.page.checkBox_mirror.isChecked() self.parent.drawPreview() super().update() @@ -56,33 +60,48 @@ class Component(Component): frame = BlankFrame(width, height) if self.imagePath and os.path.exists(self.imagePath): 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.FLIP_LEFT_RIGHT) if self.stretched and image.size != (width, height): image = image.resize((width, height), Image.ANTIALIAS) if self.scale != 100: newHeight = int((image.height / 100) * self.scale) newWidth = int((image.width / 100) * self.scale) image = image.resize((newWidth, newHeight), Image.ANTIALIAS) + + # 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 loadPreset(self, pr, presetName=None): super().loadPreset(pr, presetName) self.page.lineEdit_image.setText(pr['image']) self.page.spinBox_scale.setValue(pr['scale']) + self.page.spinBox_color.setValue(pr['color']) self.page.spinBox_rotate.setValue(pr['rotate']) self.page.spinBox_x.setValue(pr['x']) self.page.spinBox_y.setValue(pr['y']) self.page.checkBox_stretch.setChecked(pr['stretched']) + self.page.checkBox_mirror.setChecked(pr['mirror']) def savePreset(self): return { 'preset': self.currentPreset, 'image': self.imagePath, 'scale': self.scale, + 'color': self.color, 'rotate': self.rotate, 'stretched': self.stretched, + 'mirror': self.mirror, 'x': self.xPosition, 'y': self.yPosition, } diff --git a/src/components/image.ui b/src/components/image.ui index 33488f8..e549ed0 100644 --- a/src/components/image.ui +++ b/src/components/image.ui @@ -209,9 +209,16 @@ </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>Rotation</string> + <string>Rotate</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> @@ -291,6 +298,61 @@ </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> + <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/core.py b/src/core.py index 9ea9666..5623039 100644 --- a/src/core.py +++ b/src/core.py @@ -1,5 +1,5 @@ ''' - Home to the Core class which tracks the program state + Home to the Core class which tracks program state. Used by GUI & commandline ''' import sys import os diff --git a/src/mainwindow.py b/src/mainwindow.py index e8a3221..1c6bbc4 100644 --- a/src/mainwindow.py +++ b/src/mainwindow.py @@ -1,3 +1,9 @@ +''' + When using GUI mode, this module's object (the main window) takes + user input to construct a program state (stored in the Core object). + This shows a preview of the video being created and allows for saving + projects and exporting the video at a later time. +''' from PyQt5 import QtCore, QtGui, uic, QtWidgets from PyQt5.QtWidgets import QMenu, QShortcut from queue import Queue @@ -79,6 +85,7 @@ class MainWindow(QtWidgets.QMainWindow): self.previewWorker = preview_thread.Worker(self, self.previewQueue) self.previewWorker.moveToThread(self.previewThread) self.previewWorker.imageCreated.connect(self.showPreviewImage) + self.previewWorker.error.connect(self.cleanUp) self.previewThread.start() self.timer = QtCore.QTimer(self) @@ -296,11 +303,11 @@ class MainWindow(QtWidgets.QMainWindow): QtWidgets.QShortcut("Ctrl+End", self.window, self.moveComponentBottom) QtWidgets.QShortcut("Ctrl+r", self.window, self.removeComponent) + @QtCore.pyqtSlot() def cleanUp(self): self.timer.stop() self.previewThread.quit() self.previewThread.wait() - self.autosave() def updateWindowTitle(self): appName = 'Audio Visualizer' diff --git a/src/presetmanager.py b/src/presetmanager.py index 805b93e..40aa73f 100644 --- a/src/presetmanager.py +++ b/src/presetmanager.py @@ -1,3 +1,7 @@ +''' + Preset manager object handles all interactions with presets, including + the context menu accessed from MainWindow. +''' from PyQt5 import QtCore, QtWidgets import string import os diff --git a/src/preview_thread.py b/src/preview_thread.py index e58f04e..afb5e50 100644 --- a/src/preview_thread.py +++ b/src/preview_thread.py @@ -1,3 +1,7 @@ +''' + Thread that runs to create QImages for MainWindow's preview label. + Processes a queue of component lists. +''' from PyQt5 import QtCore, QtGui, uic from PyQt5.QtCore import pyqtSignal, pyqtSlot from PIL import Image @@ -11,6 +15,7 @@ from copy import copy class Worker(QtCore.QObject): imageCreated = pyqtSignal(['QImage']) + error = pyqtSignal() def __init__(self, parent=None, queue=None): QtCore.QObject.__init__(self) @@ -59,12 +64,15 @@ class Worker(QtCore.QObject): "This is a fatal error." % str(component), detail=str(e), - icon='Warning' + icon='Warning', + parent=None # mainwindow is in a different thread ) - quit(1) - - self._image = ImageQt(frame) - self.imageCreated.emit(QtGui.QImage(self._image)) + from frame import BlankFrame + self.imageCreated.emit(ImageQt(BlankFrame)) + self.error.emit() + break + else: + self.imageCreated.emit(ImageQt(frame)) except Empty: True diff --git a/src/video_thread.py b/src/video_thread.py index aed4d60..d35a37a 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -1,3 +1,10 @@ +''' + Thread created to export a video. It has a slot to begin export using + an input file, output path, and component list. During export multiple + threads are created to render the video as quickly as possible. Signals + are emitted to update MainWindow's progress bar, detail text, and preview. + Export can be cancelled with cancel() + reset() +''' from PyQt5 import QtCore, QtGui, uic from PyQt5.QtCore import pyqtSignal, pyqtSlot from PIL import Image, ImageDraw, ImageFont |
