preset-loading and basic args from commandline

also made some docstrings more informative
This commit is contained in:
tassaron 2017-06-22 18:40:34 -04:00
parent 82011de966
commit 5c74d496a9
10 changed files with 156 additions and 62 deletions

View File

@ -36,7 +36,7 @@ class Command(QtCore.QObject):
help='open a project file (.avp)', nargs='?')
self.parser.add_argument(
'-c', '--comp', metavar=('LAYER', 'NAME', 'ARG'),
help='create/edit component NAME at LAYER.'
help='create component NAME at LAYER.'
'"help" for information about possible args', nargs=3,
action='append')
@ -80,12 +80,18 @@ class Command(QtCore.QObject):
if self.args.comp:
for comp in self.args.comp:
pos, name, arg = comp
try:
pos = int(pos)
except ValueError:
print(pos, 'is not a layer number.')
quit(1)
realName = self.parseCompName(name)
if not realName:
print(name, 'is not a valid component name.')
quit()
quit(1)
modI = self.core.moduleIndexFor(realName)
self.core.insertComponent(int(pos), modI, self)
i = self.core.insertComponent(pos, modI, self)
self.core.selectedComponents[i].command(arg)
self.createAudioVisualisation()
@ -99,12 +105,12 @@ class Command(QtCore.QObject):
self.videoTask.emit(
self.args.input,
self.args.output,
self.core.selectedComponents)
list(reversed(self.core.selectedComponents))
)
def videoCreated(self):
self.videoThread.quit()
self.videoThread.wait()
self.cleanUp()
def showMessage(self, **kwargs):
print(kwargs['msg'])
@ -114,22 +120,20 @@ class Command(QtCore.QObject):
def drawPreview(self, *args):
pass
def cleanUp(self, *args):
pass
def parseCompName(self, name):
'''Deduces a proper component name out of a commandline arg'''
compFileNames = [ \
os.path.splitext(os.path.basename(
mod.__file__))[0] \
for mod in self.core.modules \
]
if name.title() in self.core.compNames:
return name.title()
for compName in self.core.compNames:
if name.capitalize() in compName:
return compName
compFileNames = [ \
os.path.splitext(os.path.basename(
mod.__file__))[0] \
for mod in self.core.modules \
]
for i, compFileName in enumerate(compFileNames):
if name.lower() in compFileName:
return self.core.compNames[i]

View File

@ -1,5 +1,6 @@
from PyQt4 import QtGui, QtCore
from PIL import Image
import os
class Component(QtCore.QObject):
@ -7,11 +8,12 @@ class Component(QtCore.QObject):
# modified = QtCore.pyqtSignal(int, bool)
def __init__(self, moduleIndex, compPos):
def __init__(self, moduleIndex, compPos, core):
super().__init__()
self.currentPreset = None
self.moduleIndex = moduleIndex
self.compPos = compPos
self.core = core
def __str__(self):
return self.__doc__
@ -32,24 +34,59 @@ class Component(QtCore.QObject):
# 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
'''Subclasses take (presetDict, presetName=None) as args.
Must use super().loadPreset(presetDict, presetName) first,
then update self.page widgets using the preset dict.
'''
self.currentPreset = presetName \
if presetName != None else presetDict['preset']
'''
def savePreset(self):
return {}
'''
def preFrameRender(self, **kwargs):
'''Triggered only before a video is exported (video_thread.py)
self.worker = the video thread worker
self.completeAudioArray = a list of audio samples
self.sampleSize = number of audio samples per video frame
self.progressBarUpdate = signal to set progress bar number
self.progressBarSetText = signal to set progress bar text
Use the latter two signals to update the MainProgram if needed
for a long initialization procedure (i.e., for a visualizer)
'''
for var, value in kwargs.items():
exec('self.%s = value' % var)
def command(self, arg):
'''Configure a component using argument from the commandline.
Use super().command(arg) at the end of a subclass's method,
if no arguments are found in that method first
'''
if arg.startswith('preset='):
_, preset = arg.split('=', 1)
path = os.path.join(self.core.getPresetDir(self), preset)
if not os.path.exists(path):
print('Couldn\'t locate preset "%s"' % preset)
quit(1)
else:
print('Opening "%s" preset on layer %s' % \
(preset, self.compPos))
self.core.openPreset(path, self.compPos, preset)
else:
print(
'To open a preset for this component:\n'
' "preset=Preset Name"\n')
self.commandHelp()
quit(0)
def commandHelp(self):
'''Print help text for this Component's commandline arguments'''
def blankFrame(self, width, height):
return Image.new("RGBA", (width, height), (0, 0, 0, 0))
def pickColor(self):
'''Use color picker to get color input from the user,
and return this as an RGB string and QPushButton stylesheet.
In a subclass apply stylesheet to any color selection widgets
'''
dialog = QtGui.QColorDialog()
dialog.setOption(QtGui.QColorDialog.ShowAlphaChannel, True)
color = dialog.getColor()
@ -63,7 +100,7 @@ class Component(QtCore.QObject):
return None, None
def RGBFromString(self, string):
''' turns an RGB string like "255, 255, 255" into a tuple '''
''' Turns an RGB string like "255, 255, 255" into a tuple '''
try:
tup = tuple([int(i) for i in string.split(',')])
if len(tup) != 3:
@ -83,14 +120,10 @@ class Component(QtCore.QObject):
self.parent = parent
page = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'example.ui'))
# connect widgets signals
# --- connect widget signals here ---
self.page = page
return page
def update(self):
super().update()
self.parent.drawPreview()
def previewRender(self, previewWorker):
width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight'))
@ -102,12 +135,6 @@ class Component(QtCore.QObject):
height = int(self.worker.core.settings.value('outputHeight'))
image = Image.new("RGBA", (width, height), (0,0,0,0))
return image
def cancel(self):
self.canceled = True
def reset(self):
self.canceled = False
'''
class BadComponentInit(Exception):

View File

@ -92,3 +92,20 @@ class Component(__base__.Component):
self.settings.setValue("backgroundDir", os.path.dirname(filename))
self.page.lineEdit_image.setText(filename)
self.update()
def command(self, arg):
if not arg.startswith('preset='):
if os.path.exists(arg):
try:
Image.open(arg)
self.imagePath = arg
self.stretched = True
return True
except OSError as e:
print("Not a supported image format")
quit(1)
super().command(arg)
def commandHelp(self):
print('Give a complete filepath to an image to load that '
'image with default settings.')

View File

@ -183,3 +183,15 @@ class Component(__base__.Component):
return im
def command(self, arg):
if not arg.startswith('preset='):
if arg == 'classic':
self.layout = 0; return
elif arg == 'split':
self.layout = 1; return
elif arg == 'bottom':
self.layout = 2; return
super().command(arg)
def commandHelp(self):
print('Give a layout name: classic, split, or bottom')

View File

@ -146,3 +146,13 @@ class Component(__base__.Component):
return
self.page.lineEdit_textColor.setText(RGBstring)
self.page.pushButton_textColor.setStyleSheet(btnStyle)
def commandHelp(self, arg):
print('Enter a string to use as centred white text. '
'Use quotes around the string to escape spaces.')
def command(self, arg):
if not arg.startswith('preset='):
self.title = arg
return True
super().command(arg)

View File

@ -221,6 +221,22 @@ class Component(__base__.Component):
width, height = scale(self.scale, width, height, int)
self.chunkSize = 4*width*height
def command(self, arg):
if not arg.startswith('preset='):
if os.path.exists(arg):
if os.path.splitext(arg)[1] in self.core.videoFormats:
self.videoPath = arg
self.scale = 100
return True
else:
print("Not a supported video format")
quit(1)
super().command(arg)
def commandHelp(self):
print('Give a complete filepath to a video to load that '
'video with default settings.')
def scale(scale, width, height, returntype=None):
width = (float(width) / 100.0) * float(scale)
height = (float(height) / 100.0) * float(scale)

22
core.py
View File

@ -86,7 +86,7 @@ class Core():
return None
component = self.modules[moduleIndex].Component(
moduleIndex, compPos)
moduleIndex, compPos, self)
self.selectedComponents.insert(
compPos,
component)
@ -142,6 +142,10 @@ class Core():
self.savedPresets[presetName] = dict(saveValueStore)
return True
def getPresetDir(self, comp):
return os.path.join(
self.presetDir, str(comp), str(comp.version()))
def getPreset(self, filepath):
'''Returns the preset dict stored at this filepath'''
if not os.path.exists(filepath):
@ -153,10 +157,11 @@ class Core():
return saveValueStore
def openProject(self, loader, filepath):
'''loader is the object calling this method which must have
its own showMessage(**kwargs) method for displaying errors'''
''' loader is the object calling this method which must have
its own showMessage(**kwargs) method for displaying errors.
'''
errcode, data = self.parseAvFile(filepath)
print(data)
#print(data)
if errcode == 0:
try:
for i, tup in enumerate(data['Components']):
@ -224,12 +229,14 @@ class Core():
def parseAvFile(self, filepath):
'''Parses an avp (project) or avl (preset package) file.
Returns data usable by another method.'''
Returns dictionary with section names as the keys, each one
contains a list of tuples: (compName, version, compPresetDict)
'''
data = {}
try:
with open(filepath, 'r') as f:
def parseLine(line):
'''Decides if a given avp or avl line is a section header'''
'''Decides if a file line is a section header'''
validSections = ('Components')
line = line.strip()
newSection = ''
@ -313,8 +320,7 @@ class Core():
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.'''
Or if filepath is empty, create an internal preset using args'''
if not filepath:
dirname = os.path.join(self.presetDir, compName, str(vers))
if not os.path.exists(dirname):

12
main.py
View File

@ -1,8 +1,6 @@
from PyQt4 import QtGui, uic
import sys
import os
import atexit
import signal
import core
import preview_thread
@ -32,7 +30,7 @@ def LoadDefaultSettings(self):
}
for parm, value in default.items():
print(parm, self.settings.value(parm))
#print(parm, self.settings.value(parm))
if self.settings.value(parm) is None:
self.settings.setValue(parm, value)
@ -62,6 +60,8 @@ if __name__ == "__main__":
elif mode == 'gui':
from mainwindow import *
import atexit
import signal
window = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)), "mainwindow.ui"))
@ -73,10 +73,10 @@ if __name__ == "__main__":
window.resize(window.width() * (dpi / 96), window.height() * (dpi / 96))
# window.verticalLayout_2.setContentsMargins(0, topMargin, 0, 0)
signal.signal(signal.SIGINT, main.cleanUp)
atexit.register(main.cleanUp)
main = MainWindow(window, proj)
# applicable to both modes
signal.signal(signal.SIGINT, main.cleanUp)
atexit.register(main.cleanUp)
sys.exit(app.exec_())

View File

@ -597,7 +597,6 @@ class MainWindow(QtCore.QObject):
self.openProject(filename)
def openProject(self, filepath, prompt=True):
print('opening', filepath)
if not filepath or not os.path.exists(filepath) \
or not filepath.endswith('.avp'):
self.updateWindowTitle()

View File

@ -29,7 +29,7 @@ class Worker(QtCore.QObject):
self.modules = parent.core.modules
self.parent = parent
parent.videoTask.connect(self.createVideo)
self.sampleSize = 1470
self.sampleSize = 1470 # 44100 / 30 = 1470
self.canceled = False
self.error = False
self.stopped = False
@ -99,7 +99,8 @@ class Worker(QtCore.QObject):
# test if user has libfdk_aac
encoders = sp.check_output(
self.core.FFMPEG_BIN + " -encoders -hide_banner", shell=True)
self.core.FFMPEG_BIN + " -encoders -hide_banner",
shell=True)
encoders = encoders.decode("utf-8")
@ -120,15 +121,15 @@ class Worker(QtCore.QObject):
vencoders = options['video-codecs'][vcodec]
aencoders = options['audio-codecs'][acodec]
print(encoders)
#print(encoders)
for encoder in vencoders:
print(encoder)
#print(encoder)
if encoder in encoders:
vencoder = encoder
break
for encoder in aencoders:
print(encoder)
#print(encoder)
if encoder in encoders:
aencoder = encoder
break
@ -161,16 +162,15 @@ class Worker(QtCore.QObject):
ffmpegCommand.append('-2')
ffmpegCommand.append(outputFile)
self.out_pipe = sp.Popen(
ffmpegCommand, stdin=sp.PIPE, stdout=sys.stdout, stderr=sys.stdout)
# create video for output
# ### Now start creating video for output ###
numpy.seterr(divide='ignore')
# initialize components
print('loaded components:',
["%s%s" % (num, str(component)) for num,
component in enumerate(self.components)])
# Call preFrameRender on all components
print('Loaded Components:', ", ".join(
["%s) %s" % (num, str(component)) \
for num, component in enumerate(reversed(self.components))
]))
self.staticComponents = {}
numComps = len(self.components)
for compNo, comp in enumerate(self.components):
@ -190,14 +190,17 @@ class Worker(QtCore.QObject):
comp.frameRender(compNo, 0, 0))
self.progressBarUpdate.emit(100)
# Create ffmpeg pipe and queues for frames
self.out_pipe = sp.Popen(
ffmpegCommand, stdin=sp.PIPE, stdout=sys.stdout, stderr=sys.stdout)
self.compositeQueue = Queue()
self.compositeQueue.maxsize = 20
self.renderQueue = PriorityQueue()
self.renderQueue.maxsize = 20
self.previewQueue = PriorityQueue()
self.renderThreads = []
# Threads to render frames and send them back here for piping out
self.renderThreads = []
for i in range(3):
self.renderThreads.append(
Thread(target=self.renderNode, name="Render Thread"))