aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorBrianna2017-07-27 22:47:40 -0400
committerGitHub2017-07-27 22:47:40 -0400
commitae2af28808816d67e964b63bee1b5dbc18073672 (patch)
tree93818b30c43761288e0414e620e27871ac32cc8d /src/components
parentc799305eff66432d1d42ff3a8d1b7fd24448e1b6 (diff)
parent6ecb6df23628de65c9efd8cac4810fdf74238c3d (diff)
Merge pull request #49 from djfun/toolkit
Code reorganization, more readable component code, better error messages
Diffstat (limited to 'src/components')
-rw-r--r--src/components/color.py164
-rw-r--r--src/components/image.py98
-rw-r--r--src/components/original.py87
-rw-r--r--src/components/sound.py70
-rw-r--r--src/components/text.py109
-rw-r--r--src/components/video.py212
6 files changed, 262 insertions, 478 deletions
diff --git a/src/components/color.py b/src/components/color.py
index ef4dd95..2abd79a 100644
--- a/src/components/color.py
+++ b/src/components/color.py
@@ -5,26 +5,23 @@ from PIL.ImageQt import ImageQt
import os
from component import Component
-from frame import BlankFrame, FloodFrame, FramePainter, PaintColor
+from toolkit.frame import BlankFrame, FloodFrame, FramePainter, PaintColor
+from toolkit import rgbFromString, pickColor
class Component(Component):
- '''Color'''
-
- modified = QtCore.pyqtSignal(int, dict)
-
- def widget(self, parent):
- self.parent = parent
- self.settings = self.parent.core.settings
- page = self.loadUi('color.ui')
+ name = 'Color'
+ version = '1.0.0'
+ def widget(self, *args):
self.color1 = (0, 0, 0)
self.color2 = (133, 133, 133)
self.x = 0
self.y = 0
+ super().widget(*args)
- page.lineEdit_color1.setText('%s,%s,%s' % self.color1)
- page.lineEdit_color2.setText('%s,%s,%s' % self.color2)
+ self.page.lineEdit_color1.setText('%s,%s,%s' % self.color1)
+ self.page.lineEdit_color2.setText('%s,%s,%s' % self.color2)
btnStyle1 = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.color1).name()
@@ -32,68 +29,55 @@ class Component(Component):
btnStyle2 = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.color2).name()
- page.pushButton_color1.setStyleSheet(btnStyle1)
- page.pushButton_color2.setStyleSheet(btnStyle2)
- page.pushButton_color1.clicked.connect(lambda: self.pickColor(1))
- page.pushButton_color2.clicked.connect(lambda: self.pickColor(2))
+ self.page.pushButton_color1.setStyleSheet(btnStyle1)
+ self.page.pushButton_color2.setStyleSheet(btnStyle2)
+ self.page.pushButton_color1.clicked.connect(lambda: self.pickColor(1))
+ self.page.pushButton_color2.clicked.connect(lambda: self.pickColor(2))
# disable color #2 until non-default 'fill' option gets changed
- page.lineEdit_color2.setDisabled(True)
- page.pushButton_color2.setDisabled(True)
- page.spinBox_x.valueChanged.connect(self.update)
- page.spinBox_y.valueChanged.connect(self.update)
- page.spinBox_width.setValue(
+ self.page.lineEdit_color2.setDisabled(True)
+ self.page.pushButton_color2.setDisabled(True)
+ self.page.spinBox_width.setValue(
int(self.settings.value("outputWidth")))
- page.spinBox_height.setValue(
+ self.page.spinBox_height.setValue(
int(self.settings.value("outputHeight")))
- page.lineEdit_color1.textChanged.connect(self.update)
- page.lineEdit_color2.textChanged.connect(self.update)
- page.spinBox_x.valueChanged.connect(self.update)
- page.spinBox_y.valueChanged.connect(self.update)
- page.spinBox_width.valueChanged.connect(self.update)
- page.spinBox_height.valueChanged.connect(self.update)
- page.checkBox_trans.stateChanged.connect(self.update)
-
self.fillLabels = [
'Solid',
'Linear Gradient',
'Radial Gradient',
]
for label in self.fillLabels:
- page.comboBox_fill.addItem(label)
- page.comboBox_fill.setCurrentIndex(0)
- page.comboBox_fill.currentIndexChanged.connect(self.update)
- page.comboBox_spread.currentIndexChanged.connect(self.update)
- page.spinBox_radialGradient_end.valueChanged.connect(self.update)
- page.spinBox_radialGradient_start.valueChanged.connect(self.update)
- page.spinBox_radialGradient_spread.valueChanged.connect(self.update)
- page.spinBox_linearGradient_end.valueChanged.connect(self.update)
- page.spinBox_linearGradient_start.valueChanged.connect(self.update)
- page.checkBox_stretch.stateChanged.connect(self.update)
-
- self.page = page
- return page
+ 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,
+ }, presetNames={
+ 'sizeWidth': 'width',
+ 'sizeHeight': 'height',
+ }
+ )
def update(self):
- self.color1 = self.RGBFromString(self.page.lineEdit_color1.text())
- self.color2 = self.RGBFromString(self.page.lineEdit_color2.text())
- self.x = self.page.spinBox_x.value()
- self.y = self.page.spinBox_y.value()
- self.sizeWidth = self.page.spinBox_width.value()
- self.sizeHeight = self.page.spinBox_height.value()
- self.trans = self.page.checkBox_trans.isChecked()
- self.spread = self.page.comboBox_spread.currentIndex()
-
- self.RG_start = self.page.spinBox_radialGradient_start.value()
- self.RG_end = self.page.spinBox_radialGradient_end.value()
- self.RG_centre = self.page.spinBox_radialGradient_spread.value()
- self.stretch = self.page.checkBox_stretch.isChecked()
- self.LG_start = self.page.spinBox_linearGradient_start.value()
- self.LG_end = self.page.spinBox_linearGradient_end.value()
-
- self.fillType = self.page.comboBox_fill.currentIndex()
- if self.fillType == 0:
+ self.color1 = rgbFromString(self.page.lineEdit_color1.text())
+ self.color2 = rgbFromString(self.page.lineEdit_color2.text())
+
+ 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)
@@ -105,25 +89,21 @@ class Component(Component):
self.page.checkBox_trans.setEnabled(True)
self.page.checkBox_stretch.setEnabled(True)
self.page.comboBox_spread.setEnabled(True)
- if self.trans:
+ if self.page.checkBox_trans.isChecked():
self.page.lineEdit_color2.setEnabled(False)
self.page.pushButton_color2.setEnabled(False)
- self.page.fillWidget.setCurrentIndex(self.fillType)
+ self.page.fillWidget.setCurrentIndex(fillType)
super().update()
- def previewRender(self, previewWorker):
- width = int(self.settings.value('outputWidth'))
- height = int(self.settings.value('outputHeight'))
- return self.drawFrame(width, height)
+ def previewRender(self):
+ return self.drawFrame(self.width, self.height)
def properties(self):
return ['static']
- def frameRender(self, layerNo, frameNo):
- width = int(self.settings.value('outputWidth'))
- height = int(self.settings.value('outputHeight'))
- return self.drawFrame(width, height)
+ def frameRender(self, frameNo):
+ return self.drawFrame(self.width, self.height)
def drawFrame(self, width, height):
r, g, b = self.color1
@@ -181,25 +161,11 @@ class Component(Component):
return image.finalize()
- def loadPreset(self, pr, presetName=None):
- super().loadPreset(pr, presetName)
+ def loadPreset(self, pr, *args):
+ super().loadPreset(pr, *args)
- self.page.comboBox_fill.setCurrentIndex(pr['fillType'])
self.page.lineEdit_color1.setText('%s,%s,%s' % pr['color1'])
self.page.lineEdit_color2.setText('%s,%s,%s' % pr['color2'])
- self.page.spinBox_x.setValue(pr['x'])
- self.page.spinBox_y.setValue(pr['y'])
- self.page.spinBox_width.setValue(pr['width'])
- self.page.spinBox_height.setValue(pr['height'])
- self.page.checkBox_trans.setChecked(pr['trans'])
-
- self.page.spinBox_radialGradient_start.setValue(pr['RG_start'])
- self.page.spinBox_radialGradient_end.setValue(pr['RG_end'])
- self.page.spinBox_radialGradient_spread.setValue(pr['RG_centre'])
- self.page.spinBox_linearGradient_start.setValue(pr['LG_start'])
- self.page.spinBox_linearGradient_end.setValue(pr['LG_end'])
- self.page.checkBox_stretch.setChecked(pr['stretch'])
- self.page.comboBox_spread.setCurrentIndex(pr['spread'])
btnStyle1 = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*pr['color1']).name()
@@ -209,27 +175,13 @@ class Component(Component):
self.page.pushButton_color2.setStyleSheet(btnStyle2)
def savePreset(self):
- return {
- 'preset': self.currentPreset,
- 'color1': self.color1,
- 'color2': self.color2,
- 'x': self.x,
- 'y': self.y,
- 'fillType': self.fillType,
- 'width': self.sizeWidth,
- 'height': self.sizeHeight,
- 'trans': self.trans,
- 'stretch': self.stretch,
- 'spread': self.spread,
- 'RG_start': self.RG_start,
- 'RG_end': self.RG_end,
- 'RG_centre': self.RG_centre,
- 'LG_start': self.LG_start,
- 'LG_end': self.LG_end,
- }
+ saveValueStore = super().savePreset()
+ saveValueStore['color1'] = self.color1
+ saveValueStore['color2'] = self.color2
+ return saveValueStore
def pickColor(self, num):
- RGBstring, btnStyle = super().pickColor()
+ RGBstring, btnStyle = pickColor()
if not RGBstring:
return
if num == 1:
@@ -243,7 +195,7 @@ class Component(Component):
print('Specify a color:\n color=255,255,255')
def command(self, arg):
- if not arg.startswith('preset=') and '=' in arg:
+ if '=' in arg:
key, arg = arg.split('=', 1)
if key == 'color':
self.page.lineEdit_color1.setText(arg)
diff --git a/src/components/image.py b/src/components/image.py
index c0d1c0d..a96f127 100644
--- a/src/components/image.py
+++ b/src/components/image.py
@@ -3,48 +3,36 @@ from PyQt5 import QtGui, QtCore, QtWidgets
import os
from component import Component
-from frame import BlankFrame
+from toolkit.frame import BlankFrame
class Component(Component):
- '''Image'''
-
- modified = QtCore.pyqtSignal(int, dict)
-
- def widget(self, parent):
- self.parent = parent
- self.settings = self.parent.core.settings
- page = self.loadUi('image.ui')
-
- page.lineEdit_image.textChanged.connect(self.update)
- 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)
-
- self.page = page
- return page
-
- def update(self):
- 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()
-
- super().update()
-
- def previewRender(self, previewWorker):
- width = int(self.settings.value('outputWidth'))
- height = int(self.settings.value('outputHeight'))
- return self.drawFrame(width, height)
+ name = 'Image'
+ version = '1.0.0'
+
+ 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,
+ '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',
+ },
+ )
+
+ def previewRender(self):
+ return self.drawFrame(self.width, self.height)
def properties(self):
props = ['static']
@@ -58,10 +46,8 @@ class Component(Component):
if not os.path.exists(self.imagePath):
return "The image selected does not exist!"
- def frameRender(self, layerNo, frameNo):
- width = int(self.settings.value('outputWidth'))
- height = int(self.settings.value('outputHeight'))
- return self.drawFrame(width, height)
+ def frameRender(self, frameNo):
+ return self.drawFrame(self.width, self.height)
def drawFrame(self, width, height):
frame = BlankFrame(width, height)
@@ -89,30 +75,6 @@ class Component(Component):
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,
- }
-
def pickImage(self):
imgDir = self.settings.value("componentDir", os.path.expanduser("~"))
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
@@ -124,7 +86,7 @@ class Component(Component):
self.update()
def command(self, arg):
- if not arg.startswith('preset=') and '=' in arg:
+ if '=' in arg:
key, arg = arg.split('=', 1)
if key == 'path' and os.path.exists(arg):
try:
diff --git a/src/components/original.py b/src/components/original.py
index f5776a4..3d1a574 100644
--- a/src/components/original.py
+++ b/src/components/original.py
@@ -7,80 +7,63 @@ import time
from copy import copy
from component import Component
-from frame import BlankFrame
+from toolkit.frame import BlankFrame
+from toolkit import rgbFromString, pickColor
class Component(Component):
- '''Classic Visualizer'''
+ name = 'Classic Visualizer'
+ version = '1.0.0'
- modified = QtCore.pyqtSignal(int, dict)
-
- @classmethod
- def names(cls):
+ def names(*args):
return ['Original Audio Visualization']
- def widget(self, parent):
- self.parent = parent
- self.settings = self.parent.core.settings
+ def widget(self, *args):
self.visColor = (255, 255, 255)
self.scale = 20
self.y = 0
- self.canceled = False
-
- page = self.loadUi('original.ui')
- page.comboBox_visLayout.addItem("Classic")
- page.comboBox_visLayout.addItem("Split")
- page.comboBox_visLayout.addItem("Bottom")
- page.comboBox_visLayout.addItem("Top")
- page.comboBox_visLayout.setCurrentIndex(0)
- page.comboBox_visLayout.currentIndexChanged.connect(self.update)
- page.lineEdit_visColor.setText('%s,%s,%s' % self.visColor)
- page.pushButton_visColor.clicked.connect(lambda: self.pickColor())
+ 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('%s,%s,%s' % self.visColor)
+ self.page.pushButton_visColor.clicked.connect(lambda: self.pickColor())
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.visColor).name()
- page.pushButton_visColor.setStyleSheet(btnStyle)
- page.lineEdit_visColor.textChanged.connect(self.update)
- page.spinBox_scale.valueChanged.connect(self.update)
- page.spinBox_y.valueChanged.connect(self.update)
+ self.page.pushButton_visColor.setStyleSheet(btnStyle)
- self.page = page
- return page
+ self.trackWidgets({
+ 'layout': self.page.comboBox_visLayout,
+ 'scale': self.page.spinBox_scale,
+ 'y': self.page.spinBox_y,
+ })
def update(self):
- self.layout = self.page.comboBox_visLayout.currentIndex()
- self.visColor = self.RGBFromString(self.page.lineEdit_visColor.text())
- self.scale = self.page.spinBox_scale.value()
- self.y = self.page.spinBox_y.value()
-
+ self.visColor = rgbFromString(self.page.lineEdit_visColor.text())
super().update()
- def loadPreset(self, pr, presetName=None):
- super().loadPreset(pr, presetName)
+ def loadPreset(self, pr, *args):
+ super().loadPreset(pr, *args)
self.page.lineEdit_visColor.setText('%s,%s,%s' % pr['visColor'])
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*pr['visColor']).name()
self.page.pushButton_visColor.setStyleSheet(btnStyle)
- self.page.comboBox_visLayout.setCurrentIndex(pr['layout'])
- self.page.spinBox_scale.setValue(pr['scale'])
- self.page.spinBox_y.setValue(pr['y'])
def savePreset(self):
- return {
- 'preset': self.currentPreset,
- 'layout': self.layout,
- 'visColor': self.visColor,
- 'scale': self.scale,
- 'y': self.y,
- }
-
- def previewRender(self, previewWorker):
+ saveValueStore = super().savePreset()
+ saveValueStore['visColor'] = self.visColor
+ return saveValueStore
+
+ def previewRender(self):
spectrum = numpy.fromfunction(
lambda x: float(self.scale)/2500*(x-128)**2, (255,), dtype="int16")
- width = int(self.settings.value('outputWidth'))
- height = int(self.settings.value('outputHeight'))
return self.drawBars(
- width, height, spectrum, self.visColor, self.layout
+ self.width, self.height, spectrum, self.visColor, self.layout
)
def preFrameRender(self, **kwargs):
@@ -89,8 +72,6 @@ class Component(Component):
self.smoothConstantUp = 0.8
self.lastSpectrum = None
self.spectrumArray = {}
- 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:
@@ -108,7 +89,7 @@ class Component(Component):
self.progressBarSetText.emit(pStr)
self.progressBarUpdate.emit(int(progress))
- def frameRender(self, layerNo, frameNo):
+ def frameRender(self, frameNo):
arrayNo = frameNo * self.sampleSize
return self.drawBars(
self.width, self.height,
@@ -116,7 +97,7 @@ class Component(Component):
self.visColor, self.layout)
def pickColor(self):
- RGBstring, btnStyle = super().pickColor()
+ RGBstring, btnStyle = pickColor()
if not RGBstring:
return
self.page.lineEdit_visColor.setText(RGBstring)
@@ -208,7 +189,7 @@ class Component(Component):
return im
def command(self, arg):
- if not arg.startswith('preset=') and '=' in arg:
+ if '=' in arg:
key, arg = arg.split('=', 1)
try:
if key == 'color':
diff --git a/src/components/sound.py b/src/components/sound.py
index bd7d002..26ecf93 100644
--- a/src/components/sound.py
+++ b/src/components/sound.py
@@ -2,42 +2,24 @@ from PyQt5 import QtGui, QtCore, QtWidgets
import os
from component import Component
-from frame import BlankFrame
+from toolkit.frame import BlankFrame
class Component(Component):
- '''Sound'''
-
- modified = QtCore.pyqtSignal(int, dict)
-
- def widget(self, parent):
- self.parent = parent
- self.settings = parent.settings
- page = self.loadUi('sound.ui')
-
- page.lineEdit_sound.textChanged.connect(self.update)
- page.pushButton_sound.clicked.connect(self.pickSound)
- page.checkBox_chorus.stateChanged.connect(self.update)
- page.spinBox_delay.valueChanged.connect(self.update)
- page.spinBox_volume.valueChanged.connect(self.update)
-
- self.page = page
- return page
-
- def update(self):
- self.sound = self.page.lineEdit_sound.text()
- self.delay = self.page.spinBox_delay.value()
- self.volume = self.page.spinBox_volume.value()
- self.chorus = self.page.checkBox_chorus.isChecked()
- super().update()
-
- def previewRender(self, previewWorker):
- width = int(previewWorker.core.settings.value('outputWidth'))
- height = int(previewWorker.core.settings.value('outputHeight'))
- return BlankFrame(width, height)
-
- def preFrameRender(self, **kwargs):
- pass
+ 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']
@@ -73,31 +55,11 @@ class Component(Component):
self.page.lineEdit_sound.setText(filename)
self.update()
- def frameRender(self, layerNo, frameNo):
- width = int(self.settings.value('outputWidth'))
- height = int(self.settings.value('outputHeight'))
- return BlankFrame(width, height)
-
- def loadPreset(self, pr, presetName=None):
- super().loadPreset(pr, presetName)
- self.page.lineEdit_sound.setText(pr['sound'])
- self.page.checkBox_chorus.setChecked(pr['chorus'])
- self.page.spinBox_delay.setValue(pr['delay'])
- self.page.spinBox_volume.setValue(pr['volume'])
-
- def savePreset(self):
- return {
- 'sound': self.sound,
- 'chorus': self.chorus,
- 'delay': self.delay,
- 'volume': self.volume,
- }
-
def commandHelp(self):
print('Path to audio file:\n path=/filepath/to/sound.ogg')
def command(self, arg):
- if not arg.startswith('preset=') and '=' in arg:
+ if '=' in arg:
key, arg = arg.split('=', 1)
if key == 'path':
if '*%s' % os.path.splitext(arg)[1] \
diff --git a/src/components/text.py b/src/components/text.py
index 19460e5..8a302ff 100644
--- a/src/components/text.py
+++ b/src/components/text.py
@@ -4,24 +4,22 @@ from PyQt5 import QtGui, QtCore, QtWidgets
import os
from component import Component
-from frame import FramePainter
+from toolkit.frame import FramePainter
+from toolkit import rgbFromString, pickColor
class Component(Component):
- '''Title Text'''
-
- modified = QtCore.pyqtSignal(int, dict)
+ name = 'Title Text'
+ version = '1.0.0'
def __init__(self, *args):
super().__init__(*args)
self.titleFont = QFont()
- def widget(self, parent):
- self.parent = parent
- self.settings = self.parent.core.settings
+ def widget(self, *args):
+ super().widget(*args)
height = int(self.settings.value('outputHeight'))
width = int(self.settings.value('outputWidth'))
-
self.textColor = (255, 255, 255)
self.title = 'Text'
self.alignment = 1
@@ -30,41 +28,36 @@ class Component(Component):
self.xPosition = width / 2 - fm.width(self.title)/2
self.yPosition = height / 2 * 1.036
- page = self.loadUi('text.ui')
- page.comboBox_textAlign.addItem("Left")
- page.comboBox_textAlign.addItem("Middle")
- page.comboBox_textAlign.addItem("Right")
+ self.page.comboBox_textAlign.addItem("Left")
+ self.page.comboBox_textAlign.addItem("Middle")
+ self.page.comboBox_textAlign.addItem("Right")
- page.lineEdit_textColor.setText('%s,%s,%s' % self.textColor)
- page.pushButton_textColor.clicked.connect(self.pickColor)
+ self.page.lineEdit_textColor.setText('%s,%s,%s' % self.textColor)
+ self.page.pushButton_textColor.clicked.connect(self.pickColor)
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.textColor).name()
- page.pushButton_textColor.setStyleSheet(btnStyle)
-
- page.lineEdit_title.setText(self.title)
- page.comboBox_textAlign.setCurrentIndex(int(self.alignment))
- page.spinBox_fontSize.setValue(int(self.fontSize))
- page.spinBox_xTextAlign.setValue(int(self.xPosition))
- page.spinBox_yTextAlign.setValue(int(self.yPosition))
-
- page.fontComboBox_titleFont.currentFontChanged.connect(self.update)
- page.lineEdit_title.textChanged.connect(self.update)
- page.comboBox_textAlign.currentIndexChanged.connect(self.update)
- page.spinBox_xTextAlign.valueChanged.connect(self.update)
- page.spinBox_yTextAlign.valueChanged.connect(self.update)
- page.spinBox_fontSize.valueChanged.connect(self.update)
- page.lineEdit_textColor.textChanged.connect(self.update)
- self.page = page
- return page
+ self.page.pushButton_textColor.setStyleSheet(btnStyle)
+
+ self.page.lineEdit_title.setText(self.title)
+ self.page.comboBox_textAlign.setCurrentIndex(int(self.alignment))
+ self.page.spinBox_fontSize.setValue(int(self.fontSize))
+ self.page.spinBox_xTextAlign.setValue(int(self.xPosition))
+ self.page.spinBox_yTextAlign.setValue(int(self.yPosition))
+
+ self.page.fontComboBox_titleFont.currentFontChanged.connect(
+ self.update
+ )
+ self.trackWidgets({
+ '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,
+ })
def update(self):
- self.title = self.page.lineEdit_title.text()
- self.alignment = self.page.comboBox_textAlign.currentIndex()
self.titleFont = self.page.fontComboBox_titleFont.currentFont()
- self.fontSize = self.page.spinBox_fontSize.value()
- self.xPosition = self.page.spinBox_xTextAlign.value()
- self.yPosition = self.page.spinBox_yTextAlign.value()
- self.textColor = self.RGBFromString(
+ self.textColor = rgbFromString(
self.page.lineEdit_textColor.text())
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.textColor).name()
@@ -87,38 +80,25 @@ class Component(Component):
x = self.xPosition - offset
return x, self.yPosition
- def loadPreset(self, pr, presetName=None):
- super().loadPreset(pr, presetName)
+ def loadPreset(self, pr, *args):
+ super().loadPreset(pr, *args)
- self.page.lineEdit_title.setText(pr['title'])
font = QFont()
font.fromString(pr['titleFont'])
self.page.fontComboBox_titleFont.setCurrentFont(font)
- self.page.spinBox_fontSize.setValue(pr['fontSize'])
- self.page.comboBox_textAlign.setCurrentIndex(pr['alignment'])
- self.page.spinBox_xTextAlign.setValue(pr['xPosition'])
- self.page.spinBox_yTextAlign.setValue(pr['yPosition'])
self.page.lineEdit_textColor.setText('%s,%s,%s' % pr['textColor'])
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*pr['textColor']).name()
self.page.pushButton_textColor.setStyleSheet(btnStyle)
def savePreset(self):
- return {
- 'preset': self.currentPreset,
- 'title': self.title,
- 'titleFont': self.titleFont.toString(),
- 'alignment': self.alignment,
- 'fontSize': self.fontSize,
- 'xPosition': self.xPosition,
- 'yPosition': self.yPosition,
- 'textColor': self.textColor
- }
-
- def previewRender(self, previewWorker):
- width = int(self.settings.value('outputWidth'))
- height = int(self.settings.value('outputHeight'))
- return self.addText(width, height)
+ saveValueStore = super().savePreset()
+ saveValueStore['titleFont'] = self.titleFont.toString()
+ saveValueStore['textColor'] = self.textColor
+ return saveValueStore
+
+ def previewRender(self):
+ return self.addText(self.width, self.height)
def properties(self):
props = ['static']
@@ -129,13 +109,10 @@ class Component(Component):
def error(self):
return "No text provided."
- def frameRender(self, layerNo, frameNo):
- width = int(self.settings.value('outputWidth'))
- height = int(self.settings.value('outputHeight'))
- return self.addText(width, height)
+ def frameRender(self, frameNo):
+ return self.addText(self.width, self.height)
def addText(self, width, height):
-
image = FramePainter(width, height)
self.titleFont.setPixelSize(self.fontSize)
image.setFont(self.titleFont)
@@ -146,7 +123,7 @@ class Component(Component):
return image.finalize()
def pickColor(self):
- RGBstring, btnStyle = super().pickColor()
+ RGBstring, btnStyle = pickColor()
if not RGBstring:
return
self.page.lineEdit_textColor.setText(RGBstring)
@@ -159,7 +136,7 @@ class Component(Component):
print('Set custom x, y position:\n x=500 y=500')
def command(self, arg):
- if not arg.startswith('preset=') and '=' in arg:
+ if '=' in arg:
key, arg = arg.split('=', 1)
if key == 'color':
self.page.lineEdit_textColor.setText(arg)
diff --git a/src/components/video.py b/src/components/video.py
index 9e3db30..b2487c1 100644
--- a/src/components/video.py
+++ b/src/components/video.py
@@ -3,19 +3,25 @@ from PyQt5 import QtGui, QtCore, QtWidgets
import os
import math
import subprocess
+import signal
import threading
from queue import PriorityQueue
-from component import Component, BadComponentInit
-from frame import BlankFrame
+from component import Component, ComponentError
+from toolkit.frame import BlankFrame
+from toolkit.ffmpeg import testAudioStream
from toolkit import openPipe, checkOutput
class Video:
- '''Video Component Frame-Fetcher'''
+ '''Opens a pipe to ffmpeg and stores a buffer of raw video frames.'''
+
+ # error from the thread used to fill the buffer
+ threadError = None
+
def __init__(self, **kwargs):
mandatoryArgs = [
- 'ffmpeg', # path to ffmpeg, usually core.FFMPEG_BIN
+ 'ffmpeg', # path to ffmpeg, usually self.core.FFMPEG_BIN
'videoPath',
'width',
'height',
@@ -26,10 +32,7 @@ class Video:
'component', # component object
]
for arg in mandatoryArgs:
- try:
- exec('self.%s = kwargs[arg]' % arg)
- except KeyError:
- raise BadComponentInit(arg, self.__doc__)
+ setattr(self, arg, kwargs[arg])
self.frameNo = -1
self.currentFrame = 'None'
@@ -56,7 +59,7 @@ class Video:
self.thread = threading.Thread(
target=self.fillBuffer,
- name=self.__doc__
+ name='Video Frame-Fetcher'
)
self.thread.daemon = True
self.thread.start()
@@ -73,8 +76,8 @@ class Video:
self.frameBuffer.task_done()
def fillBuffer(self):
- pipe = openPipe(
- self.command, stdout=subprocess.PIPE,
+ self.pipe = openPipe(
+ self.command, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, bufsize=10**8
)
while True:
@@ -87,120 +90,89 @@ class Video:
if len(self.currentFrame) == 0:
self.frameBuffer.put((self.frameNo-1, self.lastFrame))
continue
- except AttributeError as e:
- self.parent.showMessage(
- msg='%s couldn\'t be loaded. '
- 'This is a fatal error.' % os.path.basename(
- self.videoPath
- ),
- detail=str(e),
- icon='Warning'
- )
- self.parent.stopVideo()
+ except AttributeError:
+ Video.threadError = ComponentError(self.component, 'video')
break
- self.currentFrame = pipe.stdout.read(self.chunkSize)
+ self.currentFrame = self.pipe.stdout.read(self.chunkSize)
if len(self.currentFrame) != 0:
self.frameBuffer.put((self.frameNo, self.currentFrame))
self.lastFrame = self.currentFrame
class Component(Component):
- '''Video'''
+ name = 'Video'
+ version = '1.0.0'
- modified = QtCore.pyqtSignal(int, dict)
-
- def widget(self, parent):
- self.parent = parent
- self.settings = parent.settings
- page = self.loadUi('video.ui')
+ def widget(self, *args):
self.videoPath = ''
self.badVideo = False
self.badAudio = False
self.x = 0
self.y = 0
self.loopVideo = False
-
- page.lineEdit_video.textChanged.connect(self.update)
- page.pushButton_video.clicked.connect(self.pickVideo)
- page.checkBox_loop.stateChanged.connect(self.update)
- page.checkBox_distort.stateChanged.connect(self.update)
- page.checkBox_useAudio.stateChanged.connect(self.update)
- page.spinBox_scale.valueChanged.connect(self.update)
- page.spinBox_volume.valueChanged.connect(self.update)
- page.spinBox_x.valueChanged.connect(self.update)
- page.spinBox_y.valueChanged.connect(self.update)
-
- self.page = page
- return page
+ super().widget(*args)
+ 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',
+ }
+ )
def update(self):
- self.videoPath = self.page.lineEdit_video.text()
- self.loopVideo = self.page.checkBox_loop.isChecked()
- self.useAudio = self.page.checkBox_useAudio.isChecked()
- self.distort = self.page.checkBox_distort.isChecked()
- self.scale = self.page.spinBox_scale.value()
- self.volume = self.page.spinBox_volume.value()
- self.xPosition = self.page.spinBox_x.value()
- self.yPosition = self.page.spinBox_y.value()
-
- if self.useAudio:
+ 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)
-
super().update()
- def previewRender(self, previewWorker):
- width = int(previewWorker.core.settings.value('outputWidth'))
- height = int(previewWorker.core.settings.value('outputHeight'))
- self.updateChunksize(width, height)
- frame = self.getPreviewFrame(width, height)
+ def previewRender(self):
+ self.updateChunksize()
+ frame = self.getPreviewFrame(self.width, self.height)
if not frame:
- return BlankFrame(width, height)
+ return BlankFrame(self.width, self.height)
else:
return frame
def properties(self):
props = []
- if not self.videoPath or self.badVideo \
- or not os.path.exists(self.videoPath):
- return ['error']
+ if hasattr(self.parent, 'window'):
+ outputFile = self.parent.window.lineEdit_outputFile.text()
+ else:
+ outputFile = str(self.parent.args.output)
+
+ if not self.videoPath:
+ self.lockError("There is no video selected.")
+ elif self.badVideo:
+ self.lockError("Could not identify an audio stream in this video.")
+ elif not os.path.exists(self.videoPath):
+ self.lockError("The video selected does not exist!")
+ elif os.path.realpath(self.videoPath) == os.path.realpath(outputFile):
+ self.lockError("Input and output paths match.")
if self.useAudio:
props.append('audio')
- self.testAudioStream()
- if self.badAudio:
- return ['error']
+ if not testAudioStream(self.videoPath) \
+ and self.error() is None:
+ self.lockError(
+ "Could not identify an audio stream in this video.")
return props
- def error(self):
- if self.badAudio:
- return "Could not identify an audio stream in this video."
- if not self.videoPath:
- return "There is no video selected."
- if not os.path.exists(self.videoPath):
- return "The video selected does not exist!"
- if self.badVideo:
- return "The video selected is corrupt!"
-
- def testAudioStream(self):
- # test if an audio stream really exists
- audioTestCommand = [
- self.core.FFMPEG_BIN,
- '-i', self.videoPath,
- '-vn', '-f', 'null', '-'
- ]
- try:
- checkOutput(audioTestCommand, stderr=subprocess.DEVNULL)
- except subprocess.CalledProcessError:
- self.badAudio = True
- else:
- self.badAudio = False
-
def audio(self):
params = {}
if self.volume != 1.0:
@@ -209,47 +181,23 @@ class Component(Component):
def preFrameRender(self, **kwargs):
super().preFrameRender(**kwargs)
- width = int(self.worker.core.settings.value('outputWidth'))
- height = int(self.worker.core.settings.value('outputHeight'))
- self.blankFrame_ = BlankFrame(width, height)
- self.updateChunksize(width, height)
+ self.updateChunksize()
self.video = Video(
ffmpeg=self.core.FFMPEG_BIN, videoPath=self.videoPath,
- width=width, height=height, chunkSize=self.chunkSize,
+ width=self.width, height=self.height, chunkSize=self.chunkSize,
frameRate=int(self.settings.value("outputFrameRate")),
parent=self.parent, loopVideo=self.loopVideo,
component=self, scale=self.scale
) if os.path.exists(self.videoPath) else None
- def frameRender(self, layerNo, frameNo):
- if self.video:
- return self.video.frame(frameNo)
- else:
- return self.blankFrame_
-
- def loadPreset(self, pr, presetName=None):
- super().loadPreset(pr, presetName)
- self.page.lineEdit_video.setText(pr['video'])
- self.page.checkBox_loop.setChecked(pr['loop'])
- self.page.checkBox_useAudio.setChecked(pr['useAudio'])
- self.page.checkBox_distort.setChecked(pr['distort'])
- self.page.spinBox_scale.setValue(pr['scale'])
- self.page.spinBox_volume.setValue(pr['volume'])
- self.page.spinBox_x.setValue(pr['x'])
- self.page.spinBox_y.setValue(pr['y'])
-
- def savePreset(self):
- return {
- 'preset': self.currentPreset,
- 'video': self.videoPath,
- 'loop': self.loopVideo,
- 'useAudio': self.useAudio,
- 'distort': self.distort,
- 'scale': self.scale,
- 'volume': self.volume,
- 'x': self.xPosition,
- 'y': self.yPosition,
- }
+ def frameRender(self, frameNo):
+ if Video.threadError is not None:
+ raise Video.threadError
+ return self.video.frame(frameNo)
+
+ def postFrameRender(self):
+ self.video.pipe.stdout.close()
+ self.video.pipe.send_signal(signal.SIGINT)
def pickVideo(self):
imgDir = self.settings.value("componentDir", os.path.expanduser("~"))
@@ -267,7 +215,7 @@ class Component(Component):
return
command = [
- self.parent.core.FFMPEG_BIN,
+ self.core.FFMPEG_BIN,
'-thread_queue_size', '512',
'-i', self.videoPath,
'-f', 'image2pipe',
@@ -279,23 +227,25 @@ class Component(Component):
'-vframes', '1',
]
pipe = openPipe(
- command, stdout=subprocess.PIPE,
+ command, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, bufsize=10**8
)
byteFrame = pipe.stdout.read(self.chunkSize)
- frame = finalizeFrame(self, byteFrame, width, height)
pipe.stdout.close()
- pipe.kill()
+ pipe.send_signal(signal.SIGINT)
+ frame = finalizeFrame(self, byteFrame, width, height)
return frame
- def updateChunksize(self, width, height):
+ def updateChunksize(self):
if self.scale != 100 and not self.distort:
- width, height = scale(self.scale, width, height, int)
- self.chunkSize = 4*width*height
+ 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 not arg.startswith('preset=') and '=' in 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: