merge consecutive static components

This commit is contained in:
tassaron 2017-07-13 00:05:11 -04:00
parent 2e37dafd70
commit 8811b699a9
10 changed files with 100 additions and 55 deletions

View File

@ -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):

View File

@ -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):

View File

@ -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:

View File

@ -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):

View File

@ -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, {})

View File

@ -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
)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
)