diff options
| author | tassaron | 2017-07-13 00:05:11 -0400 |
|---|---|---|
| committer | tassaron | 2017-07-13 00:05:11 -0400 |
| commit | 8811b699a9c2d6b78af1e2a332d3031aef73aec4 (patch) | |
| tree | e62f318fb625c5c79314de2eb0ce4b7b93e7496c | |
| parent | 2e37dafd7036973a315b525f131850a6fb6d0b35 (diff) | |
merge consecutive static components
| -rw-r--r-- | src/components/color.py | 13 | ||||
| -rw-r--r-- | src/components/image.py | 16 | ||||
| -rw-r--r-- | src/components/original.py | 9 | ||||
| -rw-r--r-- | src/components/text.py | 21 | ||||
| -rw-r--r-- | src/components/video.py | 6 | ||||
| -rw-r--r-- | src/core.py | 2 | ||||
| -rw-r--r-- | src/frame.py | 21 | ||||
| -rw-r--r-- | src/mainwindow.py | 2 | ||||
| -rw-r--r-- | src/preview_thread.py | 38 | ||||
| -rw-r--r-- | src/video_thread.py | 27 |
10 files changed, 100 insertions, 55 deletions
diff --git a/src/components/color.py b/src/components/color.py index 82b45b3..da3bcf9 100644 --- a/src/components/color.py +++ b/src/components/color.py @@ -15,6 +15,7 @@ class Component(Component): def widget(self, parent): self.parent = parent + self.settings = self.parent.core.settings page = self.loadUi('color.ui') self.color1 = (0, 0, 0) @@ -42,9 +43,9 @@ class Component(Component): page.spinBox_x.valueChanged.connect(self.update) page.spinBox_y.valueChanged.connect(self.update) page.spinBox_width.setValue( - int(parent.settings.value("outputWidth"))) + int(self.settings.value("outputWidth"))) page.spinBox_height.setValue( - int(parent.settings.value("outputHeight"))) + int(self.settings.value("outputHeight"))) page.lineEdit_color1.textChanged.connect(self.update) page.lineEdit_color2.textChanged.connect(self.update) @@ -113,16 +114,16 @@ class Component(Component): super().update() def previewRender(self, previewWorker): - width = int(previewWorker.core.settings.value('outputWidth')) - height = int(previewWorker.core.settings.value('outputHeight')) + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) return self.drawFrame(width, height) def properties(self): return ['static'] def frameRender(self, layerNo, frameNo): - width = int(self.worker.core.settings.value('outputWidth')) - height = int(self.worker.core.settings.value('outputHeight')) + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) return self.drawFrame(width, height) def drawFrame(self, width, height): diff --git a/src/components/image.py b/src/components/image.py index 07abc3f..6465bc9 100644 --- a/src/components/image.py +++ b/src/components/image.py @@ -13,7 +13,7 @@ class Component(Component): def widget(self, parent): self.parent = parent - self.settings = parent.settings + self.settings = self.parent.core.settings page = self.loadUi('image.ui') page.lineEdit_image.textChanged.connect(self.update) @@ -42,24 +42,24 @@ class Component(Component): super().update() def previewRender(self, previewWorker): - width = int(previewWorker.core.settings.value('outputWidth')) - height = int(previewWorker.core.settings.value('outputHeight')) + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) return self.drawFrame(width, height) def properties(self): props = ['static'] - if not os.path.exists(self.imagePath): + if self.imagePath and not os.path.exists(self.imagePath): props.append('error') return props def error(self): if not os.path.exists(self.imagePath): - return "The image path selected on " \ - "layer %s no longer exists!" % str(self.compPos) + return "The image selected on " \ + "layer %s does not exist!" % str(self.compPos) def frameRender(self, layerNo, frameNo): - width = int(self.worker.core.settings.value('outputWidth')) - height = int(self.worker.core.settings.value('outputHeight')) + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) return self.drawFrame(width, height) def drawFrame(self, width, height): diff --git a/src/components/original.py b/src/components/original.py index 638095d..3599c30 100644 --- a/src/components/original.py +++ b/src/components/original.py @@ -21,6 +21,7 @@ class Component(Component): def widget(self, parent): self.parent = parent + self.settings = self.parent.core.settings self.visColor = (255, 255, 255) self.scale = 20 self.y = 0 @@ -76,8 +77,8 @@ class Component(Component): def previewRender(self, previewWorker): spectrum = numpy.fromfunction( lambda x: float(self.scale)/2500*(x-128)**2, (255,), dtype="int16") - width = int(previewWorker.core.settings.value('outputWidth')) - height = int(previewWorker.core.settings.value('outputHeight')) + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) return self.drawBars( width, height, spectrum, self.visColor, self.layout ) @@ -88,8 +89,8 @@ class Component(Component): self.smoothConstantUp = 0.8 self.lastSpectrum = None self.spectrumArray = {} - self.width = int(self.worker.core.settings.value('outputWidth')) - self.height = int(self.worker.core.settings.value('outputHeight')) + self.width = int(self.settings.value('outputWidth')) + self.height = int(self.settings.value('outputHeight')) for i in range(0, len(self.completeAudioArray), self.sampleSize): if self.canceled: diff --git a/src/components/text.py b/src/components/text.py index ed50064..4435b80 100644 --- a/src/components/text.py +++ b/src/components/text.py @@ -17,10 +17,11 @@ class Component(Component): self.titleFont = QFont() def widget(self, parent): - height = int(parent.settings.value('outputHeight')) - width = int(parent.settings.value('outputWidth')) - self.parent = parent + self.settings = self.parent.core.settings + height = int(self.settings.value('outputHeight')) + width = int(self.settings.value('outputWidth')) + self.textColor = (255, 255, 255) self.title = 'Text' self.alignment = 1 @@ -78,12 +79,12 @@ class Component(Component): x = int(self.xPosition) if self.alignment == 1: # Middle - offset = fm.width(self.title)/2 - x = int(self.xPosition - offset) + offset = int(fm.width(self.title)/2) + x = self.xPosition - offset if self.alignment == 2: # Right offset = fm.width(self.title) - x = int(self.xPosition - offset) + x = self.xPosition - offset return x, self.yPosition def loadPreset(self, pr, presetName=None): @@ -115,16 +116,16 @@ class Component(Component): } def previewRender(self, previewWorker): - width = int(previewWorker.core.settings.value('outputWidth')) - height = int(previewWorker.core.settings.value('outputHeight')) + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) return self.addText(width, height) def properties(self): return ['static'] def frameRender(self, layerNo, frameNo): - width = int(self.worker.core.settings.value('outputWidth')) - height = int(self.worker.core.settings.value('outputHeight')) + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) return self.addText(width, height) def addText(self, width, height): diff --git a/src/components/video.py b/src/components/video.py index 5303e3a..49bd145 100644 --- a/src/components/video.py +++ b/src/components/video.py @@ -158,14 +158,14 @@ class Component(Component): if self.useAudio: # props.append('audio') pass - if not os.path.exists(self.videoPath): + if self.videoPath and not os.path.exists(self.videoPath): props.append('error') return props def error(self): if not os.path.exists(self.videoPath): - return "The video path selected on " \ - "layer %s no longer exists!" % str(self.compPos) + return "The video selected on " \ + "layer %s does not exist!" % str(self.compPos) def audio(self): return (self.videoPath, {}) diff --git a/src/core.py b/src/core.py index 450e43b..64f55eb 100644 --- a/src/core.py +++ b/src/core.py @@ -11,6 +11,7 @@ from importlib import import_module from PyQt5.QtCore import QStandardPaths import toolkit +from frame import Frame class Core: @@ -20,6 +21,7 @@ class Core: opens projects and presets, and stores settings/paths to data. ''' def __init__(self): + Frame.core = self self.dataDir = QStandardPaths.writableLocation( QStandardPaths.AppConfigLocation ) diff --git a/src/frame.py b/src/frame.py index c066cdb..cddb611 100644 --- a/src/frame.py +++ b/src/frame.py @@ -5,6 +5,11 @@ from PyQt5 import QtGui from PIL import Image from PIL.ImageQt import ImageQt import sys +import os + + +class Frame: + '''Controller class for all frames.''' class FramePainter(QtGui.QPainter): @@ -43,5 +48,19 @@ def FloodFrame(width, height, RgbaTuple): def BlankFrame(width, height): - '''The base frame used by each component to start drawing''' + '''The base frame used by each component to start drawing.''' return FloodFrame(width, height, (0, 0, 0, 0)) + + +def Checkerboard(width, height): + ''' + A checkerboard to represent transparency to the user. + TODO: Would be cool to generate this image with numpy instead. + ''' + image = FloodFrame(1920, 1080, (0, 0, 0, 0)) + image.paste(Image.open( + os.path.join(Frame.core.wd, "background.png")), + (0, 0) + ) + image = image.resize((width, height)) + return image diff --git a/src/mainwindow.py b/src/mainwindow.py index d21ba0a..771b6b8 100644 --- a/src/mainwindow.py +++ b/src/mainwindow.py @@ -306,6 +306,7 @@ class MainWindow(QtWidgets.QMainWindow): QtWidgets.QShortcut("Ctrl+A", self.window, self.openSaveProjectDialog) QtWidgets.QShortcut("Ctrl+O", self.window, self.openOpenProjectDialog) QtWidgets.QShortcut("Ctrl+N", self.window, self.createNewProject) + QtWidgets.QShortcut("Ctrl+Alt+Shift+R", self.window, self.drawPreview) QtWidgets.QShortcut( "Ctrl+T", self.window, @@ -585,6 +586,7 @@ class MainWindow(QtWidgets.QMainWindow): self.autosave(force) self.updateWindowTitle() + @QtCore.pyqtSlot(QtGui.QImage) def showPreviewImage(self, image): self.previewWindow.changePixmap(image) diff --git a/src/preview_thread.py b/src/preview_thread.py index fb3b792..4ffb7f6 100644 --- a/src/preview_thread.py +++ b/src/preview_thread.py @@ -10,12 +10,12 @@ import core from queue import Queue, Empty import os -from frame import FloodFrame +from frame import Checkerboard class Worker(QtCore.QObject): - imageCreated = pyqtSignal(['QImage']) + imageCreated = pyqtSignal(QtGui.QImage) error = pyqtSignal() def __init__(self, parent=None, queue=None): @@ -24,14 +24,12 @@ class Worker(QtCore.QObject): parent.processTask.connect(self.process) self.parent = parent self.core = self.parent.core + self.settings = self.parent.core.settings self.queue = queue - self.width = int(self.core.settings.value('outputWidth')) - self.height = int(self.core.settings.value('outputHeight')) - # create checkerboard background to represent transparency - self.background = FloodFrame(1920, 1080, (0, 0, 0, 0)) - self.background.paste(Image.open(os.path.join( - self.core.wd, "background.png"))) + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) + self.background = Checkerboard(width, height) @pyqtSlot(list) def createPreviewImage(self, components): @@ -42,6 +40,8 @@ class Worker(QtCore.QObject): @pyqtSlot() def process(self): + width = int(self.settings.value('outputWidth')) + height = int(self.settings.value('outputHeight')) try: nextPreviewInformation = self.queue.get(block=False) while self.queue.qsize() >= 2: @@ -50,22 +50,27 @@ class Worker(QtCore.QObject): except Empty: continue - if self.background.width != self.width: - self.background = self.background.resize( - (self.width, self.height)) + if self.background.width != width \ + or self.background.height != height: + self.background = Checkerboard(width, height) + frame = self.background.copy() components = nextPreviewInformation["components"] for component in reversed(components): try: + newFrame = component.previewRender(self) frame = Image.alpha_composite( - frame, component.previewRender(self) + frame, newFrame ) except ValueError as e: errMsg = "Bad frame returned by %s's preview renderer. " \ - "%s. This is a fatal error." % ( - str(component), str(e).capitalize() + "%s. New frame size was %s*%s; should be %s*%s. " \ + "This is a fatal error." % ( + str(component), str(e).capitalize(), + newFrame.width, newFrame.height, + width, height ) print(errMsg) self.parent.showMessage( @@ -76,8 +81,11 @@ class Worker(QtCore.QObject): ) self.error.emit() break + except RuntimeError as e: + print(e) else: - self.imageCreated.emit(QtGui.QImage(ImageQt(frame))) + self.frame = ImageQt(frame) + self.imageCreated.emit(QtGui.QImage(self.frame)) except Empty: True diff --git a/src/video_thread.py b/src/video_thread.py index b00d512..f736013 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -20,7 +20,7 @@ import signal import core from toolkit import openPipe, checkOutput -from frame import FloodFrame +from frame import Checkerboard class Worker(QtCore.QObject): @@ -56,8 +56,10 @@ class Worker(QtCore.QObject): frame = None for compNo, comp in reversed(list(enumerate(self.components))): - if compNo in self.staticComponents and \ - self.staticComponents[compNo] is not None: + if compNo in self.staticComponents: + if self.staticComponents[compNo] is None: + # this layer was merged into a following layer + continue # static component if frame is None: # bottom-most layer frame = self.staticComponents[compNo] @@ -93,10 +95,7 @@ class Worker(QtCore.QObject): Grabs frames from the previewQueue, adds them to the checkerboard and emits a final QImage to the MainWindow for the live preview ''' - background = FloodFrame(1920, 1080, (0, 0, 0, 0)) - background.paste(Image.open(os.path.join( - self.core.wd, "background.png"))) - background = background.resize((self.width, self.height)) + background = Checkerboard(self.width, self.height) while not self.stopped: audioI, frame = self.previewQueue.get() @@ -164,8 +163,20 @@ class Worker(QtCore.QObject): self.staticComponents[compNo] = \ comp.frameRender(compNo, 0).copy() + # Merge consecutive static component frames together + for compNo in range(len(self.components), 0, -1): + if compNo not in self.staticComponents \ + or compNo - 1 not in self.staticComponents: + continue + self.staticComponents[compNo - 1] = Image.alpha_composite( + self.staticComponents.pop(compNo), + self.staticComponents[compNo - 1] + ) + self.staticComponents[compNo] = None + ffmpegCommand = self.core.createFfmpegCommand(inputFile, outputFile) - print(ffmpegCommand) + print('###### FFMPEG COMMAND ######\n %s' % " ".join(ffmpegCommand)) + print('###### -------------- ######') self.out_pipe = openPipe( ffmpegCommand, stdin=sp.PIPE, stdout=sys.stdout, stderr=sys.stdout ) |
