merge consecutive static components
This commit is contained in:
parent
2e37dafd70
commit
8811b699a9
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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, {})
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
21
src/frame.py
21
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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
Reference in New Issue