Redesigned preset UI + video & image component scaling/positioning
Added preset manager
This commit is contained in:
commit
fc7ee6d8e5
|
@ -1,7 +1,18 @@
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui, QtCore
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
class Component:
|
class Component(QtCore.QObject):
|
||||||
|
'''A base class for components to inherit from'''
|
||||||
|
|
||||||
|
# modified = QtCore.pyqtSignal(int, bool)
|
||||||
|
|
||||||
|
def __init__(self, moduleIndex, compPos):
|
||||||
|
super().__init__()
|
||||||
|
self.currentPreset = None
|
||||||
|
self.moduleIndex = moduleIndex
|
||||||
|
self.compPos = compPos
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.__doc__
|
return self.__doc__
|
||||||
|
|
||||||
|
@ -10,18 +21,38 @@ class Component:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
# make sure your component responds to these variables in frameRender()
|
# please stop any lengthy process in response to this variable
|
||||||
self.canceled = True
|
self.canceled = True
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.canceled = False
|
self.canceled = False
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.modified.emit(self.compPos, self.savePreset())
|
||||||
|
# read your widget values, then call super().update()
|
||||||
|
|
||||||
|
def loadPreset(self, presetDict, presetName):
|
||||||
|
'''Children should take (presetDict, presetName=None) as args'''
|
||||||
|
|
||||||
|
# Use super().loadPreset(presetDict, presetName)
|
||||||
|
# Then update your widgets using the preset dict
|
||||||
|
self.currentPreset = presetName \
|
||||||
|
if presetName != None else presetDict['preset']
|
||||||
|
'''
|
||||||
|
def savePreset(self):
|
||||||
|
return {}
|
||||||
|
'''
|
||||||
def preFrameRender(self, **kwargs):
|
def preFrameRender(self, **kwargs):
|
||||||
for var, value in kwargs.items():
|
for var, value in kwargs.items():
|
||||||
exec('self.%s = value' % var)
|
exec('self.%s = value' % var)
|
||||||
|
|
||||||
|
def blankFrame(self, width, height):
|
||||||
|
return Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
||||||
|
|
||||||
def pickColor(self):
|
def pickColor(self):
|
||||||
color = QtGui.QColorDialog.getColor()
|
dialog = QtGui.QColorDialog()
|
||||||
|
dialog.setOption(QtGui.QColorDialog.ShowAlphaChannel, True)
|
||||||
|
color = dialog.getColor()
|
||||||
if color.isValid():
|
if color.isValid():
|
||||||
RGBstring = '%s,%s,%s' % (
|
RGBstring = '%s,%s,%s' % (
|
||||||
str(color.red()), str(color.green()), str(color.blue()))
|
str(color.red()), str(color.green()), str(color.blue()))
|
||||||
|
@ -57,7 +88,7 @@ class Component:
|
||||||
return page
|
return page
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
# read widget values
|
super().update()
|
||||||
self.parent.drawPreview()
|
self.parent.drawPreview()
|
||||||
|
|
||||||
def previewRender(self, previewWorker):
|
def previewRender(self, previewWorker):
|
||||||
|
@ -72,12 +103,6 @@ class Component:
|
||||||
image = Image.new("RGBA", (width, height), (0,0,0,0))
|
image = Image.new("RGBA", (width, height), (0,0,0,0))
|
||||||
return image
|
return image
|
||||||
|
|
||||||
def loadPreset(self, presetDict):
|
|
||||||
# update widgets using a preset dict
|
|
||||||
|
|
||||||
def savePreset(self):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self.canceled = True
|
self.canceled = True
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@ from . import __base__
|
||||||
|
|
||||||
class Component(__base__.Component):
|
class Component(__base__.Component):
|
||||||
'''Color'''
|
'''Color'''
|
||||||
|
|
||||||
|
modified = QtCore.pyqtSignal(int, dict)
|
||||||
|
|
||||||
def widget(self, parent):
|
def widget(self, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
page = uic.loadUi(os.path.join(
|
page = uic.loadUi(os.path.join(
|
||||||
|
@ -20,14 +23,14 @@ class Component(__base__.Component):
|
||||||
page.lineEdit_color1.setText('%s,%s,%s' % self.color1)
|
page.lineEdit_color1.setText('%s,%s,%s' % self.color1)
|
||||||
page.lineEdit_color2.setText('%s,%s,%s' % self.color2)
|
page.lineEdit_color2.setText('%s,%s,%s' % self.color2)
|
||||||
|
|
||||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
btnStyle1 = "QPushButton { background-color : %s; outline: none; }" \
|
||||||
% QColor(*self.color1).name()
|
% QColor(*self.color1).name()
|
||||||
|
|
||||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
btnStyle2 = "QPushButton { background-color : %s; outline: none; }" \
|
||||||
% QColor(*self.color2).name()
|
% QColor(*self.color2).name()
|
||||||
|
|
||||||
page.pushButton_color1.setStyleSheet(btnStyle)
|
page.pushButton_color1.setStyleSheet(btnStyle1)
|
||||||
page.pushButton_color2.setStyleSheet(btnStyle)
|
page.pushButton_color2.setStyleSheet(btnStyle2)
|
||||||
page.pushButton_color1.clicked.connect(lambda: self.pickColor(1))
|
page.pushButton_color1.clicked.connect(lambda: self.pickColor(1))
|
||||||
page.pushButton_color2.clicked.connect(lambda: self.pickColor(2))
|
page.pushButton_color2.clicked.connect(lambda: self.pickColor(2))
|
||||||
|
|
||||||
|
@ -50,6 +53,7 @@ class Component(__base__.Component):
|
||||||
self.x = self.page.spinBox_x.value()
|
self.x = self.page.spinBox_x.value()
|
||||||
self.y = self.page.spinBox_y.value()
|
self.y = self.page.spinBox_y.value()
|
||||||
self.parent.drawPreview()
|
self.parent.drawPreview()
|
||||||
|
super().update()
|
||||||
|
|
||||||
def previewRender(self, previewWorker):
|
def previewRender(self, previewWorker):
|
||||||
width = int(previewWorker.core.settings.value('outputWidth'))
|
width = int(previewWorker.core.settings.value('outputWidth'))
|
||||||
|
@ -67,23 +71,26 @@ class Component(__base__.Component):
|
||||||
|
|
||||||
def drawFrame(self, width, height):
|
def drawFrame(self, width, height):
|
||||||
r, g, b = self.color1
|
r, g, b = self.color1
|
||||||
return Image.new("RGBA", (width, height), (r, g, b, 255))
|
return self.blankFrame(width, height)
|
||||||
|
|
||||||
|
def loadPreset(self, pr, presetName=None):
|
||||||
|
super().loadPreset(pr, presetName)
|
||||||
|
|
||||||
def loadPreset(self, pr):
|
|
||||||
self.page.lineEdit_color1.setText('%s,%s,%s' % pr['color1'])
|
self.page.lineEdit_color1.setText('%s,%s,%s' % pr['color1'])
|
||||||
self.page.lineEdit_color2.setText('%s,%s,%s' % pr['color2'])
|
self.page.lineEdit_color2.setText('%s,%s,%s' % pr['color2'])
|
||||||
|
|
||||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
btnStyle1 = "QPushButton { background-color : %s; outline: none; }" \
|
||||||
% QColor(*pr['color1']).name()
|
% QColor(*pr['color1']).name()
|
||||||
|
|
||||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
btnStyle2 = "QPushButton { background-color : %s; outline: none; }" \
|
||||||
% QColor(*pr['color2']).name()
|
% QColor(*pr['color2']).name()
|
||||||
|
|
||||||
self.page.pushButton_color1.setStyleSheet(btnStyle)
|
self.page.pushButton_color1.setStyleSheet(btnStyle1)
|
||||||
self.page.pushButton_color2.setStyleSheet(btnStyle)
|
self.page.pushButton_color2.setStyleSheet(btnStyle2)
|
||||||
|
|
||||||
def savePreset(self):
|
def savePreset(self):
|
||||||
return {
|
return {
|
||||||
|
'preset': self.currentPreset,
|
||||||
'color1': self.color1,
|
'color1': self.color1,
|
||||||
'color2': self.color2,
|
'color2': self.color2,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ from . import __base__
|
||||||
|
|
||||||
class Component(__base__.Component):
|
class Component(__base__.Component):
|
||||||
'''Image'''
|
'''Image'''
|
||||||
|
|
||||||
|
modified = QtCore.pyqtSignal(int, dict)
|
||||||
|
|
||||||
def widget(self, parent):
|
def widget(self, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.settings = parent.settings
|
self.settings = parent.settings
|
||||||
|
@ -17,15 +20,25 @@ class Component(__base__.Component):
|
||||||
|
|
||||||
page.lineEdit_image.textChanged.connect(self.update)
|
page.lineEdit_image.textChanged.connect(self.update)
|
||||||
page.pushButton_image.clicked.connect(self.pickImage)
|
page.pushButton_image.clicked.connect(self.pickImage)
|
||||||
|
page.spinBox_scale.valueChanged.connect(self.update)
|
||||||
|
page.checkBox_stretch.stateChanged.connect(self.update)
|
||||||
|
page.spinBox_x.valueChanged.connect(self.update)
|
||||||
|
page.spinBox_y.valueChanged.connect(self.update)
|
||||||
|
|
||||||
self.page = page
|
self.page = page
|
||||||
return page
|
return page
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.imagePath = self.page.lineEdit_image.text()
|
self.imagePath = self.page.lineEdit_image.text()
|
||||||
|
self.scale = self.page.spinBox_scale.value()
|
||||||
|
self.xPosition = self.page.spinBox_x.value()
|
||||||
|
self.yPosition = self.page.spinBox_y.value()
|
||||||
|
self.stretched = self.page.checkBox_stretch.isChecked()
|
||||||
self.parent.drawPreview()
|
self.parent.drawPreview()
|
||||||
|
super().update()
|
||||||
|
|
||||||
def previewRender(self, previewWorker):
|
def previewRender(self, previewWorker):
|
||||||
|
self.imageFormats = previewWorker.core.imageFormats
|
||||||
width = int(previewWorker.core.settings.value('outputWidth'))
|
width = int(previewWorker.core.settings.value('outputWidth'))
|
||||||
height = int(previewWorker.core.settings.value('outputHeight'))
|
height = int(previewWorker.core.settings.value('outputHeight'))
|
||||||
return self.drawFrame(width, height)
|
return self.drawFrame(width, height)
|
||||||
|
@ -40,26 +53,41 @@ class Component(__base__.Component):
|
||||||
return self.drawFrame(width, height)
|
return self.drawFrame(width, height)
|
||||||
|
|
||||||
def drawFrame(self, width, height):
|
def drawFrame(self, width, height):
|
||||||
frame = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
frame = self.blankFrame(width, height)
|
||||||
if self.imagePath and os.path.exists(self.imagePath):
|
if self.imagePath and os.path.exists(self.imagePath):
|
||||||
image = Image.open(self.imagePath)
|
image = Image.open(self.imagePath)
|
||||||
if image.size != (width, height):
|
if self.stretched and image.size != (width, height):
|
||||||
image = image.resize((width, height), Image.ANTIALIAS)
|
image = image.resize((width, height), Image.ANTIALIAS)
|
||||||
frame.paste(image)
|
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)
|
||||||
|
frame.paste(image, box=(self.xPosition, self.yPosition))
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
def loadPreset(self, pr):
|
def loadPreset(self, pr, presetName=None):
|
||||||
|
super().loadPreset(pr, presetName)
|
||||||
self.page.lineEdit_image.setText(pr['image'])
|
self.page.lineEdit_image.setText(pr['image'])
|
||||||
|
self.page.spinBox_scale.setValue(pr['scale'])
|
||||||
|
self.page.spinBox_x.setValue(pr['x'])
|
||||||
|
self.page.spinBox_y.setValue(pr['y'])
|
||||||
|
self.page.checkBox_stretch.setChecked(pr['stretched'])
|
||||||
|
|
||||||
def savePreset(self):
|
def savePreset(self):
|
||||||
return {
|
return {
|
||||||
|
'preset': self.currentPreset,
|
||||||
'image': self.imagePath,
|
'image': self.imagePath,
|
||||||
|
'scale': self.scale,
|
||||||
|
'stretched': self.stretched,
|
||||||
|
'x': self.xPosition,
|
||||||
|
'y': self.yPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
def pickImage(self):
|
def pickImage(self):
|
||||||
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
|
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
|
||||||
filename = QtGui.QFileDialog.getOpenFileName(
|
filename = QtGui.QFileDialog.getOpenFileName(
|
||||||
self.page, "Choose Image", imgDir, "Image Files (*.jpg *.png)")
|
self.page, "Choose Image", imgDir,
|
||||||
|
"Image Files (%s)" % " ".join(self.imageFormats))
|
||||||
if filename:
|
if filename:
|
||||||
self.settings.setValue("backgroundDir", os.path.dirname(filename))
|
self.settings.setValue("backgroundDir", os.path.dirname(filename))
|
||||||
self.page.lineEdit_image.setText(filename)
|
self.page.lineEdit_image.setText(filename)
|
||||||
|
|
|
@ -124,8 +124,11 @@
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>-10000</number>
|
||||||
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>999999999</number>
|
<number>10000</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -163,10 +166,10 @@
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>0</number>
|
<number>-1000</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>999999999</number>
|
<number>1000</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
|
@ -177,6 +180,65 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkBox_stretch">
|
||||||
|
<property name="text">
|
||||||
|
<string>Stretch</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_10">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>5</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Scale</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBox_scale">
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::UpDownArrows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string>%</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>400</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import numpy
|
import numpy
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
from PyQt4 import uic, QtGui
|
from PyQt4 import uic, QtGui, QtCore
|
||||||
from PyQt4.QtGui import QColor
|
from PyQt4.QtGui import QColor
|
||||||
import os
|
import os
|
||||||
import random
|
|
||||||
from . import __base__
|
from . import __base__
|
||||||
import time
|
import time
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
@ -11,6 +10,9 @@ from copy import copy
|
||||||
|
|
||||||
class Component(__base__.Component):
|
class Component(__base__.Component):
|
||||||
'''Original Audio Visualization'''
|
'''Original Audio Visualization'''
|
||||||
|
|
||||||
|
modified = QtCore.pyqtSignal(int, dict)
|
||||||
|
|
||||||
def widget(self, parent):
|
def widget(self, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.visColor = (255, 255, 255)
|
self.visColor = (255, 255, 255)
|
||||||
|
@ -36,8 +38,11 @@ class Component(__base__.Component):
|
||||||
self.layout = self.page.comboBox_visLayout.currentIndex()
|
self.layout = self.page.comboBox_visLayout.currentIndex()
|
||||||
self.visColor = self.RGBFromString(self.page.lineEdit_visColor.text())
|
self.visColor = self.RGBFromString(self.page.lineEdit_visColor.text())
|
||||||
self.parent.drawPreview()
|
self.parent.drawPreview()
|
||||||
|
super().update()
|
||||||
|
|
||||||
|
def loadPreset(self, pr, presetName=None):
|
||||||
|
super().loadPreset(pr, presetName)
|
||||||
|
|
||||||
def loadPreset(self, pr):
|
|
||||||
self.page.lineEdit_visColor.setText('%s,%s,%s' % pr['visColor'])
|
self.page.lineEdit_visColor.setText('%s,%s,%s' % pr['visColor'])
|
||||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
||||||
% QColor(*pr['visColor']).name()
|
% QColor(*pr['visColor']).name()
|
||||||
|
@ -46,6 +51,7 @@ class Component(__base__.Component):
|
||||||
|
|
||||||
def savePreset(self):
|
def savePreset(self):
|
||||||
return {
|
return {
|
||||||
|
'preset': self.currentPreset,
|
||||||
'layout': self.layout,
|
'layout': self.layout,
|
||||||
'visColor': self.visColor,
|
'visColor': self.visColor,
|
||||||
}
|
}
|
||||||
|
@ -139,7 +145,7 @@ class Component(__base__.Component):
|
||||||
bF = width / 64
|
bF = width / 64
|
||||||
bH = bF / 2
|
bH = bF / 2
|
||||||
bQ = bF / 4
|
bQ = bF / 4
|
||||||
imTop = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
imTop = self.blankFrame(width, height)
|
||||||
draw = ImageDraw.Draw(imTop)
|
draw = ImageDraw.Draw(imTop)
|
||||||
r, g, b = color
|
r, g, b = color
|
||||||
color2 = (r, g, b, 125)
|
color2 = (r, g, b, 125)
|
||||||
|
@ -157,7 +163,7 @@ class Component(__base__.Component):
|
||||||
|
|
||||||
imBottom = imTop.transpose(Image.FLIP_TOP_BOTTOM)
|
imBottom = imTop.transpose(Image.FLIP_TOP_BOTTOM)
|
||||||
|
|
||||||
im = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
im = self.blankFrame(width, height)
|
||||||
|
|
||||||
if layout == 0:
|
if layout == 0:
|
||||||
y = 0 - int(height/100*43)
|
y = 0 - int(height/100*43)
|
||||||
|
|
|
@ -9,8 +9,11 @@ from . import __base__
|
||||||
|
|
||||||
class Component(__base__.Component):
|
class Component(__base__.Component):
|
||||||
'''Title Text'''
|
'''Title Text'''
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
modified = QtCore.pyqtSignal(int, dict)
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
self.titleFont = QFont()
|
self.titleFont = QFont()
|
||||||
|
|
||||||
def widget(self, parent):
|
def widget(self, parent):
|
||||||
|
@ -31,7 +34,7 @@ class Component(__base__.Component):
|
||||||
page.comboBox_textAlign.addItem("Right")
|
page.comboBox_textAlign.addItem("Right")
|
||||||
|
|
||||||
page.lineEdit_textColor.setText('%s,%s,%s' % self.textColor)
|
page.lineEdit_textColor.setText('%s,%s,%s' % self.textColor)
|
||||||
page.pushButton_textColor.clicked.connect(lambda: self.pickColor())
|
page.pushButton_textColor.clicked.connect(self.pickColor)
|
||||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
||||||
% QColor(*self.textColor).name()
|
% QColor(*self.textColor).name()
|
||||||
page.pushButton_textColor.setStyleSheet(btnStyle)
|
page.pushButton_textColor.setStyleSheet(btnStyle)
|
||||||
|
@ -62,6 +65,7 @@ class Component(__base__.Component):
|
||||||
self.textColor = self.RGBFromString(
|
self.textColor = self.RGBFromString(
|
||||||
self.page.lineEdit_textColor.text())
|
self.page.lineEdit_textColor.text())
|
||||||
self.parent.drawPreview()
|
self.parent.drawPreview()
|
||||||
|
super().update()
|
||||||
|
|
||||||
def getXY(self):
|
def getXY(self):
|
||||||
'''Returns true x, y after considering alignment settings'''
|
'''Returns true x, y after considering alignment settings'''
|
||||||
|
@ -78,7 +82,9 @@ class Component(__base__.Component):
|
||||||
x = self.xPosition - offset
|
x = self.xPosition - offset
|
||||||
return x, self.yPosition
|
return x, self.yPosition
|
||||||
|
|
||||||
def loadPreset(self, pr):
|
def loadPreset(self, pr, presetName=None):
|
||||||
|
super().loadPreset(pr, presetName)
|
||||||
|
|
||||||
self.page.lineEdit_title.setText(pr['title'])
|
self.page.lineEdit_title.setText(pr['title'])
|
||||||
font = QFont()
|
font = QFont()
|
||||||
font.fromString(pr['titleFont'])
|
font.fromString(pr['titleFont'])
|
||||||
|
@ -94,6 +100,7 @@ class Component(__base__.Component):
|
||||||
|
|
||||||
def savePreset(self):
|
def savePreset(self):
|
||||||
return {
|
return {
|
||||||
|
'preset': self.currentPreset,
|
||||||
'title': self.title,
|
'title': self.title,
|
||||||
'titleFont': self.titleFont.toString(),
|
'titleFont': self.titleFont.toString(),
|
||||||
'alignment': self.alignment,
|
'alignment': self.alignment,
|
||||||
|
@ -119,7 +126,7 @@ class Component(__base__.Component):
|
||||||
|
|
||||||
def addText(self, width, height):
|
def addText(self, width, height):
|
||||||
x, y = self.getXY()
|
x, y = self.getXY()
|
||||||
im = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
im = self.blankFrame(width, height)
|
||||||
image = ImageQt(im)
|
image = ImageQt(im)
|
||||||
|
|
||||||
painter = QPainter(image)
|
painter = QPainter(image)
|
||||||
|
|
|
@ -6,11 +6,21 @@ import threading
|
||||||
from queue import PriorityQueue
|
from queue import PriorityQueue
|
||||||
from . import __base__
|
from . import __base__
|
||||||
|
|
||||||
|
|
||||||
class Video:
|
class Video:
|
||||||
'''Video Component Frame-Fetcher'''
|
'''Video Component Frame-Fetcher'''
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
mandatoryArgs = ['ffmpeg', 'videoPath', 'width', 'height',
|
mandatoryArgs = [
|
||||||
'frameRate', 'chunkSize', 'parent']
|
'ffmpeg', # path to ffmpeg, usually core.FFMPEG_BIN
|
||||||
|
'videoPath',
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'scale', # percentage scale
|
||||||
|
'frameRate', # frames per second
|
||||||
|
'chunkSize', # number of bytes in one frame
|
||||||
|
'parent', # mainwindow object
|
||||||
|
'component', # component object
|
||||||
|
]
|
||||||
for arg in mandatoryArgs:
|
for arg in mandatoryArgs:
|
||||||
try:
|
try:
|
||||||
exec('self.%s = kwargs[arg]' % arg)
|
exec('self.%s = kwargs[arg]' % arg)
|
||||||
|
@ -31,7 +41,8 @@ class Video:
|
||||||
'-i', self.videoPath,
|
'-i', self.videoPath,
|
||||||
'-f', 'image2pipe',
|
'-f', 'image2pipe',
|
||||||
'-pix_fmt', 'rgba',
|
'-pix_fmt', 'rgba',
|
||||||
'-filter:v', 'scale='+str(self.width)+':'+str(self.height),
|
'-filter:v', 'scale=%s:%s' %
|
||||||
|
scale(self.scale, self.width, self.height, str),
|
||||||
'-vcodec', 'rawvideo', '-',
|
'-vcodec', 'rawvideo', '-',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -50,7 +61,9 @@ class Video:
|
||||||
while True:
|
while True:
|
||||||
if num in self.finishedFrames:
|
if num in self.finishedFrames:
|
||||||
image = self.finishedFrames.pop(num)
|
image = self.finishedFrames.pop(num)
|
||||||
return Image.frombytes('RGBA', (self.width, self.height), image)
|
return finalizeFrame(
|
||||||
|
self.component, image, self.width, self.height)
|
||||||
|
|
||||||
i, image = self.frameBuffer.get()
|
i, image = self.frameBuffer.get()
|
||||||
self.finishedFrames[i] = image
|
self.finishedFrames[i] = image
|
||||||
self.frameBuffer.task_done()
|
self.frameBuffer.task_done()
|
||||||
|
@ -78,6 +91,9 @@ class Video:
|
||||||
|
|
||||||
class Component(__base__.Component):
|
class Component(__base__.Component):
|
||||||
'''Video'''
|
'''Video'''
|
||||||
|
|
||||||
|
modified = QtCore.pyqtSignal(int, dict)
|
||||||
|
|
||||||
def widget(self, parent):
|
def widget(self, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.settings = parent.settings
|
self.settings = parent.settings
|
||||||
|
@ -93,6 +109,10 @@ class Component(__base__.Component):
|
||||||
page.lineEdit_video.textChanged.connect(self.update)
|
page.lineEdit_video.textChanged.connect(self.update)
|
||||||
page.pushButton_video.clicked.connect(self.pickVideo)
|
page.pushButton_video.clicked.connect(self.pickVideo)
|
||||||
page.checkBox_loop.stateChanged.connect(self.update)
|
page.checkBox_loop.stateChanged.connect(self.update)
|
||||||
|
page.checkBox_distort.stateChanged.connect(self.update)
|
||||||
|
page.spinBox_scale.valueChanged.connect(self.update)
|
||||||
|
page.spinBox_x.valueChanged.connect(self.update)
|
||||||
|
page.spinBox_y.valueChanged.connect(self.update)
|
||||||
|
|
||||||
self.page = page
|
self.page = page
|
||||||
return page
|
return page
|
||||||
|
@ -100,15 +120,21 @@ class Component(__base__.Component):
|
||||||
def update(self):
|
def update(self):
|
||||||
self.videoPath = self.page.lineEdit_video.text()
|
self.videoPath = self.page.lineEdit_video.text()
|
||||||
self.loopVideo = self.page.checkBox_loop.isChecked()
|
self.loopVideo = self.page.checkBox_loop.isChecked()
|
||||||
|
self.distort = self.page.checkBox_distort.isChecked()
|
||||||
|
self.scale = self.page.spinBox_scale.value()
|
||||||
|
self.xPosition = self.page.spinBox_x.value()
|
||||||
|
self.yPosition = self.page.spinBox_y.value()
|
||||||
self.parent.drawPreview()
|
self.parent.drawPreview()
|
||||||
|
super().update()
|
||||||
|
|
||||||
def previewRender(self, previewWorker):
|
def previewRender(self, previewWorker):
|
||||||
|
self.videoFormats = previewWorker.core.videoFormats
|
||||||
width = int(previewWorker.core.settings.value('outputWidth'))
|
width = int(previewWorker.core.settings.value('outputWidth'))
|
||||||
height = int(previewWorker.core.settings.value('outputHeight'))
|
height = int(previewWorker.core.settings.value('outputHeight'))
|
||||||
self.chunkSize = 4*width*height
|
self.updateChunksize(width, height)
|
||||||
frame = self.getPreviewFrame(width, height)
|
frame = self.getPreviewFrame(width, height)
|
||||||
if not frame:
|
if not frame:
|
||||||
return Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
return self.blankFrame(width, height)
|
||||||
else:
|
else:
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
|
@ -116,30 +142,47 @@ class Component(__base__.Component):
|
||||||
super().preFrameRender(**kwargs)
|
super().preFrameRender(**kwargs)
|
||||||
width = int(self.worker.core.settings.value('outputWidth'))
|
width = int(self.worker.core.settings.value('outputWidth'))
|
||||||
height = int(self.worker.core.settings.value('outputHeight'))
|
height = int(self.worker.core.settings.value('outputHeight'))
|
||||||
self.chunkSize = 4*width*height
|
self.blankFrame_ = self.blankFrame(width, height)
|
||||||
|
self.updateChunksize(width, height)
|
||||||
self.video = Video(
|
self.video = Video(
|
||||||
ffmpeg=self.parent.core.FFMPEG_BIN, videoPath=self.videoPath,
|
ffmpeg=self.parent.core.FFMPEG_BIN, videoPath=self.videoPath,
|
||||||
width=width, height=height, chunkSize=self.chunkSize,
|
width=width, height=height, chunkSize=self.chunkSize,
|
||||||
frameRate=int(self.settings.value("outputFrameRate")),
|
frameRate=int(self.settings.value("outputFrameRate")),
|
||||||
parent=self.parent, loopVideo=self.loopVideo
|
parent=self.parent, loopVideo=self.loopVideo,
|
||||||
)
|
component=self, scale=self.scale
|
||||||
|
) if os.path.exists(self.videoPath) else None
|
||||||
|
|
||||||
def frameRender(self, moduleNo, arrayNo, frameNo):
|
def frameRender(self, moduleNo, arrayNo, frameNo):
|
||||||
|
if self.video:
|
||||||
return self.video.frame(frameNo)
|
return self.video.frame(frameNo)
|
||||||
|
else:
|
||||||
|
return self.blankFrame_
|
||||||
|
|
||||||
def loadPreset(self, pr):
|
def loadPreset(self, pr, presetName=None):
|
||||||
|
super().loadPreset(pr, presetName)
|
||||||
self.page.lineEdit_video.setText(pr['video'])
|
self.page.lineEdit_video.setText(pr['video'])
|
||||||
|
self.page.checkBox_loop.setChecked(pr['loop'])
|
||||||
|
self.page.checkBox_distort.setChecked(pr['distort'])
|
||||||
|
self.page.spinBox_scale.setValue(pr['scale'])
|
||||||
|
self.page.spinBox_x.setValue(pr['x'])
|
||||||
|
self.page.spinBox_y.setValue(pr['y'])
|
||||||
|
|
||||||
def savePreset(self):
|
def savePreset(self):
|
||||||
return {
|
return {
|
||||||
|
'preset': self.currentPreset,
|
||||||
'video': self.videoPath,
|
'video': self.videoPath,
|
||||||
|
'loop': self.loopVideo,
|
||||||
|
'distort': self.distort,
|
||||||
|
'scale': self.scale,
|
||||||
|
'x': self.xPosition,
|
||||||
|
'y': self.yPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
def pickVideo(self):
|
def pickVideo(self):
|
||||||
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
|
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
|
||||||
filename = QtGui.QFileDialog.getOpenFileName(
|
filename = QtGui.QFileDialog.getOpenFileName(
|
||||||
self.page, "Choose Video",
|
self.page, "Choose Video",
|
||||||
imgDir, "Video Files (*.mp4 *.mov)"
|
imgDir, "Video Files (%s)" % " ".join(self.videoFormats)
|
||||||
)
|
)
|
||||||
if filename:
|
if filename:
|
||||||
self.settings.setValue("backgroundDir", os.path.dirname(filename))
|
self.settings.setValue("backgroundDir", os.path.dirname(filename))
|
||||||
|
@ -149,13 +192,15 @@ class Component(__base__.Component):
|
||||||
def getPreviewFrame(self, width, height):
|
def getPreviewFrame(self, width, height):
|
||||||
if not self.videoPath or not os.path.exists(self.videoPath):
|
if not self.videoPath or not os.path.exists(self.videoPath):
|
||||||
return
|
return
|
||||||
|
|
||||||
command = [
|
command = [
|
||||||
self.parent.core.FFMPEG_BIN,
|
self.parent.core.FFMPEG_BIN,
|
||||||
'-thread_queue_size', '512',
|
'-thread_queue_size', '512',
|
||||||
'-i', self.videoPath,
|
'-i', self.videoPath,
|
||||||
'-f', 'image2pipe',
|
'-f', 'image2pipe',
|
||||||
'-pix_fmt', 'rgba',
|
'-pix_fmt', 'rgba',
|
||||||
'-filter:v', 'scale='+str(width)+':'+str(height),
|
'-filter:v', 'scale=%s:%s' %
|
||||||
|
scale(self.scale, width, height, str),
|
||||||
'-vcodec', 'rawvideo', '-',
|
'-vcodec', 'rawvideo', '-',
|
||||||
'-ss', '90',
|
'-ss', '90',
|
||||||
'-vframes', '1',
|
'-vframes', '1',
|
||||||
|
@ -165,7 +210,47 @@ class Component(__base__.Component):
|
||||||
stderr=subprocess.DEVNULL, bufsize=10**8
|
stderr=subprocess.DEVNULL, bufsize=10**8
|
||||||
)
|
)
|
||||||
byteFrame = pipe.stdout.read(self.chunkSize)
|
byteFrame = pipe.stdout.read(self.chunkSize)
|
||||||
image = Image.frombytes('RGBA', (width, height), byteFrame)
|
frame = finalizeFrame(self, byteFrame, width, height)
|
||||||
pipe.stdout.close()
|
pipe.stdout.close()
|
||||||
pipe.kill()
|
pipe.kill()
|
||||||
return image
|
|
||||||
|
return frame
|
||||||
|
|
||||||
|
def updateChunksize(self, width, height):
|
||||||
|
if self.scale != 100 and not self.distort:
|
||||||
|
width, height = scale(self.scale, width, height, int)
|
||||||
|
self.chunkSize = 4*width*height
|
||||||
|
|
||||||
|
def scale(scale, width, height, returntype=None):
|
||||||
|
width = (float(width) / 100.0) * float(scale)
|
||||||
|
height = (float(height) / 100.0) * float(scale)
|
||||||
|
if returntype == str:
|
||||||
|
return (str(int(width)), str(int(height)))
|
||||||
|
elif returntype == int:
|
||||||
|
return (int(width), int(height))
|
||||||
|
else:
|
||||||
|
return (width, height)
|
||||||
|
|
||||||
|
def finalizeFrame(self, imageData, width, height):
|
||||||
|
if self.distort:
|
||||||
|
try:
|
||||||
|
image = Image.frombytes(
|
||||||
|
'RGBA',
|
||||||
|
(width, height),
|
||||||
|
imageData)
|
||||||
|
except ValueError:
|
||||||
|
print('#### ignored invalid data caused by distortion ####')
|
||||||
|
image = self.blankFrame(width, height)
|
||||||
|
else:
|
||||||
|
image = Image.frombytes(
|
||||||
|
'RGBA',
|
||||||
|
scale(self.scale, width, height, int),
|
||||||
|
imageData)
|
||||||
|
|
||||||
|
if self.scale != 100 \
|
||||||
|
or self.xPosition != 0 or self.yPosition != 0:
|
||||||
|
frame = self.blankFrame(width, height)
|
||||||
|
frame.paste(image, box=(self.xPosition, self.yPosition))
|
||||||
|
else:
|
||||||
|
frame = image
|
||||||
|
return frame
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="spinBox_x_2">
|
<widget class="QSpinBox" name="spinBox_x">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -124,8 +124,11 @@
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>-10000</number>
|
||||||
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>999999999</number>
|
<number>10000</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -163,10 +166,10 @@
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>0</number>
|
<number>-10000</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>999999999</number>
|
<number>10000</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
|
@ -202,6 +205,42 @@
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkBox_distort">
|
||||||
|
<property name="text">
|
||||||
|
<string>Distort by scale</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Scale</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBox_scale">
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::UpDownArrows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string>%</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>400</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -217,6 +256,9 @@
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget" native="true"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|
347
core.py
347
core.py
|
@ -6,25 +6,330 @@ from os.path import expanduser
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
import numpy
|
import numpy
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import tempfile
|
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
import atexit
|
|
||||||
import time
|
import time
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import json
|
import json
|
||||||
|
from importlib import import_module
|
||||||
|
from PyQt4.QtGui import QDesktopServices
|
||||||
|
import string
|
||||||
|
|
||||||
|
|
||||||
class Core():
|
class Core():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.FFMPEG_BIN = self.findFfmpeg()
|
self.FFMPEG_BIN = self.findFfmpeg()
|
||||||
self.tempDir = os.path.join(
|
self.dataDir = QDesktopServices.storageLocation(
|
||||||
tempfile.gettempdir(), 'audio-visualizer-python-data')
|
QDesktopServices.DataLocation)
|
||||||
if not os.path.exists(self.tempDir):
|
self.presetDir = os.path.join(self.dataDir, 'presets')
|
||||||
os.makedirs(self.tempDir)
|
|
||||||
atexit.register(self.deleteTempDir)
|
|
||||||
self.wd = os.path.dirname(os.path.realpath(__file__))
|
self.wd = os.path.dirname(os.path.realpath(__file__))
|
||||||
self.loadEncoderOptions()
|
self.loadEncoderOptions()
|
||||||
|
self.videoFormats = Core.appendUppercase([
|
||||||
|
'*.mp4',
|
||||||
|
'*.mov',
|
||||||
|
'*.mkv',
|
||||||
|
'*.avi',
|
||||||
|
'*.webm',
|
||||||
|
'*.flv',
|
||||||
|
])
|
||||||
|
self.audioFormats = Core.appendUppercase([
|
||||||
|
'*.mp3',
|
||||||
|
'*.wav',
|
||||||
|
'*.ogg',
|
||||||
|
'*.fla',
|
||||||
|
'*.aac',
|
||||||
|
])
|
||||||
|
self.imageFormats = Core.appendUppercase([
|
||||||
|
'*.png',
|
||||||
|
'*.jpg',
|
||||||
|
'*.tif',
|
||||||
|
'*.tiff',
|
||||||
|
'*.gif',
|
||||||
|
'*.bmp',
|
||||||
|
'*.ico',
|
||||||
|
'*.xbm',
|
||||||
|
'*.xpm',
|
||||||
|
])
|
||||||
|
|
||||||
|
self.findComponents()
|
||||||
|
self.selectedComponents = []
|
||||||
|
# copies of named presets to detect modification
|
||||||
|
self.savedPresets = {}
|
||||||
|
|
||||||
|
def findComponents(self):
|
||||||
|
def findComponents():
|
||||||
|
srcPath = os.path.join(self.wd, 'components')
|
||||||
|
if os.path.exists(srcPath):
|
||||||
|
for f in sorted(os.listdir(srcPath)):
|
||||||
|
name, ext = os.path.splitext(f)
|
||||||
|
if name.startswith("__"):
|
||||||
|
continue
|
||||||
|
elif ext == '.py':
|
||||||
|
yield name
|
||||||
|
self.modules = [
|
||||||
|
import_module('components.%s' % name)
|
||||||
|
for name in findComponents()
|
||||||
|
]
|
||||||
|
self.moduleIndexes = [i for i in range(len(self.modules))]
|
||||||
|
|
||||||
|
def componentListChanged(self):
|
||||||
|
for i, component in enumerate(self.selectedComponents):
|
||||||
|
component.compPos = i
|
||||||
|
|
||||||
|
def insertComponent(self, compPos, moduleIndex):
|
||||||
|
if compPos < 0:
|
||||||
|
compPos = len(self.selectedComponents) -1
|
||||||
|
|
||||||
|
component = self.modules[moduleIndex].Component(
|
||||||
|
moduleIndex, compPos)
|
||||||
|
self.selectedComponents.insert(
|
||||||
|
compPos,
|
||||||
|
component)
|
||||||
|
|
||||||
|
self.componentListChanged()
|
||||||
|
return compPos
|
||||||
|
|
||||||
|
def moveComponent(self, startI, endI):
|
||||||
|
comp = self.selectedComponents.pop(startI)
|
||||||
|
self.selectedComponents.insert(endI, comp)
|
||||||
|
|
||||||
|
self.componentListChanged()
|
||||||
|
return endI
|
||||||
|
|
||||||
|
def removeComponent(self, i):
|
||||||
|
self.selectedComponents.pop(i)
|
||||||
|
self.componentListChanged()
|
||||||
|
|
||||||
|
def updateComponent(self, i):
|
||||||
|
# print('updating %s' % self.selectedComponents[i])
|
||||||
|
self.selectedComponents[i].update()
|
||||||
|
|
||||||
|
def moduleIndexFor(self, compName):
|
||||||
|
compNames = [mod.Component.__doc__ for mod in self.modules]
|
||||||
|
index = compNames.index(compName)
|
||||||
|
return self.moduleIndexes[index]
|
||||||
|
|
||||||
|
def clearPreset(self, compIndex, loader=None):
|
||||||
|
'''Clears a preset from a component'''
|
||||||
|
self.selectedComponents[compIndex].currentPreset = None
|
||||||
|
if loader:
|
||||||
|
loader.updateComponentTitle(compIndex)
|
||||||
|
|
||||||
|
def openPreset(self, filepath, compIndex, presetName):
|
||||||
|
'''Applies a preset to a specific component'''
|
||||||
|
saveValueStore = self.getPreset(filepath)
|
||||||
|
if not saveValueStore:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
self.selectedComponents[compIndex].loadPreset(
|
||||||
|
saveValueStore,
|
||||||
|
presetName
|
||||||
|
)
|
||||||
|
except KeyError as e:
|
||||||
|
print('preset missing value: %s' % e)
|
||||||
|
|
||||||
|
self.savedPresets[presetName] = dict(saveValueStore)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getPreset(self, filepath):
|
||||||
|
'''Returns the preset dict stored at this filepath'''
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
return False
|
||||||
|
with open(filepath, 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
saveValueStore = Core.presetFromString(line.strip())
|
||||||
|
break
|
||||||
|
return saveValueStore
|
||||||
|
|
||||||
|
def openProject(self, loader, filepath):
|
||||||
|
'''loader is the object calling this method (mainwindow/command)
|
||||||
|
which implements an insertComponent method'''
|
||||||
|
errcode, data = self.parseAvFile(filepath)
|
||||||
|
if errcode == 0:
|
||||||
|
try:
|
||||||
|
for i, tup in enumerate(data['Components']):
|
||||||
|
name, vers, preset = tup
|
||||||
|
clearThis = False
|
||||||
|
|
||||||
|
# add loaded named presets to savedPresets dict
|
||||||
|
if 'preset' in preset and preset['preset'] != None:
|
||||||
|
nam = preset['preset']
|
||||||
|
filepath2 = os.path.join(
|
||||||
|
self.presetDir, name, str(vers), nam)
|
||||||
|
origSaveValueStore = self.getPreset(filepath2)
|
||||||
|
if origSaveValueStore:
|
||||||
|
self.savedPresets[nam] = dict(origSaveValueStore)
|
||||||
|
else:
|
||||||
|
# saved preset was renamed or deleted
|
||||||
|
clearThis = True
|
||||||
|
|
||||||
|
# insert component into the loader
|
||||||
|
loader.insertComponent(
|
||||||
|
self.moduleIndexFor(name), -1)
|
||||||
|
try:
|
||||||
|
if 'preset' in preset and preset['preset'] != None:
|
||||||
|
self.selectedComponents[-1].loadPreset(
|
||||||
|
preset
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.selectedComponents[-1].loadPreset(
|
||||||
|
preset,
|
||||||
|
preset['preset']
|
||||||
|
)
|
||||||
|
except KeyError as e:
|
||||||
|
print('%s missing value %s' %
|
||||||
|
(self.selectedComponents[-1], e))
|
||||||
|
|
||||||
|
if clearThis:
|
||||||
|
self.clearPreset(-1, loader)
|
||||||
|
except:
|
||||||
|
errcode = 1
|
||||||
|
data = sys.exc_info()
|
||||||
|
|
||||||
|
|
||||||
|
if errcode == 1:
|
||||||
|
typ, value, _ = data
|
||||||
|
if typ.__name__ == KeyError:
|
||||||
|
# probably just an old version, still loadable
|
||||||
|
print('file missing value: %s' % value)
|
||||||
|
return
|
||||||
|
loader.createNewProject()
|
||||||
|
msg = '%s: %s' % (typ.__name__, value)
|
||||||
|
loader.showMessage(
|
||||||
|
msg="Project file '%s' is corrupted." % filepath,
|
||||||
|
showCancel=False,
|
||||||
|
icon=QtGui.QMessageBox.Warning,
|
||||||
|
detail=msg)
|
||||||
|
|
||||||
|
def parseAvFile(self, filepath):
|
||||||
|
'''Parses an avp (project) or avl (preset package) file.
|
||||||
|
Returns data usable by another method.'''
|
||||||
|
data = {}
|
||||||
|
try:
|
||||||
|
with open(filepath, 'r') as f:
|
||||||
|
def parseLine(line):
|
||||||
|
'''Decides if a given avp or avl line is a section header'''
|
||||||
|
validSections = ('Components')
|
||||||
|
line = line.strip()
|
||||||
|
newSection = ''
|
||||||
|
|
||||||
|
if line.startswith('[') and line.endswith(']') \
|
||||||
|
and line[1:-1] in validSections:
|
||||||
|
newSection = line[1:-1]
|
||||||
|
|
||||||
|
return line, newSection
|
||||||
|
|
||||||
|
section = ''
|
||||||
|
i = 0
|
||||||
|
for line in f:
|
||||||
|
line, newSection = parseLine(line)
|
||||||
|
if newSection:
|
||||||
|
section = str(newSection)
|
||||||
|
data[section] = []
|
||||||
|
continue
|
||||||
|
if line and section == 'Components':
|
||||||
|
if i == 0:
|
||||||
|
lastCompName = str(line)
|
||||||
|
i += 1
|
||||||
|
elif i == 1:
|
||||||
|
lastCompVers = str(line)
|
||||||
|
i += 1
|
||||||
|
elif i == 2:
|
||||||
|
lastCompPreset = Core.presetFromString(line)
|
||||||
|
data[section].append(
|
||||||
|
(lastCompName,
|
||||||
|
lastCompVers,
|
||||||
|
lastCompPreset)
|
||||||
|
)
|
||||||
|
i = 0
|
||||||
|
return 0, data
|
||||||
|
except:
|
||||||
|
return 1, sys.exc_info()
|
||||||
|
|
||||||
|
def importPreset(self, filepath):
|
||||||
|
errcode, data = self.parseAvFile(filepath)
|
||||||
|
returnList = []
|
||||||
|
if errcode == 0:
|
||||||
|
name, vers, preset = data['Components'][0]
|
||||||
|
presetName = preset['preset'] \
|
||||||
|
if preset['preset'] else os.path.basename(filepath)[:-4]
|
||||||
|
newPath = os.path.join(
|
||||||
|
self.presetDir,
|
||||||
|
name,
|
||||||
|
vers,
|
||||||
|
presetName
|
||||||
|
)
|
||||||
|
if os.path.exists(newPath):
|
||||||
|
return False, newPath
|
||||||
|
preset['preset'] = presetName
|
||||||
|
self.createPresetFile(
|
||||||
|
name, vers, presetName, preset
|
||||||
|
)
|
||||||
|
return True, presetName
|
||||||
|
elif errcode == 1:
|
||||||
|
# TODO: an error message
|
||||||
|
return False, ''
|
||||||
|
|
||||||
|
def exportPreset(self, exportPath, compName, vers, origName):
|
||||||
|
internalPath = os.path.join(self.presetDir, compName, str(vers), origName)
|
||||||
|
if not os.path.exists(internalPath):
|
||||||
|
return
|
||||||
|
if os.path.exists(exportPath):
|
||||||
|
os.remove(exportPath)
|
||||||
|
with open(internalPath, 'r') as f:
|
||||||
|
internalData = [line for line in f]
|
||||||
|
try:
|
||||||
|
saveValueStore = Core.presetFromString(internalData[0].strip())
|
||||||
|
self.createPresetFile(
|
||||||
|
compName, vers,
|
||||||
|
origName, saveValueStore,
|
||||||
|
exportPath
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def createPresetFile(
|
||||||
|
self, compName, vers, presetName, saveValueStore, filepath=''):
|
||||||
|
'''Create a preset file (.avl) at filepath using args.
|
||||||
|
Or if filepath is empty, create an internal preset using
|
||||||
|
the args for the filepath.'''
|
||||||
|
if not filepath:
|
||||||
|
dirname = os.path.join(self.presetDir, compName, str(vers))
|
||||||
|
if not os.path.exists(dirname):
|
||||||
|
os.makedirs(dirname)
|
||||||
|
filepath = os.path.join(dirname, presetName)
|
||||||
|
internal = True
|
||||||
|
else:
|
||||||
|
if not filepath.endswith('.avl'):
|
||||||
|
filepath += '.avl'
|
||||||
|
internal = False
|
||||||
|
|
||||||
|
with open(filepath, 'w') as f:
|
||||||
|
if not internal:
|
||||||
|
f.write('[Components]\n')
|
||||||
|
f.write('%s\n' % compName)
|
||||||
|
f.write('%s\n' % str(vers))
|
||||||
|
f.write(Core.presetToString(saveValueStore))
|
||||||
|
|
||||||
|
def createProjectFile(self, filepath):
|
||||||
|
'''Create a project file (.avp) using the current program state'''
|
||||||
|
try:
|
||||||
|
if not filepath.endswith(".avp"):
|
||||||
|
filepath += '.avp'
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
os.remove(filepath)
|
||||||
|
with open(filepath, 'w') as f:
|
||||||
|
print('creating %s' % filepath)
|
||||||
|
f.write('[Components]\n')
|
||||||
|
for comp in self.selectedComponents:
|
||||||
|
saveValueStore = comp.savePreset()
|
||||||
|
f.write('%s\n' % str(comp))
|
||||||
|
f.write('%s\n' % str(comp.version()))
|
||||||
|
f.write('%s\n' % Core.presetToString(saveValueStore))
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
def loadEncoderOptions(self):
|
def loadEncoderOptions(self):
|
||||||
file_path = os.path.join(self.wd, 'encoder-options.json')
|
file_path = os.path.join(self.wd, 'encoder-options.json')
|
||||||
|
@ -107,12 +412,6 @@ class Core():
|
||||||
|
|
||||||
return completeAudioArray
|
return completeAudioArray
|
||||||
|
|
||||||
def deleteTempDir(self):
|
|
||||||
try:
|
|
||||||
rmtree(self.tempDir)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self.canceled = True
|
self.canceled = True
|
||||||
|
|
||||||
|
@ -120,6 +419,22 @@ class Core():
|
||||||
self.canceled = False
|
self.canceled = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def stringOrderedDict(dictionary):
|
def badName(name):
|
||||||
sorted_ = OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
|
'''Returns whether a name contains non-alphanumeric chars'''
|
||||||
return repr(sorted_)
|
return any([letter in string.punctuation for letter in name])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def presetToString(dictionary):
|
||||||
|
'''Alphabetizes a dict into OrderedDict & returns string repr'''
|
||||||
|
return repr(OrderedDict(sorted(dictionary.items(), key=lambda t: t[0])))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def presetFromString(string):
|
||||||
|
'''Turns a string repr of OrderedDict into a regular dict'''
|
||||||
|
return dict(eval(string))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def appendUppercase(lst):
|
||||||
|
for form, i in zip(lst, range(len(lst))):
|
||||||
|
lst.append(form.upper())
|
||||||
|
return lst
|
||||||
|
|
1
main.py
1
main.py
|
@ -1,5 +1,4 @@
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from collections import OrderedDict
|
|
||||||
from PyQt4 import QtGui, uic
|
from PyQt4 import QtGui, uic
|
||||||
from PyQt4.QtCore import Qt
|
from PyQt4.QtCore import Qt
|
||||||
import sys
|
import sys
|
||||||
|
|
538
mainwindow.py
538
mainwindow.py
|
@ -1,14 +1,10 @@
|
||||||
from os.path import expanduser
|
from os.path import expanduser
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from importlib import import_module
|
from PyQt4 import QtCore, QtGui, uic
|
||||||
from collections import OrderedDict
|
|
||||||
from PyQt4 import QtCore, QtGui
|
|
||||||
from PyQt4.QtCore import QSettings, Qt
|
from PyQt4.QtCore import QSettings, Qt
|
||||||
from PyQt4.QtGui import QDesktopServices, QMenu
|
from PyQt4.QtGui import QMenu
|
||||||
import sys
|
import sys
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import string
|
|
||||||
import signal
|
import signal
|
||||||
import filecmp
|
import filecmp
|
||||||
import time
|
import time
|
||||||
|
@ -16,6 +12,7 @@ import time
|
||||||
import core
|
import core
|
||||||
import preview_thread
|
import preview_thread
|
||||||
import video_thread
|
import video_thread
|
||||||
|
from presetmanager import PresetManager
|
||||||
from main import LoadDefaultSettings
|
from main import LoadDefaultSettings
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,26 +52,30 @@ class MainWindow(QtCore.QObject):
|
||||||
# print('main thread id: {}'.format(QtCore.QThread.currentThreadId()))
|
# print('main thread id: {}'.format(QtCore.QThread.currentThreadId()))
|
||||||
self.window = window
|
self.window = window
|
||||||
self.core = core.Core()
|
self.core = core.Core()
|
||||||
self.pages = []
|
|
||||||
self.selectedComponents = []
|
self.pages = [] # widgets of component settings
|
||||||
self.lastAutosave = time.time()
|
self.lastAutosave = time.time()
|
||||||
|
|
||||||
# create data directory, load/create settings
|
# Create data directory, load/create settings
|
||||||
self.dataDir = QDesktopServices.storageLocation(
|
self.dataDir = self.core.dataDir
|
||||||
QDesktopServices.DataLocation)
|
|
||||||
self.autosavePath = os.path.join(self.dataDir, 'autosave.avp')
|
self.autosavePath = os.path.join(self.dataDir, 'autosave.avp')
|
||||||
self.presetDir = os.path.join(self.dataDir, 'presets')
|
|
||||||
self.settings = QSettings(
|
self.settings = QSettings(
|
||||||
os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat)
|
os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat)
|
||||||
LoadDefaultSettings(self)
|
LoadDefaultSettings(self)
|
||||||
|
self.presetManager = PresetManager(
|
||||||
|
uic.loadUi(
|
||||||
|
os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||||
|
'presetmanager.ui')),
|
||||||
|
self)
|
||||||
|
|
||||||
if not os.path.exists(self.dataDir):
|
if not os.path.exists(self.dataDir):
|
||||||
os.makedirs(self.dataDir)
|
os.makedirs(self.dataDir)
|
||||||
for neededDirectory in (
|
for neededDirectory in (
|
||||||
self.presetDir, self.settings.value("projectDir")):
|
self.core.presetDir, self.settings.value("projectDir")):
|
||||||
if not os.path.exists(neededDirectory):
|
if not os.path.exists(neededDirectory):
|
||||||
os.mkdir(neededDirectory)
|
os.mkdir(neededDirectory)
|
||||||
|
|
||||||
#
|
# Make queues/timers for the preview thread
|
||||||
self.previewQueue = Queue()
|
self.previewQueue = Queue()
|
||||||
self.previewThread = QtCore.QThread(self)
|
self.previewThread = QtCore.QThread(self)
|
||||||
self.previewWorker = preview_thread.Worker(self, self.previewQueue)
|
self.previewWorker = preview_thread.Worker(self, self.previewQueue)
|
||||||
|
@ -86,7 +87,9 @@ class MainWindow(QtCore.QObject):
|
||||||
self.timer.timeout.connect(self.processTask.emit)
|
self.timer.timeout.connect(self.processTask.emit)
|
||||||
self.timer.start(500)
|
self.timer.start(500)
|
||||||
|
|
||||||
# begin decorating the window and connecting events
|
# Begin decorating the window and connecting events
|
||||||
|
componentList = self.window.listWidget_componentList
|
||||||
|
|
||||||
window.toolButton_selectAudioFile.clicked.connect(
|
window.toolButton_selectAudioFile.clicked.connect(
|
||||||
self.openInputFileDialog)
|
self.openInputFileDialog)
|
||||||
|
|
||||||
|
@ -117,7 +120,7 @@ class MainWindow(QtCore.QObject):
|
||||||
codec = window.comboBox_videoCodec.itemText(i)
|
codec = window.comboBox_videoCodec.itemText(i)
|
||||||
if codec == self.settings.value('outputVideoCodec'):
|
if codec == self.settings.value('outputVideoCodec'):
|
||||||
window.comboBox_videoCodec.setCurrentIndex(i)
|
window.comboBox_videoCodec.setCurrentIndex(i)
|
||||||
print(codec)
|
#print(codec)
|
||||||
|
|
||||||
for i in range(window.comboBox_audioCodec.count()):
|
for i in range(window.comboBox_audioCodec.count()):
|
||||||
codec = window.comboBox_audioCodec.itemText(i)
|
codec = window.comboBox_audioCodec.itemText(i)
|
||||||
|
@ -141,25 +144,33 @@ class MainWindow(QtCore.QObject):
|
||||||
window.spinBox_vBitrate.valueChanged.connect(self.updateCodecSettings)
|
window.spinBox_vBitrate.valueChanged.connect(self.updateCodecSettings)
|
||||||
window.spinBox_aBitrate.valueChanged.connect(self.updateCodecSettings)
|
window.spinBox_aBitrate.valueChanged.connect(self.updateCodecSettings)
|
||||||
|
|
||||||
|
|
||||||
self.previewWindow = PreviewWindow(self, os.path.join(
|
self.previewWindow = PreviewWindow(self, os.path.join(
|
||||||
os.path.dirname(os.path.realpath(__file__)), "background.png"))
|
os.path.dirname(os.path.realpath(__file__)), "background.png"))
|
||||||
window.verticalLayout_previewWrapper.addWidget(self.previewWindow)
|
window.verticalLayout_previewWrapper.addWidget(self.previewWindow)
|
||||||
|
|
||||||
self.modules = self.findComponents()
|
# Make component buttons
|
||||||
self.compMenu = QMenu()
|
self.compMenu = QMenu()
|
||||||
for i, comp in enumerate(self.modules):
|
for i, comp in enumerate(self.core.modules):
|
||||||
action = self.compMenu.addAction(comp.Component.__doc__)
|
action = self.compMenu.addAction(comp.Component.__doc__)
|
||||||
action.triggered[()].connect(
|
action.triggered[()].connect(
|
||||||
lambda item=i: self.insertComponent(item))
|
lambda item=i: self.insertComponent(item))
|
||||||
|
|
||||||
self.window.pushButton_addComponent.setMenu(self.compMenu)
|
self.window.pushButton_addComponent.setMenu(self.compMenu)
|
||||||
window.listWidget_componentList.clicked.connect(
|
|
||||||
lambda _: self.changeComponentWidget())
|
componentList.dropEvent = self.dragComponent
|
||||||
|
componentList.itemSelectionChanged.connect(
|
||||||
|
self.changeComponentWidget)
|
||||||
|
|
||||||
self.window.pushButton_removeComponent.clicked.connect(
|
self.window.pushButton_removeComponent.clicked.connect(
|
||||||
lambda _: self.removeComponent())
|
lambda _: self.removeComponent())
|
||||||
|
|
||||||
|
componentList.setContextMenuPolicy(
|
||||||
|
QtCore.Qt.CustomContextMenu)
|
||||||
|
componentList.connect(
|
||||||
|
componentList,
|
||||||
|
QtCore.SIGNAL("customContextMenuRequested(QPoint)"),
|
||||||
|
self.componentContextMenu)
|
||||||
|
|
||||||
currentRes = str(self.settings.value('outputWidth'))+'x' + \
|
currentRes = str(self.settings.value('outputWidth'))+'x' + \
|
||||||
str(self.settings.value('outputHeight'))
|
str(self.settings.value('outputHeight'))
|
||||||
for i, res in enumerate(self.resolutions):
|
for i, res in enumerate(self.resolutions):
|
||||||
|
@ -170,35 +181,46 @@ class MainWindow(QtCore.QObject):
|
||||||
window.comboBox_resolution.currentIndexChanged.connect(
|
window.comboBox_resolution.currentIndexChanged.connect(
|
||||||
self.updateResolution)
|
self.updateResolution)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.window.pushButton_listMoveUp.clicked.connect(
|
self.window.pushButton_listMoveUp.clicked.connect(
|
||||||
self.moveComponentUp)
|
lambda: self.moveComponent(-1)
|
||||||
|
)
|
||||||
self.window.pushButton_listMoveDown.clicked.connect(
|
self.window.pushButton_listMoveDown.clicked.connect(
|
||||||
self.moveComponentDown)
|
lambda: self.moveComponent(1)
|
||||||
self.window.pushButton_savePreset.clicked.connect(
|
)
|
||||||
self.openSavePresetDialog)
|
|
||||||
self.window.comboBox_openPreset.currentIndexChanged.connect(
|
|
||||||
self.openPreset)
|
|
||||||
self.window.pushButton_saveAs.clicked.connect(
|
|
||||||
self.openSaveProjectDialog)
|
|
||||||
self.window.pushButton_saveProject.clicked.connect(
|
|
||||||
self.saveCurrentProject)
|
|
||||||
self.window.pushButton_openProject.clicked.connect(
|
|
||||||
self.openOpenProjectDialog)
|
|
||||||
|
|
||||||
# show the window and load current project
|
# Configure the Projects Menu
|
||||||
|
self.projectMenu = QMenu()
|
||||||
|
self.ui_newProject = self.projectMenu.addAction("New Project")
|
||||||
|
self.ui_newProject.triggered[()].connect(self.createNewProject)
|
||||||
|
|
||||||
|
self.ui_openProject = self.projectMenu.addAction("Open Project")
|
||||||
|
self.ui_openProject.triggered[()].connect(self.openOpenProjectDialog)
|
||||||
|
|
||||||
|
action = self.projectMenu.addAction("Save Project")
|
||||||
|
action.triggered[()].connect(self.saveCurrentProject)
|
||||||
|
|
||||||
|
action = self.projectMenu.addAction("Save Project As")
|
||||||
|
action.triggered[()].connect(self.openSaveProjectDialog)
|
||||||
|
|
||||||
|
self.window.pushButton_projects.setMenu(self.projectMenu)
|
||||||
|
|
||||||
|
# Configure the Presets Button
|
||||||
|
self.window.pushButton_presets.clicked.connect(
|
||||||
|
self.openPresetManager
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show the window and load current project
|
||||||
window.show()
|
window.show()
|
||||||
self.currentProject = self.settings.value("currentProject")
|
self.currentProject = self.settings.value("currentProject")
|
||||||
if self.currentProject and os.path.exists(self.autosavePath) \
|
if self.autosaveExists():
|
||||||
and filecmp.cmp(self.autosavePath, self.currentProject):
|
|
||||||
# delete autosave if it's identical to the project
|
# delete autosave if it's identical to the project
|
||||||
os.remove(self.autosavePath)
|
os.remove(self.autosavePath)
|
||||||
|
|
||||||
if self.currentProject and os.path.exists(self.autosavePath):
|
if self.currentProject and os.path.exists(self.autosavePath):
|
||||||
ch = self.showMessage(
|
ch = self.showMessage(
|
||||||
"Restore unsaved changes in project '%s'?"
|
msg="Restore unsaved changes in project '%s'?"
|
||||||
% os.path.basename(self.currentProject)[:-4], True)
|
% os.path.basename(self.currentProject)[:-4],
|
||||||
|
showCancel=True)
|
||||||
if ch:
|
if ch:
|
||||||
os.remove(self.currentProject)
|
os.remove(self.currentProject)
|
||||||
os.rename(self.autosavePath, self.currentProject)
|
os.rename(self.autosavePath, self.currentProject)
|
||||||
|
@ -214,6 +236,26 @@ class MainWindow(QtCore.QObject):
|
||||||
self.previewThread.wait()
|
self.previewThread.wait()
|
||||||
self.autosave()
|
self.autosave()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int, dict)
|
||||||
|
def updateComponentTitle(self, pos, presetStore=False):
|
||||||
|
if type(presetStore) == dict:
|
||||||
|
name = presetStore['preset']
|
||||||
|
if name == None or name not in self.core.savedPresets:
|
||||||
|
modified = False
|
||||||
|
else:
|
||||||
|
modified = (presetStore != self.core.savedPresets[name])
|
||||||
|
else:
|
||||||
|
print(pos, presetStore)
|
||||||
|
modified = bool(presetStore)
|
||||||
|
if pos < 0:
|
||||||
|
pos = len(self.core.selectedComponents)-1
|
||||||
|
title = str(self.core.selectedComponents[pos])
|
||||||
|
if self.core.selectedComponents[pos].currentPreset:
|
||||||
|
title += ' - %s' % self.core.selectedComponents[pos].currentPreset
|
||||||
|
if modified:
|
||||||
|
title += '*'
|
||||||
|
self.window.listWidget_componentList.item(pos).setText(title)
|
||||||
|
|
||||||
def updateCodecs(self):
|
def updateCodecs(self):
|
||||||
containerWidget = self.window.comboBox_videoContainer
|
containerWidget = self.window.comboBox_videoContainer
|
||||||
vCodecWidget = self.window.comboBox_videoCodec
|
vCodecWidget = self.window.comboBox_videoCodec
|
||||||
|
@ -249,18 +291,26 @@ class MainWindow(QtCore.QObject):
|
||||||
self.settings.setValue('outputAudioBitrate', currentAudioBitrate)
|
self.settings.setValue('outputAudioBitrate', currentAudioBitrate)
|
||||||
|
|
||||||
def autosave(self):
|
def autosave(self):
|
||||||
if time.time() - self.lastAutosave >= 1.0:
|
if not self.currentProject:
|
||||||
if os.path.exists(self.autosavePath):
|
if os.path.exists(self.autosavePath):
|
||||||
os.remove(self.autosavePath)
|
os.remove(self.autosavePath)
|
||||||
self.createProjectFile(self.autosavePath)
|
elif time.time() - self.lastAutosave >= 2.0:
|
||||||
|
self.core.createProjectFile(self.autosavePath)
|
||||||
self.lastAutosave = time.time()
|
self.lastAutosave = time.time()
|
||||||
|
|
||||||
|
def autosaveExists(self):
|
||||||
|
if self.currentProject and os.path.exists(self.autosavePath) \
|
||||||
|
and filecmp.cmp(self.autosavePath, self.currentProject):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def openInputFileDialog(self):
|
def openInputFileDialog(self):
|
||||||
inputDir = self.settings.value("inputDir", expanduser("~"))
|
inputDir = self.settings.value("inputDir", expanduser("~"))
|
||||||
|
|
||||||
fileName = QtGui.QFileDialog.getOpenFileName(
|
fileName = QtGui.QFileDialog.getOpenFileName(
|
||||||
self.window, "Open Music File",
|
self.window, "Open Music File",
|
||||||
inputDir, "Music Files (*.mp3 *.wav *.ogg *.fla *.aac)")
|
inputDir, "Music Files (%s)" % " ".join(self.core.audioFormats))
|
||||||
|
|
||||||
if not fileName == "":
|
if not fileName == "":
|
||||||
self.settings.setValue("inputDir", os.path.dirname(fileName))
|
self.settings.setValue("inputDir", os.path.dirname(fileName))
|
||||||
|
@ -271,7 +321,8 @@ class MainWindow(QtCore.QObject):
|
||||||
|
|
||||||
fileName = QtGui.QFileDialog.getSaveFileName(
|
fileName = QtGui.QFileDialog.getSaveFileName(
|
||||||
self.window, "Set Output Video File",
|
self.window, "Set Output Video File",
|
||||||
outputDir, "Video Files (*.mp4 *.mov *.mkv *.avi *.webm *.flv)")
|
outputDir,
|
||||||
|
"Video Files (%s);; All Files (*)" % " ".join(self.core.videoFormats))
|
||||||
|
|
||||||
if not fileName == "":
|
if not fileName == "":
|
||||||
self.settings.setValue("outputDir", os.path.dirname(fileName))
|
self.settings.setValue("outputDir", os.path.dirname(fileName))
|
||||||
|
@ -302,13 +353,10 @@ class MainWindow(QtCore.QObject):
|
||||||
self.videoTask.emit(
|
self.videoTask.emit(
|
||||||
self.window.lineEdit_audioFile.text(),
|
self.window.lineEdit_audioFile.text(),
|
||||||
self.window.lineEdit_outputFile.text(),
|
self.window.lineEdit_outputFile.text(),
|
||||||
self.selectedComponents)
|
self.core.selectedComponents)
|
||||||
else:
|
else:
|
||||||
self.showMessage(
|
self.showMessage(
|
||||||
"You must select an audio file and output filename.")
|
msg="You must select an audio file and output filename.")
|
||||||
|
|
||||||
def progressBarUpdated(self, value):
|
|
||||||
self.window.progressBar_createVideo.setValue(value)
|
|
||||||
|
|
||||||
def changeEncodingStatus(self, status):
|
def changeEncodingStatus(self, status):
|
||||||
if status:
|
if status:
|
||||||
|
@ -327,11 +375,9 @@ class MainWindow(QtCore.QObject):
|
||||||
self.window.pushButton_removeComponent.setEnabled(False)
|
self.window.pushButton_removeComponent.setEnabled(False)
|
||||||
self.window.pushButton_listMoveDown.setEnabled(False)
|
self.window.pushButton_listMoveDown.setEnabled(False)
|
||||||
self.window.pushButton_listMoveUp.setEnabled(False)
|
self.window.pushButton_listMoveUp.setEnabled(False)
|
||||||
self.window.comboBox_openPreset.setEnabled(False)
|
|
||||||
self.window.pushButton_removePreset.setEnabled(False)
|
|
||||||
self.window.pushButton_savePreset.setEnabled(False)
|
|
||||||
self.window.pushButton_openProject.setEnabled(False)
|
|
||||||
self.window.listWidget_componentList.setEnabled(False)
|
self.window.listWidget_componentList.setEnabled(False)
|
||||||
|
self.ui_newProject.setEnabled(False)
|
||||||
|
self.ui_openProject.setEnabled(False)
|
||||||
else:
|
else:
|
||||||
self.window.pushButton_createVideo.setEnabled(True)
|
self.window.pushButton_createVideo.setEnabled(True)
|
||||||
self.window.pushButton_Cancel.setEnabled(False)
|
self.window.pushButton_Cancel.setEnabled(False)
|
||||||
|
@ -348,11 +394,12 @@ class MainWindow(QtCore.QObject):
|
||||||
self.window.pushButton_removeComponent.setEnabled(True)
|
self.window.pushButton_removeComponent.setEnabled(True)
|
||||||
self.window.pushButton_listMoveDown.setEnabled(True)
|
self.window.pushButton_listMoveDown.setEnabled(True)
|
||||||
self.window.pushButton_listMoveUp.setEnabled(True)
|
self.window.pushButton_listMoveUp.setEnabled(True)
|
||||||
self.window.comboBox_openPreset.setEnabled(True)
|
|
||||||
self.window.pushButton_removePreset.setEnabled(True)
|
|
||||||
self.window.pushButton_savePreset.setEnabled(True)
|
|
||||||
self.window.pushButton_openProject.setEnabled(True)
|
|
||||||
self.window.listWidget_componentList.setEnabled(True)
|
self.window.listWidget_componentList.setEnabled(True)
|
||||||
|
self.ui_newProject.setEnabled(True)
|
||||||
|
self.ui_openProject.setEnabled(True)
|
||||||
|
|
||||||
|
def progressBarUpdated(self, value):
|
||||||
|
self.window.progressBar_createVideo.setValue(value)
|
||||||
|
|
||||||
def progressBarSetText(self, value):
|
def progressBarSetText(self, value):
|
||||||
self.window.progressBar_createVideo.setFormat(value)
|
self.window.progressBar_createVideo.setFormat(value)
|
||||||
|
@ -369,187 +416,126 @@ class MainWindow(QtCore.QObject):
|
||||||
self.drawPreview()
|
self.drawPreview()
|
||||||
|
|
||||||
def drawPreview(self):
|
def drawPreview(self):
|
||||||
self.newTask.emit(self.selectedComponents)
|
self.newTask.emit(self.core.selectedComponents)
|
||||||
# self.processTask.emit()
|
# self.processTask.emit()
|
||||||
self.autosave()
|
self.autosave()
|
||||||
|
|
||||||
def showPreviewImage(self, image):
|
def showPreviewImage(self, image):
|
||||||
self.previewWindow.changePixmap(image)
|
self.previewWindow.changePixmap(image)
|
||||||
|
|
||||||
def findComponents(self):
|
def insertComponent(self, moduleIndex, compPos=0):
|
||||||
def findComponents():
|
componentList = self.window.listWidget_componentList
|
||||||
srcPath = os.path.join(
|
stackedWidget = self.window.stackedWidget
|
||||||
os.path.dirname(os.path.realpath(__file__)), 'components')
|
if compPos < 0:
|
||||||
if os.path.exists(srcPath):
|
compPos = componentList.count()
|
||||||
for f in sorted(os.listdir(srcPath)):
|
|
||||||
name, ext = os.path.splitext(f)
|
|
||||||
if name.startswith("__"):
|
|
||||||
continue
|
|
||||||
elif ext == '.py':
|
|
||||||
yield name
|
|
||||||
return [
|
|
||||||
import_module('components.%s' % name)
|
|
||||||
for name in findComponents()]
|
|
||||||
|
|
||||||
def addComponent(self, moduleIndex):
|
index = self.core.insertComponent(
|
||||||
index = len(self.pages)
|
compPos, moduleIndex)
|
||||||
self.selectedComponents.append(self.modules[moduleIndex].Component())
|
row = componentList.insertItem(
|
||||||
self.window.listWidget_componentList.addItem(
|
index,
|
||||||
self.selectedComponents[-1].__doc__)
|
self.core.selectedComponents[index].__doc__)
|
||||||
self.pages.append(self.selectedComponents[-1].widget(self))
|
componentList.setCurrentRow(index)
|
||||||
self.window.listWidget_componentList.setCurrentRow(index)
|
|
||||||
self.window.stackedWidget.addWidget(self.pages[-1])
|
|
||||||
self.window.stackedWidget.setCurrentIndex(index)
|
|
||||||
self.selectedComponents[-1].update()
|
|
||||||
self.updateOpenPresetComboBox(self.selectedComponents[-1])
|
|
||||||
|
|
||||||
def insertComponent(self, moduleIndex):
|
# connect to signal that adds an asterisk when modified
|
||||||
self.selectedComponents.insert(
|
self.core.selectedComponents[index].modified.connect(
|
||||||
0, self.modules[moduleIndex].Component())
|
self.updateComponentTitle)
|
||||||
self.window.listWidget_componentList.insertItem(
|
|
||||||
0, self.selectedComponents[0].__doc__)
|
self.pages.insert(index, self.core.selectedComponents[index].widget(self))
|
||||||
self.pages.insert(0, self.selectedComponents[0].widget(self))
|
stackedWidget.insertWidget(index, self.pages[index])
|
||||||
self.window.listWidget_componentList.setCurrentRow(0)
|
stackedWidget.setCurrentIndex(index)
|
||||||
self.window.stackedWidget.insertWidget(0, self.pages[0])
|
|
||||||
self.window.stackedWidget.setCurrentIndex(0)
|
self.core.updateComponent(index)
|
||||||
self.selectedComponents[0].update()
|
|
||||||
self.updateOpenPresetComboBox(self.selectedComponents[0])
|
|
||||||
|
|
||||||
def removeComponent(self):
|
def removeComponent(self):
|
||||||
for selected in self.window.listWidget_componentList.selectedItems():
|
componentList = self.window.listWidget_componentList
|
||||||
index = self.window.listWidget_componentList.row(selected)
|
|
||||||
|
for selected in componentList.selectedItems():
|
||||||
|
index = componentList.row(selected)
|
||||||
self.window.stackedWidget.removeWidget(self.pages[index])
|
self.window.stackedWidget.removeWidget(self.pages[index])
|
||||||
self.window.listWidget_componentList.takeItem(index)
|
componentList.takeItem(index)
|
||||||
self.selectedComponents.pop(index)
|
self.core.removeComponent(index)
|
||||||
self.pages.pop(index)
|
self.pages.pop(index)
|
||||||
self.changeComponentWidget()
|
self.changeComponentWidget()
|
||||||
self.drawPreview()
|
self.drawPreview()
|
||||||
|
|
||||||
|
def moveComponent(self, change):
|
||||||
|
'''Moves a component relatively from its current position'''
|
||||||
|
componentList = self.window.listWidget_componentList
|
||||||
|
stackedWidget = self.window.stackedWidget
|
||||||
|
|
||||||
|
row = componentList.currentRow()
|
||||||
|
newRow = row + change
|
||||||
|
if newRow > -1 and newRow < componentList.count():
|
||||||
|
self.core.moveComponent(row, newRow)
|
||||||
|
|
||||||
|
# update widgets
|
||||||
|
page = self.pages.pop(row)
|
||||||
|
self.pages.insert(newRow, page)
|
||||||
|
item = componentList.takeItem(row)
|
||||||
|
newItem = componentList.insertItem(newRow, item)
|
||||||
|
widget = stackedWidget.removeWidget(page)
|
||||||
|
stackedWidget.insertWidget(newRow, page)
|
||||||
|
componentList.setCurrentRow(newRow)
|
||||||
|
stackedWidget.setCurrentIndex(newRow)
|
||||||
|
self.drawPreview()
|
||||||
|
|
||||||
|
def dragComponent(self, event):
|
||||||
|
'''Drop event for the component listwidget'''
|
||||||
|
componentList = self.window.listWidget_componentList
|
||||||
|
|
||||||
|
modelIndexes = [ \
|
||||||
|
componentList.model().index(i) \
|
||||||
|
for i in range(componentList.count()) \
|
||||||
|
]
|
||||||
|
rects = [ \
|
||||||
|
componentList.visualRect(modelIndex) \
|
||||||
|
for modelIndex in modelIndexes \
|
||||||
|
]
|
||||||
|
|
||||||
|
rowPos = [rect.contains(event.pos()) for rect in rects]
|
||||||
|
if not any(rowPos):
|
||||||
|
return
|
||||||
|
|
||||||
|
i = rowPos.index(True)
|
||||||
|
change = (componentList.currentRow() - i) * -1
|
||||||
|
self.moveComponent(change)
|
||||||
|
|
||||||
def changeComponentWidget(self):
|
def changeComponentWidget(self):
|
||||||
selected = self.window.listWidget_componentList.selectedItems()
|
selected = self.window.listWidget_componentList.selectedItems()
|
||||||
if selected:
|
if selected:
|
||||||
index = self.window.listWidget_componentList.row(selected[0])
|
index = self.window.listWidget_componentList.row(selected[0])
|
||||||
self.window.stackedWidget.setCurrentIndex(index)
|
self.window.stackedWidget.setCurrentIndex(index)
|
||||||
self.updateOpenPresetComboBox(self.selectedComponents[index])
|
|
||||||
|
|
||||||
def moveComponentUp(self):
|
def openPresetManager(self):
|
||||||
row = self.window.listWidget_componentList.currentRow()
|
'''Preset manager for importing, exporting, renaming, deleting'''
|
||||||
if row > 0:
|
self.presetManager.show()
|
||||||
module = self.selectedComponents[row]
|
|
||||||
self.selectedComponents.pop(row)
|
|
||||||
self.selectedComponents.insert(row - 1, module)
|
|
||||||
page = self.pages[row]
|
|
||||||
self.pages.pop(row)
|
|
||||||
self.pages.insert(row - 1, page)
|
|
||||||
item = self.window.listWidget_componentList.takeItem(row)
|
|
||||||
self.window.listWidget_componentList.insertItem(row - 1, item)
|
|
||||||
widget = self.window.stackedWidget.removeWidget(page)
|
|
||||||
self.window.stackedWidget.insertWidget(row - 1, page)
|
|
||||||
self.window.listWidget_componentList.setCurrentRow(row - 1)
|
|
||||||
self.window.stackedWidget.setCurrentIndex(row - 1)
|
|
||||||
self.drawPreview()
|
|
||||||
|
|
||||||
def moveComponentDown(self):
|
def clear(self):
|
||||||
row = self.window.listWidget_componentList.currentRow()
|
'''Get a blank slate'''
|
||||||
if row != -1 and row < len(self.pages)+1:
|
self.core.selectedComponents = []
|
||||||
module = self.selectedComponents[row]
|
self.window.listWidget_componentList.clear()
|
||||||
self.selectedComponents.pop(row)
|
for widget in self.pages:
|
||||||
self.selectedComponents.insert(row + 1, module)
|
self.window.stackedWidget.removeWidget(widget)
|
||||||
page = self.pages[row]
|
self.pages = []
|
||||||
self.pages.pop(row)
|
|
||||||
self.pages.insert(row + 1, page)
|
|
||||||
item = self.window.listWidget_componentList.takeItem(row)
|
|
||||||
self.window.listWidget_componentList.insertItem(row + 1, item)
|
|
||||||
widget = self.window.stackedWidget.removeWidget(page)
|
|
||||||
self.window.stackedWidget.insertWidget(row + 1, page)
|
|
||||||
self.window.listWidget_componentList.setCurrentRow(row + 1)
|
|
||||||
self.window.stackedWidget.setCurrentIndex(row + 1)
|
|
||||||
self.drawPreview()
|
|
||||||
|
|
||||||
def updateOpenPresetComboBox(self, component):
|
def createNewProject(self):
|
||||||
self.window.comboBox_openPreset.clear()
|
if self.autosaveExists():
|
||||||
self.window.comboBox_openPreset.addItem("Component Presets")
|
|
||||||
destination = os.path.join(
|
|
||||||
self.presetDir, str(component).strip(), str(component.version()))
|
|
||||||
if not os.path.exists(destination):
|
|
||||||
os.makedirs(destination)
|
|
||||||
for f in os.listdir(destination):
|
|
||||||
self.window.comboBox_openPreset.addItem(f)
|
|
||||||
|
|
||||||
def openSavePresetDialog(self):
|
|
||||||
if self.window.listWidget_componentList.currentRow() == -1:
|
|
||||||
return
|
|
||||||
while True:
|
|
||||||
newName, OK = QtGui.QInputDialog.getText(
|
|
||||||
QtGui.QWidget(), 'Audio Visualizer', 'New Preset Name:')
|
|
||||||
badName = False
|
|
||||||
for letter in newName:
|
|
||||||
if letter in string.punctuation:
|
|
||||||
badName = True
|
|
||||||
if badName:
|
|
||||||
# some filesystems don't like bizarre characters
|
|
||||||
self.showMessage("Preset names must contain only letters, \
|
|
||||||
numbers, and spaces.")
|
|
||||||
continue
|
|
||||||
if OK and newName:
|
|
||||||
index = self.window.listWidget_componentList.currentRow()
|
|
||||||
if index != -1:
|
|
||||||
saveValueStore = \
|
|
||||||
self.selectedComponents[index].savePreset()
|
|
||||||
componentName = str(self.selectedComponents[index]).strip()
|
|
||||||
vers = self.selectedComponents[index].version()
|
|
||||||
self.createPresetFile(
|
|
||||||
componentName, vers, saveValueStore, newName)
|
|
||||||
break
|
|
||||||
|
|
||||||
def createPresetFile(
|
|
||||||
self, componentName, version, saveValueStore, filename):
|
|
||||||
dirname = os.path.join(self.presetDir, componentName, str(version))
|
|
||||||
if not os.path.exists(dirname):
|
|
||||||
os.makedirs(dirname)
|
|
||||||
filepath = os.path.join(dirname, filename)
|
|
||||||
if os.path.exists(filepath):
|
|
||||||
ch = self.showMessage(
|
ch = self.showMessage(
|
||||||
"%s already exists! Overwrite it?" % filename,
|
msg="You have unsaved changes in project '%s'. "
|
||||||
True, QtGui.QMessageBox.Warning)
|
"Save before starting a new project?"
|
||||||
if not ch:
|
% os.path.basename(self.currentProject)[:-4],
|
||||||
return
|
showCancel=True)
|
||||||
# remove old copies of the preset
|
if ch:
|
||||||
for i in range(0, self.window.comboBox_openPreset.count()):
|
self.saveCurrentProject()
|
||||||
if self.window.comboBox_openPreset.itemText(i) == filename:
|
|
||||||
self.window.comboBox_openPreset.removeItem(i)
|
|
||||||
with open(filepath, 'w') as f:
|
|
||||||
f.write(core.Core.stringOrderedDict(saveValueStore))
|
|
||||||
self.window.comboBox_openPreset.addItem(filename)
|
|
||||||
self.window.comboBox_openPreset.setCurrentIndex(
|
|
||||||
self.window.comboBox_openPreset.count()-1)
|
|
||||||
|
|
||||||
def openPreset(self):
|
self.clear()
|
||||||
if self.window.comboBox_openPreset.currentIndex() < 1:
|
self.currentProject = None
|
||||||
return
|
self.settings.setValue("currentProject", None)
|
||||||
index = self.window.listWidget_componentList.currentRow()
|
|
||||||
if index == -1:
|
|
||||||
return
|
|
||||||
filename = self.window.comboBox_openPreset.itemText(
|
|
||||||
self.window.comboBox_openPreset.currentIndex())
|
|
||||||
componentName = str(self.selectedComponents[index]).strip()
|
|
||||||
version = self.selectedComponents[index].version()
|
|
||||||
dirname = os.path.join(self.presetDir, componentName, str(version))
|
|
||||||
filepath = os.path.join(dirname, filename)
|
|
||||||
if not os.path.exists(filepath):
|
|
||||||
self.window.comboBox_openPreset.removeItem(
|
|
||||||
self.window.comboBox_openPreset.currentIndex())
|
|
||||||
return
|
|
||||||
with open(filepath, 'r') as f:
|
|
||||||
for line in f:
|
|
||||||
saveValueStore = dict(eval(line.strip()))
|
|
||||||
break
|
|
||||||
self.selectedComponents[index].loadPreset(saveValueStore)
|
|
||||||
self.drawPreview()
|
self.drawPreview()
|
||||||
|
|
||||||
def saveCurrentProject(self):
|
def saveCurrentProject(self):
|
||||||
if self.currentProject:
|
if self.currentProject:
|
||||||
self.createProjectFile(self.currentProject)
|
self.core.createProjectFile(self.currentProject)
|
||||||
else:
|
else:
|
||||||
self.openSaveProjectDialog()
|
self.openSaveProjectDialog()
|
||||||
|
|
||||||
|
@ -560,23 +546,13 @@ class MainWindow(QtCore.QObject):
|
||||||
"Project Files (*.avp)")
|
"Project Files (*.avp)")
|
||||||
if not filename:
|
if not filename:
|
||||||
return
|
return
|
||||||
self.createProjectFile(filename)
|
if not filename.endswith(".avp"):
|
||||||
|
filename += '.avp'
|
||||||
|
self.settings.setValue("projectDir", os.path.dirname(filename))
|
||||||
|
self.settings.setValue("currentProject", filename)
|
||||||
|
self.currentProject = filename
|
||||||
|
|
||||||
def createProjectFile(self, filepath):
|
self.core.createProjectFile(filename)
|
||||||
if not filepath.endswith(".avp"):
|
|
||||||
filepath += '.avp'
|
|
||||||
with open(filepath, 'w') as f:
|
|
||||||
print('creating %s' % filepath)
|
|
||||||
f.write('[Components]\n')
|
|
||||||
for comp in self.selectedComponents:
|
|
||||||
saveValueStore = comp.savePreset()
|
|
||||||
f.write('%s\n' % str(comp))
|
|
||||||
f.write('%s\n' % str(comp.version()))
|
|
||||||
f.write('%s\n' % core.Core.stringOrderedDict(saveValueStore))
|
|
||||||
if filepath != self.autosavePath:
|
|
||||||
self.settings.setValue("projectDir", os.path.dirname(filepath))
|
|
||||||
self.settings.setValue("currentProject", filepath)
|
|
||||||
self.currentProject = filepath
|
|
||||||
|
|
||||||
def openOpenProjectDialog(self):
|
def openOpenProjectDialog(self):
|
||||||
filename = QtGui.QFileDialog.getOpenFileName(
|
filename = QtGui.QFileDialog.getOpenFileName(
|
||||||
|
@ -593,58 +569,18 @@ class MainWindow(QtCore.QObject):
|
||||||
self.currentProject = filepath
|
self.currentProject = filepath
|
||||||
self.settings.setValue("currentProject", filepath)
|
self.settings.setValue("currentProject", filepath)
|
||||||
self.settings.setValue("projectDir", os.path.dirname(filepath))
|
self.settings.setValue("projectDir", os.path.dirname(filepath))
|
||||||
compNames = [mod.Component.__doc__ for mod in self.modules]
|
# actually load the project using core method
|
||||||
try:
|
self.core.openProject(self, filepath)
|
||||||
with open(filepath, 'r') as f:
|
|
||||||
validSections = ('Components')
|
|
||||||
section = ''
|
|
||||||
|
|
||||||
def parseLine(line):
|
def showMessage(self, **kwargs):
|
||||||
line = line.strip()
|
parent = kwargs['parent'] if 'parent' in kwargs else self.window
|
||||||
newSection = ''
|
msg = QtGui.QMessageBox(parent)
|
||||||
|
msg.setModal(True)
|
||||||
if line.startswith('[') and line.endswith(']') \
|
msg.setText(kwargs['msg'])
|
||||||
and line[1:-1] in validSections:
|
msg.setIcon(
|
||||||
newSection = line[1:-1]
|
kwargs['icon'] if 'icon' in kwargs else QtGui.QMessageBox.Information)
|
||||||
|
msg.setDetailedText(kwargs['detail'] if 'detail' in kwargs else None)
|
||||||
return line, newSection
|
if 'showCancel'in kwargs and kwargs['showCancel']:
|
||||||
|
|
||||||
i = 0
|
|
||||||
for line in f:
|
|
||||||
line, newSection = parseLine(line)
|
|
||||||
if newSection:
|
|
||||||
section = str(newSection)
|
|
||||||
continue
|
|
||||||
if line and section == 'Components':
|
|
||||||
if i == 0:
|
|
||||||
compIndex = compNames.index(line)
|
|
||||||
self.addComponent(compIndex)
|
|
||||||
i += 1
|
|
||||||
elif i == 1:
|
|
||||||
# version, not used yet
|
|
||||||
i += 1
|
|
||||||
elif i == 2:
|
|
||||||
saveValueStore = dict(eval(line))
|
|
||||||
self.selectedComponents[-1].loadPreset(
|
|
||||||
saveValueStore)
|
|
||||||
i = 0
|
|
||||||
except (IndexError, ValueError, KeyError, NameError,
|
|
||||||
SyntaxError, AttributeError, TypeError) as e:
|
|
||||||
self.clear()
|
|
||||||
typ, value, _ = sys.exc_info()
|
|
||||||
msg = '%s: %s' % (typ.__name__, value)
|
|
||||||
self.showMessage(
|
|
||||||
"Project file '%s' is corrupted." % filepath, False,
|
|
||||||
QtGui.QMessageBox.Warning, msg)
|
|
||||||
|
|
||||||
def showMessage(
|
|
||||||
self, string, showCancel=False,
|
|
||||||
icon=QtGui.QMessageBox.Information, detail=None):
|
|
||||||
msg = QtGui.QMessageBox()
|
|
||||||
msg.setIcon(icon)
|
|
||||||
msg.setText(string)
|
|
||||||
msg.setDetailedText(detail)
|
|
||||||
if showCancel:
|
|
||||||
msg.setStandardButtons(
|
msg.setStandardButtons(
|
||||||
QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
|
QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
|
||||||
else:
|
else:
|
||||||
|
@ -654,10 +590,46 @@ class MainWindow(QtCore.QObject):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def clear(self):
|
def componentContextMenu(self, QPos):
|
||||||
''' empty out all components and fields, get a blank slate '''
|
'''Appears when right-clicking a component in the list'''
|
||||||
self.selectedComponents = []
|
componentList = self.window.listWidget_componentList
|
||||||
self.window.listWidget_componentList.clear()
|
if not componentList.selectedItems():
|
||||||
for widget in self.pages:
|
return
|
||||||
self.window.stackedWidget.removeWidget(widget)
|
|
||||||
self.pages = []
|
# don't show menu if clicking empty space
|
||||||
|
parentPosition = componentList.mapToGlobal(QtCore.QPoint(0, 0))
|
||||||
|
index = componentList.currentRow()
|
||||||
|
modelIndex = componentList.model().index(index)
|
||||||
|
if not componentList.visualRect(modelIndex).contains(QPos):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.presetManager.findPresets()
|
||||||
|
self.menu = QtGui.QMenu()
|
||||||
|
menuItem = self.menu.addAction("Save Preset")
|
||||||
|
menuItem.triggered.connect(
|
||||||
|
self.presetManager.openSavePresetDialog
|
||||||
|
)
|
||||||
|
|
||||||
|
# submenu for opening presets
|
||||||
|
try:
|
||||||
|
presets = self.presetManager.presets[str(self.core.selectedComponents[index])]
|
||||||
|
self.submenu = QtGui.QMenu("Open Preset")
|
||||||
|
self.menu.addMenu(self.submenu)
|
||||||
|
|
||||||
|
for version, presetName in presets:
|
||||||
|
menuItem = self.submenu.addAction(presetName)
|
||||||
|
menuItem.triggered.connect(
|
||||||
|
lambda _, presetName=presetName:
|
||||||
|
self.presetManager.openPreset(presetName)
|
||||||
|
)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.core.selectedComponents[index].currentPreset:
|
||||||
|
menuItem = self.menu.addAction("Clear Preset")
|
||||||
|
menuItem.triggered.connect(
|
||||||
|
self.presetManager.clearPreset
|
||||||
|
)
|
||||||
|
|
||||||
|
self.menu.move(parentPosition + QPos)
|
||||||
|
self.menu.show()
|
||||||
|
|
177
mainwindow.ui
177
mainwindow.ui
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1008</width>
|
<width>1028</width>
|
||||||
<height>575</height>
|
<height>592</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -108,23 +108,32 @@
|
||||||
<enum>QLayout::SetMinimumSize</enum>
|
<enum>QLayout::SetMinimumSize</enum>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="pushButton_openProject">
|
<spacer name="horizontalSpacer_6">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_projects">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Open Project</string>
|
<string>Projects</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="pushButton_saveProject">
|
<widget class="QPushButton" name="pushButton_presets">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Save Project</string>
|
<string>Presets</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="pushButton_saveAs">
|
|
||||||
<property name="text">
|
|
||||||
<string>Save As</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -141,11 +150,60 @@
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>15</height>
|
<height>2</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="listWidget_componentList">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="acceptDrops">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Sunken</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="tabKeyNavigation">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropOverwriteMode">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropMode">
|
||||||
|
<enum>QAbstractItemView::InternalMove</enum>
|
||||||
|
</property>
|
||||||
|
<property name="defaultDropAction">
|
||||||
|
<enum>Qt::MoveAction</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||||
<item>
|
<item>
|
||||||
|
@ -188,97 +246,6 @@
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>2</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<widget class="QListWidget" name="listWidget_componentList">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="frameShape">
|
|
||||||
<enum>QFrame::StyledPanel</enum>
|
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Sunken</enum>
|
|
||||||
</property>
|
|
||||||
<property name="lineWidth">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="showDropIndicator" stdset="0">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragEnabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropOverwriteMode">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropMode">
|
|
||||||
<enum>QAbstractItemView::NoDragDrop</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_15">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="comboBox_openPreset">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>180</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Component Presets</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="pushButton_savePreset">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Save</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="pushButton_removePreset">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -0,0 +1,290 @@
|
||||||
|
from PyQt4 import QtGui, QtCore
|
||||||
|
import string
|
||||||
|
import os
|
||||||
|
|
||||||
|
import core
|
||||||
|
|
||||||
|
|
||||||
|
class PresetManager(QtGui.QDialog):
|
||||||
|
def __init__(self, window, parent):
|
||||||
|
super().__init__(parent.window)
|
||||||
|
self.parent = parent
|
||||||
|
self.core = parent.core
|
||||||
|
self.settings = parent.settings
|
||||||
|
self.presetDir = self.core.presetDir
|
||||||
|
if not self.settings.value('presetDir'):
|
||||||
|
self.settings.setValue(
|
||||||
|
"presetDir",
|
||||||
|
os.path.join(self.core.dataDir, 'projects'))
|
||||||
|
|
||||||
|
self.findPresets()
|
||||||
|
|
||||||
|
# window
|
||||||
|
self.lastFilter = '*'
|
||||||
|
self.presetRows = [] # list of (comp, vers, name) tuples
|
||||||
|
self.window = window
|
||||||
|
self.window.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
|
||||||
|
|
||||||
|
# connect button signals
|
||||||
|
self.window.pushButton_delete.clicked.connect(self.openDeletePresetDialog)
|
||||||
|
self.window.pushButton_rename.clicked.connect(self.openRenamePresetDialog)
|
||||||
|
self.window.pushButton_import.clicked.connect(self.openImportDialog)
|
||||||
|
self.window.pushButton_export.clicked.connect(self.openExportDialog)
|
||||||
|
self.window.pushButton_close.clicked.connect(self.window.close)
|
||||||
|
|
||||||
|
# create filter box and preset list
|
||||||
|
self.drawFilterList()
|
||||||
|
self.window.comboBox_filter.currentIndexChanged.connect(
|
||||||
|
lambda: self.drawPresetList(
|
||||||
|
self.window.comboBox_filter.currentText(), self.window.lineEdit_search.text()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# make auto-completion for search bar
|
||||||
|
self.autocomplete = QtGui.QStringListModel()
|
||||||
|
completer = QtGui.QCompleter()
|
||||||
|
completer.setModel(self.autocomplete)
|
||||||
|
self.window.lineEdit_search.setCompleter(completer)
|
||||||
|
self.window.lineEdit_search.textChanged.connect(
|
||||||
|
lambda: self.drawPresetList(
|
||||||
|
self.window.comboBox_filter.currentText(), self.window.lineEdit_search.text()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.drawPresetList('*')
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
'''Open a new preset manager window from the mainwindow'''
|
||||||
|
self.findPresets()
|
||||||
|
self.drawFilterList()
|
||||||
|
self.drawPresetList('*')
|
||||||
|
self.window.show()
|
||||||
|
|
||||||
|
def findPresets(self):
|
||||||
|
parseList = []
|
||||||
|
for dirpath, dirnames, filenames in os.walk(self.presetDir):
|
||||||
|
# anything without a subdirectory must be a preset folder
|
||||||
|
if dirnames:
|
||||||
|
continue
|
||||||
|
for preset in filenames:
|
||||||
|
compName = os.path.basename(os.path.dirname(dirpath))
|
||||||
|
compVers = os.path.basename(dirpath)
|
||||||
|
try:
|
||||||
|
parseList.append((compName, int(compVers), preset))
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
self.presets =\
|
||||||
|
{
|
||||||
|
compName : \
|
||||||
|
[
|
||||||
|
(vers, preset) \
|
||||||
|
for name, vers, preset in parseList \
|
||||||
|
if name == compName \
|
||||||
|
] \
|
||||||
|
for compName, _, __ in parseList \
|
||||||
|
}
|
||||||
|
|
||||||
|
def drawPresetList(self, compFilter=None, presetFilter=''):
|
||||||
|
self.window.listWidget_presets.clear()
|
||||||
|
if compFilter:
|
||||||
|
self.lastFilter = str(compFilter)
|
||||||
|
else:
|
||||||
|
compFilter = str(self.lastFilter)
|
||||||
|
self.presetRows = []
|
||||||
|
presetNames = []
|
||||||
|
for component, presets in self.presets.items():
|
||||||
|
if compFilter != '*' and component != compFilter:
|
||||||
|
continue
|
||||||
|
for vers, preset in presets:
|
||||||
|
if not presetFilter or presetFilter in preset:
|
||||||
|
self.window.listWidget_presets.addItem('%s: %s' % (component, preset))
|
||||||
|
self.presetRows.append((component, vers, preset))
|
||||||
|
if preset not in presetNames:
|
||||||
|
presetNames.append(preset)
|
||||||
|
self.autocomplete.setStringList(presetNames)
|
||||||
|
|
||||||
|
def drawFilterList(self):
|
||||||
|
self.window.comboBox_filter.clear()
|
||||||
|
self.window.comboBox_filter.addItem('*')
|
||||||
|
for component in self.presets:
|
||||||
|
self.window.comboBox_filter.addItem(component)
|
||||||
|
|
||||||
|
def clearPreset(self, compI=None):
|
||||||
|
'''Functions on mainwindow level from the context menu'''
|
||||||
|
compI = self.parent.window.listWidget_componentList.currentRow()
|
||||||
|
self.core.clearPreset(compI, self.parent)
|
||||||
|
|
||||||
|
def openSavePresetDialog(self):
|
||||||
|
'''Functions on mainwindow level from the context menu'''
|
||||||
|
window = self.parent.window
|
||||||
|
selectedComponents = self.core.selectedComponents
|
||||||
|
componentList = self.parent.window.listWidget_componentList
|
||||||
|
|
||||||
|
if componentList.currentRow() == -1:
|
||||||
|
return
|
||||||
|
while True:
|
||||||
|
index = componentList.currentRow()
|
||||||
|
currentPreset = selectedComponents[index].currentPreset
|
||||||
|
newName, OK = QtGui.QInputDialog.getText(
|
||||||
|
self.parent.window,
|
||||||
|
'Audio Visualizer',
|
||||||
|
'New Preset Name:',
|
||||||
|
QtGui.QLineEdit.Normal,
|
||||||
|
currentPreset
|
||||||
|
)
|
||||||
|
if OK:
|
||||||
|
if core.Core.badName(newName):
|
||||||
|
self.warnMessage(self.parent.window)
|
||||||
|
continue
|
||||||
|
if newName:
|
||||||
|
if index != -1:
|
||||||
|
selectedComponents[index].currentPreset = newName
|
||||||
|
saveValueStore = \
|
||||||
|
selectedComponents[index].savePreset()
|
||||||
|
componentName = str(selectedComponents[index]).strip()
|
||||||
|
vers = selectedComponents[index].version()
|
||||||
|
self.createNewPreset(
|
||||||
|
componentName, vers, newName,
|
||||||
|
saveValueStore, window=self.parent.window)
|
||||||
|
self.openPreset(newName)
|
||||||
|
break
|
||||||
|
|
||||||
|
def createNewPreset(
|
||||||
|
self, compName, vers, filename, saveValueStore, **kwargs):
|
||||||
|
path = os.path.join(self.presetDir, compName, str(vers), filename)
|
||||||
|
if self.presetExists(path, **kwargs):
|
||||||
|
return
|
||||||
|
self.core.createPresetFile(compName, vers, filename, saveValueStore)
|
||||||
|
|
||||||
|
def presetExists(self, path, **kwargs):
|
||||||
|
if os.path.exists(path):
|
||||||
|
window = self.window \
|
||||||
|
if 'window' not in kwargs else kwargs['window']
|
||||||
|
ch = self.parent.showMessage(
|
||||||
|
msg="%s already exists! Overwrite it?" %
|
||||||
|
os.path.basename(path),
|
||||||
|
showCancel=True,
|
||||||
|
icon=QtGui.QMessageBox.Warning,
|
||||||
|
parent=window)
|
||||||
|
if not ch:
|
||||||
|
# user clicked cancel
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def openPreset(self, presetName):
|
||||||
|
componentList = self.parent.window.listWidget_componentList
|
||||||
|
selectedComponents = self.parent.core.selectedComponents
|
||||||
|
|
||||||
|
index = componentList.currentRow()
|
||||||
|
if index == -1:
|
||||||
|
return
|
||||||
|
componentName = str(selectedComponents[index]).strip()
|
||||||
|
version = selectedComponents[index].version()
|
||||||
|
dirname = os.path.join(self.presetDir, componentName, str(version))
|
||||||
|
filepath = os.path.join(dirname, presetName)
|
||||||
|
self.core.openPreset(filepath, index, presetName)
|
||||||
|
|
||||||
|
self.parent.updateComponentTitle(index)
|
||||||
|
self.parent.drawPreview()
|
||||||
|
|
||||||
|
def openDeletePresetDialog(self):
|
||||||
|
selected = self.window.listWidget_presets.selectedItems()
|
||||||
|
if not selected:
|
||||||
|
return
|
||||||
|
row = self.window.listWidget_presets.row(selected[0])
|
||||||
|
comp, vers, name = self.presetRows[row]
|
||||||
|
ch = self.parent.showMessage(
|
||||||
|
msg='Really delete %s?' % name,
|
||||||
|
showCancel=True,
|
||||||
|
icon=QtGui.QMessageBox.Warning,
|
||||||
|
parent=self.window
|
||||||
|
)
|
||||||
|
if not ch:
|
||||||
|
return
|
||||||
|
self.deletePreset(comp, vers, name)
|
||||||
|
self.findPresets()
|
||||||
|
self.drawPresetList()
|
||||||
|
|
||||||
|
def deletePreset(self, comp, vers, name):
|
||||||
|
filepath = os.path.join(self.presetDir, comp, str(vers), name)
|
||||||
|
os.remove(filepath)
|
||||||
|
|
||||||
|
def warnMessage(self, window=None):
|
||||||
|
print(window)
|
||||||
|
self.parent.showMessage(
|
||||||
|
msg='Preset names must contain only letters, '
|
||||||
|
'numbers, and spaces.',
|
||||||
|
parent=window if window else self.window)
|
||||||
|
|
||||||
|
def openRenamePresetDialog(self):
|
||||||
|
presetList = self.window.listWidget_presets
|
||||||
|
if presetList.currentRow() == -1:
|
||||||
|
return
|
||||||
|
|
||||||
|
while True:
|
||||||
|
index = presetList.currentRow()
|
||||||
|
newName, OK = QtGui.QInputDialog.getText(
|
||||||
|
self.window,
|
||||||
|
'Preset Manager',
|
||||||
|
'Rename Preset:',
|
||||||
|
QtGui.QLineEdit.Normal,
|
||||||
|
self.presetRows[index][2]
|
||||||
|
)
|
||||||
|
if OK:
|
||||||
|
if core.Core.badName(newName):
|
||||||
|
self.warnMessage()
|
||||||
|
continue
|
||||||
|
if newName:
|
||||||
|
comp, vers, oldName = self.presetRows[index]
|
||||||
|
path = os.path.join(
|
||||||
|
self.presetDir, comp, str(vers))
|
||||||
|
newPath = os.path.join(path, newName)
|
||||||
|
oldPath = os.path.join(path, oldName)
|
||||||
|
if self.presetExists(newPath):
|
||||||
|
return
|
||||||
|
if os.path.exists(newPath):
|
||||||
|
os.remove(newPath)
|
||||||
|
os.rename(oldPath, newPath)
|
||||||
|
self.findPresets()
|
||||||
|
self.drawPresetList()
|
||||||
|
break
|
||||||
|
|
||||||
|
def openImportDialog(self):
|
||||||
|
filename = QtGui.QFileDialog.getOpenFileName(
|
||||||
|
self.window, "Import Preset File",
|
||||||
|
self.settings.value("presetDir"),
|
||||||
|
"Preset Files (*.avl)")
|
||||||
|
if filename:
|
||||||
|
# get installed path & ask user to overwrite if needed
|
||||||
|
path = ''
|
||||||
|
while True:
|
||||||
|
if path:
|
||||||
|
if self.presetExists(path):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if os.path.exists(path):
|
||||||
|
os.remove(path)
|
||||||
|
success, path = self.core.importPreset(filename)
|
||||||
|
if success:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.findPresets()
|
||||||
|
self.drawPresetList()
|
||||||
|
self.settings.setValue("presetDir", os.path.dirname(filename))
|
||||||
|
|
||||||
|
def openExportDialog(self):
|
||||||
|
if not self.window.listWidget_presets.selectedItems():
|
||||||
|
return
|
||||||
|
filename = QtGui.QFileDialog.getSaveFileName(
|
||||||
|
self.window, "Export Preset",
|
||||||
|
self.settings.value("presetDir"),
|
||||||
|
"Preset Files (*.avl)")
|
||||||
|
if filename:
|
||||||
|
index = self.window.listWidget_presets.currentRow()
|
||||||
|
comp, vers, name = self.presetRows[index]
|
||||||
|
if not self.core.exportPreset(filename, comp, vers, name):
|
||||||
|
self.parent.showMessage(
|
||||||
|
msg='Couldn\'t export %s.' % filename,
|
||||||
|
parent=self.window
|
||||||
|
)
|
||||||
|
self.settings.setValue("presetDir", os.path.dirname(filename))
|
|
@ -0,0 +1,150 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>presetmanager</class>
|
||||||
|
<widget class="QWidget" name="presetmanager">
|
||||||
|
<property name="windowModality">
|
||||||
|
<enum>Qt::NonModal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>497</width>
|
||||||
|
<height>377</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Preset Manager</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit_search">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Filter by name</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboBox_filter">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>200</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="listWidget_presets">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="tabKeyNavigation">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetMinimumSize</enum>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_import">
|
||||||
|
<property name="text">
|
||||||
|
<string>Import</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_export">
|
||||||
|
<property name="text">
|
||||||
|
<string>Export</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<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="QPushButton" name="pushButton_rename">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Rename</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_delete">
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item alignment="Qt::AlignRight">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><span style=" font-size:10pt; font-style:italic;">Right-click components in the main window to create presets</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<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="QPushButton" name="pushButton_close">
|
||||||
|
<property name="text">
|
||||||
|
<string>Close</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -1,11 +1,9 @@
|
||||||
from PyQt4 import QtCore, QtGui, uic
|
from PyQt4 import QtCore, QtGui, uic
|
||||||
from PyQt4.QtCore import pyqtSignal, pyqtSlot
|
from PyQt4.QtCore import pyqtSignal, pyqtSlot
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image
|
||||||
from PIL.ImageQt import ImageQt
|
from PIL.ImageQt import ImageQt
|
||||||
import core
|
import core
|
||||||
import time
|
|
||||||
from queue import Queue, Empty
|
from queue import Queue, Empty
|
||||||
import numpy
|
|
||||||
import os
|
import os
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
|
@ -18,6 +16,7 @@ class Worker(QtCore.QObject):
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
||||||
parent.newTask.connect(self.createPreviewImage)
|
parent.newTask.connect(self.createPreviewImage)
|
||||||
parent.processTask.connect(self.process)
|
parent.processTask.connect(self.process)
|
||||||
|
self.parent = parent
|
||||||
self.core = core.Core()
|
self.core = core.Core()
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.core.settings = parent.settings
|
self.core.settings = parent.settings
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Worker(QtCore.QObject):
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
||||||
self.core = core.Core()
|
self.core = core.Core()
|
||||||
self.core.settings = parent.settings
|
self.core.settings = parent.settings
|
||||||
self.modules = parent.modules
|
self.modules = parent.core.modules
|
||||||
self.stackedWidget = parent.window.stackedWidget
|
self.stackedWidget = parent.window.stackedWidget
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
parent.videoTask.connect(self.createVideo)
|
parent.videoTask.connect(self.createVideo)
|
||||||
|
|
Reference in New Issue