From 044fddfa9c5063f61e4a97993efe7cd5b2bae066 Mon Sep 17 00:00:00 2001 From: tassaron Date: Sun, 18 Jun 2017 14:46:08 -0400 Subject: basic commandline functionality using 3 args needs more args so components can be modified without gui --- command.py | 215 +++++++++++++++++++++++++++---------------------------------- 1 file changed, 95 insertions(+), 120 deletions(-) (limited to 'command.py') diff --git a/command.py b/command.py index a610d8c..1b07afc 100644 --- a/command.py +++ b/command.py @@ -1,122 +1,97 @@ -# FIXME: commandline functionality broken until we decide how to implement it -''' +from PyQt4 import QtCore +from PyQt4.QtCore import QSettings +import argparse +import os + +import core +import video_thread +from main import LoadDefaultSettings + + class Command(QtCore.QObject): - videoTask = QtCore.pyqtSignal(str, str, str, list) - - def __init__(self): - QtCore.QObject.__init__(self) - self.modules = [] - self.selectedComponents = [] - - import argparse - self.parser = argparse.ArgumentParser( - description='Create a visualization for an audio file') - self.parser.add_argument( - '-i', '--input', dest='input', help='input audio file', required=True) - self.parser.add_argument( - '-o', '--output', dest='output', - help='output video file', required=True) - self.parser.add_argument( - '-b', '--background', dest='bgimage', - help='background image file', required=True) - self.parser.add_argument( - '-t', '--text', dest='text', help='title text', required=True) - self.parser.add_argument( - '-f', '--font', dest='font', help='title font', required=False) - self.parser.add_argument( - '-s', '--fontsize', dest='fontsize', - help='title font size', required=False) - self.parser.add_argument( - '-c', '--textcolor', dest='textcolor', - help='title text color in r,g,b format', required=False) - self.parser.add_argument( - '-C', '--viscolor', dest='viscolor', - help='visualization color in r,g,b format', required=False) - self.parser.add_argument( - '-x', '--xposition', dest='xposition', - help='x position', required=False) - self.parser.add_argument( - '-y', '--yposition', dest='yposition', - help='y position', required=False) - self.parser.add_argument( - '-a', '--alignment', dest='alignment', - help='title alignment', required=False, - type=int, choices=[0, 1, 2]) - self.args = self.parser.parse_args() - - self.settings = QSettings('settings.ini', QSettings.IniFormat) - LoadDefaultSettings(self) - - # load colours as tuples from comma-separated strings - self.textColor = core.Core.RGBFromString( - self.settings.value("textColor", '255, 255, 255')) - self.visColor = core.Core.RGBFromString( - self.settings.value("visColor", '255, 255, 255')) - if self.args.textcolor: - self.textColor = core.Core.RGBFromString(self.args.textcolor) - if self.args.viscolor: - self.visColor = core.Core.RGBFromString(self.args.viscolor) - - # font settings - if self.args.font: - self.font = QFont(self.args.font) - else: - self.font = QFont(self.settings.value("titleFont", QFont())) - - if self.args.fontsize: - self.fontsize = int(self.args.fontsize) - else: - self.fontsize = int(self.settings.value("fontSize", 35)) - if self.args.alignment: - self.alignment = int(self.args.alignment) - else: - self.alignment = int(self.settings.value("alignment", 0)) - - if self.args.xposition: - self.textX = int(self.args.xposition) - else: - self.textX = int(self.settings.value("xPosition", 70)) - - if self.args.yposition: - self.textY = int(self.args.yposition) - else: - self.textY = int(self.settings.value("yPosition", 375)) - - ffmpeg_cmd = self.settings.value("ffmpeg_cmd", expanduser("~")) - - self.videoThread = QtCore.QThread(self) - self.videoWorker = video_thread.Worker(self) - - self.videoWorker.moveToThread(self.videoThread) - self.videoWorker.videoCreated.connect(self.videoCreated) - - self.videoThread.start() - self.videoTask.emit(self.args.bgimage, - self.args.text, - self.font, - self.fontsize, - self.alignment, - self.textX, - self.textY, - self.textColor, - self.visColor, - self.args.input, - self.args.output, - self.selectedComponents) - - def videoCreated(self): - self.videoThread.quit() - self.videoThread.wait() - self.cleanUp() - - def cleanUp(self): - self.settings.setValue("titleFont", self.font.toString()) - self.settings.setValue("alignment", str(self.alignment)) - self.settings.setValue("fontSize", str(self.fontsize)) - self.settings.setValue("xPosition", str(self.textX)) - self.settings.setValue("yPosition", str(self.textY)) - self.settings.setValue("visColor", '%s,%s,%s' % self.visColor) - self.settings.setValue("textColor", '%s,%s,%s' % self.textColor) - sys.exit(0) -''' + videoTask = QtCore.pyqtSignal(str, str, list) + + def __init__(self): + QtCore.QObject.__init__(self) + self.core = core.Core() + self.dataDir = self.core.dataDir + + self.parser = argparse.ArgumentParser( + description='Create a visualization for an audio file') + self.parser.add_argument( + '-i', '--input', help='input audio file', required=True) + self.parser.add_argument( + '-o', '--output', help='output video file', required=True) + + # optional arguments + self.parser.add_argument( + 'projpath', metavar='path-to-project', + help='open a project file (.avp)', nargs='?') + + ''' + self.parser.add_argument( + '-b', '--background', dest='bgimage', + help='background image file', required=True) + self.parser.add_argument( + '-t', '--text', dest='text', help='title text', required=True) + self.parser.add_argument( + '-f', '--font', dest='font', help='title font', required=False) + self.parser.add_argument( + '-s', '--fontsize', dest='fontsize', + help='title font size', required=False) + self.parser.add_argument( + '-c', '--textcolor', dest='textcolor', + help='title text color in r,g,b format', required=False) + self.parser.add_argument( + '-C', '--viscolor', dest='viscolor', + help='visualization color in r,g,b format', required=False) + self.parser.add_argument( + '-x', '--xposition', dest='xposition', + help='x position', required=False) + self.parser.add_argument( + '-y', '--yposition', dest='yposition', + help='y position', required=False) + self.parser.add_argument( + '-a', '--alignment', dest='alignment', + help='title alignment', required=False, + type=int, choices=[0, 1, 2]) + ''' + + self.args = self.parser.parse_args() + self.settings = QSettings( + os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat) + LoadDefaultSettings(self) + + if self.args.projpath: + self.core.openProject(self, self.args.projpath) + + self.createAudioVisualisation() + + def createAudioVisualisation(self): + self.videoThread = QtCore.QThread(self) + self.videoWorker = video_thread.Worker(self) + self.videoWorker.moveToThread(self.videoThread) + self.videoWorker.videoCreated.connect(self.videoCreated) + + self.videoThread.start() + self.videoTask.emit( + self.args.input, + self.args.output, + self.core.selectedComponents) + + def videoCreated(self): + self.videoThread.quit() + self.videoThread.wait() + self.cleanUp() + + def showMessage(self, **kwargs): + print(kwargs['msg']) + if 'detail' in kwargs: + print(kwargs['detail']) + + def drawPreview(self, *args): + pass + + def cleanUp(self, *args): + pass -- cgit v1.2.3 From 82011de966f95afa88ec9e11e0ce86cbd04d5fc0 Mon Sep 17 00:00:00 2001 From: tassaron Date: Sun, 18 Jun 2017 21:49:00 -0400 Subject: able to create components from commandline TODO: make components respond to argument --- command.py | 47 ++++++++++++++++++++++++++++++++++++++++++++--- core.py | 4 ++-- 2 files changed, 46 insertions(+), 5 deletions(-) (limited to 'command.py') diff --git a/command.py b/command.py index 1b07afc..d56c64b 100644 --- a/command.py +++ b/command.py @@ -18,16 +18,27 @@ class Command(QtCore.QObject): self.dataDir = self.core.dataDir self.parser = argparse.ArgumentParser( - description='Create a visualization for an audio file') + description='Create a visualization for an audio file', + epilog='EXAMPLE COMMAND: main.py myvideotemplate.avp ' + '-i ~/Music/song.mp3 -o ~/video.mp4 ' + '-c 0 image ~/Pictures/thisWeeksPicture.jpg ' + '-c 1 vis classic') self.parser.add_argument( - '-i', '--input', help='input audio file', required=True) + '-i', '--input', metavar='SOUND', + help='input audio file', required=True) self.parser.add_argument( - '-o', '--output', help='output video file', required=True) + '-o', '--output', metavar='OUTPUT', + help='output video file', required=True) # optional arguments self.parser.add_argument( 'projpath', metavar='path-to-project', 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" for information about possible args', nargs=3, + action='append') ''' self.parser.add_argument( @@ -66,6 +77,16 @@ class Command(QtCore.QObject): if self.args.projpath: self.core.openProject(self, self.args.projpath) + if self.args.comp: + for comp in self.args.comp: + pos, name, arg = comp + realName = self.parseCompName(name) + if not realName: + print(name, 'is not a valid component name.') + quit() + modI = self.core.moduleIndexFor(realName) + self.core.insertComponent(int(pos), modI, self) + self.createAudioVisualisation() def createAudioVisualisation(self): @@ -95,3 +116,23 @@ class Command(QtCore.QObject): 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 + for i, compFileName in enumerate(compFileNames): + if name.lower() in compFileName: + return self.core.compNames[i] + return + + return None diff --git a/core.py b/core.py index 5e4071a..2dde464 100644 --- a/core.py +++ b/core.py @@ -72,6 +72,7 @@ class Core(): for name in findComponents() ] self.moduleIndexes = [i for i in range(len(self.modules))] + self.compNames = [mod.Component.__doc__ for mod in self.modules] def componentListChanged(self): for i, component in enumerate(self.selectedComponents): @@ -119,8 +120,7 @@ class Core(): self.selectedComponents[i].update() def moduleIndexFor(self, compName): - compNames = [mod.Component.__doc__ for mod in self.modules] - index = compNames.index(compName) + index = self.compNames.index(compName) return self.moduleIndexes[index] def clearPreset(self, compIndex): -- cgit v1.2.3 From 5c74d496a960042ed4a4279328dc81e23dfdc1d9 Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 22 Jun 2017 18:40:34 -0400 Subject: preset-loading and basic args from commandline also made some docstrings more informative --- command.py | 30 ++++++++++++---------- components/__base__.py | 69 +++++++++++++++++++++++++++++++++++--------------- components/image.py | 17 +++++++++++++ components/original.py | 12 +++++++++ components/text.py | 10 ++++++++ components/video.py | 16 ++++++++++++ core.py | 22 ++++++++++------ main.py | 12 ++++----- mainwindow.py | 1 - video_thread.py | 29 +++++++++++---------- 10 files changed, 156 insertions(+), 62 deletions(-) (limited to 'command.py') diff --git a/command.py b/command.py index d56c64b..97eddd2 100644 --- a/command.py +++ b/command.py @@ -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] diff --git a/components/__base__.py b/components/__base__.py index 88f22d4..e43a517 100644 --- a/components/__base__.py +++ b/components/__base__.py @@ -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): diff --git a/components/image.py b/components/image.py index b6aa29b..d0e1894 100644 --- a/components/image.py +++ b/components/image.py @@ -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.') diff --git a/components/original.py b/components/original.py index 5e2f9d4..328d64f 100644 --- a/components/original.py +++ b/components/original.py @@ -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') diff --git a/components/text.py b/components/text.py index f8ef7b3..6c465b1 100644 --- a/components/text.py +++ b/components/text.py @@ -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) diff --git a/components/video.py b/components/video.py index 3d43a18..dd385b4 100644 --- a/components/video.py +++ b/components/video.py @@ -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) diff --git a/core.py b/core.py index 2dde464..42eb44e 100644 --- a/core.py +++ b/core.py @@ -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): diff --git a/main.py b/main.py index 140392c..e04d002 100644 --- a/main.py +++ b/main.py @@ -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_()) diff --git a/mainwindow.py b/mainwindow.py index 2a8762d..6023831 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -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() diff --git a/video_thread.py b/video_thread.py index 2255259..e6c6531 100644 --- a/video_thread.py +++ b/video_thread.py @@ -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")) -- cgit v1.2.3 From b21a953dda4ec54d494c813af8f687d53d3675d9 Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 22 Jun 2017 19:59:31 -0400 Subject: bugfixes --- command.py | 36 ++++++------------------------------ components/__base__.py | 4 ++++ components/text.py | 4 +++- components/video.py | 1 + core.py | 2 +- main.py | 4 ++-- 6 files changed, 17 insertions(+), 34 deletions(-) (limited to 'command.py') diff --git a/command.py b/command.py index 97eddd2..65fe782 100644 --- a/command.py +++ b/command.py @@ -16,13 +16,14 @@ class Command(QtCore.QObject): QtCore.QObject.__init__(self) self.core = core.Core() self.dataDir = self.core.dataDir + self.canceled = False self.parser = argparse.ArgumentParser( description='Create a visualization for an audio file', epilog='EXAMPLE COMMAND: main.py myvideotemplate.avp ' '-i ~/Music/song.mp3 -o ~/video.mp4 ' '-c 0 image ~/Pictures/thisWeeksPicture.jpg ' - '-c 1 vis classic') + '-c 1 video "preset=My Logo" -c 2 vis classic') self.parser.add_argument( '-i', '--input', metavar='SOUND', help='input audio file', required=True) @@ -40,35 +41,6 @@ class Command(QtCore.QObject): '"help" for information about possible args', nargs=3, action='append') - ''' - self.parser.add_argument( - '-b', '--background', dest='bgimage', - help='background image file', required=True) - self.parser.add_argument( - '-t', '--text', dest='text', help='title text', required=True) - self.parser.add_argument( - '-f', '--font', dest='font', help='title font', required=False) - self.parser.add_argument( - '-s', '--fontsize', dest='fontsize', - help='title font size', required=False) - self.parser.add_argument( - '-c', '--textcolor', dest='textcolor', - help='title text color in r,g,b format', required=False) - self.parser.add_argument( - '-C', '--viscolor', dest='viscolor', - help='visualization color in r,g,b format', required=False) - self.parser.add_argument( - '-x', '--xposition', dest='xposition', - help='x position', required=False) - self.parser.add_argument( - '-y', '--yposition', dest='yposition', - help='y position', required=False) - self.parser.add_argument( - '-a', '--alignment', dest='alignment', - help='title alignment', required=False, - type=int, choices=[0, 1, 2]) - ''' - self.args = self.parser.parse_args() self.settings = QSettings( os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat) @@ -76,6 +48,9 @@ class Command(QtCore.QObject): if self.args.projpath: self.core.openProject(self, self.args.projpath) + self.core.selectedComponents = list( + reversed(self.core.selectedComponents)) + self.core.componentListChanged() if self.args.comp: for comp in self.args.comp: @@ -111,6 +86,7 @@ class Command(QtCore.QObject): def videoCreated(self): self.videoThread.quit() self.videoThread.wait() + quit(0) def showMessage(self, **kwargs): print(kwargs['msg']) diff --git a/components/__base__.py b/components/__base__.py index e43a517..bdf6fdd 100644 --- a/components/__base__.py +++ b/components/__base__.py @@ -124,6 +124,10 @@ class Component(QtCore.QObject): 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')) diff --git a/components/text.py b/components/text.py index 6c465b1..536a9ba 100644 --- a/components/text.py +++ b/components/text.py @@ -19,12 +19,14 @@ class Component(__base__.Component): def widget(self, parent): height = int(parent.settings.value('outputHeight')) width = int(parent.settings.value('outputWidth')) + self.parent = parent self.textColor = (255, 255, 255) self.title = 'Text' self.alignment = 1 self.fontSize = height / 13.5 - self.xPosition = width / 2 + fm = QtGui.QFontMetrics(self.titleFont) + self.xPosition = width / 2 - fm.width(self.title)/2 self.yPosition = height / 2 * 1.036 page = uic.loadUi(os.path.join( diff --git a/components/video.py b/components/video.py index dd385b4..66c98ce 100644 --- a/components/video.py +++ b/components/video.py @@ -227,6 +227,7 @@ class Component(__base__.Component): if os.path.splitext(arg)[1] in self.core.videoFormats: self.videoPath = arg self.scale = 100 + self.loopVideo = True return True else: print("Not a supported video format") diff --git a/core.py b/core.py index 42eb44e..2177071 100644 --- a/core.py +++ b/core.py @@ -80,7 +80,7 @@ class Core(): def insertComponent(self, compPos, moduleIndex, loader): '''Creates a new component''' - if compPos < 0: + if compPos < 0 or compPos > len(self.selectedComponents): compPos = len(self.selectedComponents) if len(self.selectedComponents) > 50: return None diff --git a/main.py b/main.py index e04d002..3fd4234 100644 --- a/main.py +++ b/main.py @@ -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) + main = MainWindow(window, proj) + signal.signal(signal.SIGINT, main.cleanUp) atexit.register(main.cleanUp) - main = MainWindow(window, proj) - # applicable to both modes sys.exit(app.exec_()) -- cgit v1.2.3 From 49cda1bf3aa1800459d1085496291bec90ae6a5a Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 22 Jun 2017 20:31:04 -0400 Subject: can send multiple arguments to a component --- command.py | 15 +++++++++------ components/__base__.py | 3 ++- core.py | 5 ++++- 3 files changed, 15 insertions(+), 8 deletions(-) (limited to 'command.py') diff --git a/command.py b/command.py index 65fe782..9012ca4 100644 --- a/command.py +++ b/command.py @@ -36,10 +36,10 @@ class Command(QtCore.QObject): 'projpath', metavar='path-to-project', help='open a project file (.avp)', nargs='?') self.parser.add_argument( - '-c', '--comp', metavar=('LAYER', 'NAME', 'ARG'), - help='create component NAME at LAYER.' - '"help" for information about possible args', nargs=3, - action='append') + '-c', '--comp', metavar=('LAYER', 'ARG'), + help='first arg must be component NAME to insert at LAYER.' + '"help" for information about possible args for a component.', + nargs='*', action='append') self.args = self.parser.parse_args() self.settings = QSettings( @@ -54,7 +54,9 @@ class Command(QtCore.QObject): if self.args.comp: for comp in self.args.comp: - pos, name, arg = comp + pos = comp[0] + name = comp[1] + args = comp[2:] try: pos = int(pos) except ValueError: @@ -66,7 +68,8 @@ class Command(QtCore.QObject): quit(1) modI = self.core.moduleIndexFor(realName) i = self.core.insertComponent(pos, modI, self) - self.core.selectedComponents[i].command(arg) + for arg in args: + self.core.selectedComponents[i].command(arg) self.createAudioVisualisation() diff --git a/components/__base__.py b/components/__base__.py index bdf6fdd..5c8865d 100644 --- a/components/__base__.py +++ b/components/__base__.py @@ -71,7 +71,8 @@ class Component(QtCore.QObject): self.core.openPreset(path, self.compPos, preset) else: print( - 'To open a preset for this component:\n' + self.__doc__, 'Usage:\n' + 'Open a preset for this component:\n' ' "preset=Preset Name"\n') self.commandHelp() quit(0) diff --git a/core.py b/core.py index 2177071..ba71b82 100644 --- a/core.py +++ b/core.py @@ -160,8 +160,11 @@ class Core(): ''' loader is the object calling this method which must have its own showMessage(**kwargs) method for displaying errors. ''' + if not os.path.exists(filepath): + loader.showMessage(msg='Project file not found') + return + errcode, data = self.parseAvFile(filepath) - #print(data) if errcode == 0: try: for i, tup in enumerate(data['Components']): -- cgit v1.2.3 From 3c903794e3588560f2b9d342214009d55a675d5a Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 22 Jun 2017 22:23:04 -0400 Subject: more commandline component options commandline options that existed before the redesign are now back --- command.py | 15 ++++++++++----- components/__base__.py | 2 +- components/color.py | 11 +++++++++++ components/image.py | 14 +++++++------- components/original.py | 23 +++++++++++++++-------- components/text.py | 28 ++++++++++++++++++++++------ components/video.py | 16 ++++++++-------- 7 files changed, 74 insertions(+), 35 deletions(-) (limited to 'command.py') diff --git a/command.py b/command.py index 9012ca4..1a1e810 100644 --- a/command.py +++ b/command.py @@ -2,6 +2,7 @@ from PyQt4 import QtCore from PyQt4.QtCore import QSettings import argparse import os +import sys import core import video_thread @@ -22,14 +23,14 @@ class Command(QtCore.QObject): description='Create a visualization for an audio file', epilog='EXAMPLE COMMAND: main.py myvideotemplate.avp ' '-i ~/Music/song.mp3 -o ~/video.mp4 ' - '-c 0 image ~/Pictures/thisWeeksPicture.jpg ' - '-c 1 video "preset=My Logo" -c 2 vis classic') + '-c 0 image path=~/Pictures/thisWeeksPicture.jpg ' + '-c 1 video "preset=My Logo" -c 2 vis layout=classic') self.parser.add_argument( '-i', '--input', metavar='SOUND', - help='input audio file', required=True) + help='input audio file') self.parser.add_argument( '-o', '--output', metavar='OUTPUT', - help='output video file', required=True) + help='output video file') # optional arguments self.parser.add_argument( @@ -71,7 +72,11 @@ class Command(QtCore.QObject): for arg in args: self.core.selectedComponents[i].command(arg) - self.createAudioVisualisation() + if self.args.input and self.args.output: + self.createAudioVisualisation() + elif 'help' not in sys.argv: + self.parser.print_help() + quit(1) def createAudioVisualisation(self): self.videoThread = QtCore.QThread(self) diff --git a/components/__base__.py b/components/__base__.py index 5c8865d..bef7f0e 100644 --- a/components/__base__.py +++ b/components/__base__.py @@ -73,7 +73,7 @@ class Component(QtCore.QObject): print( self.__doc__, 'Usage:\n' 'Open a preset for this component:\n' - ' "preset=Preset Name"\n') + ' "preset=Preset Name"') self.commandHelp() quit(0) diff --git a/components/color.py b/components/color.py index cb75839..5ffcdea 100644 --- a/components/color.py +++ b/components/color.py @@ -233,3 +233,14 @@ class Component(__base__.Component): else: self.page.lineEdit_color2.setText(RGBstring) self.page.pushButton_color2.setStyleSheet(btnStyle) + + def commandHelp(self): + print('Specify a color:\n color=255,255,255') + + def command(self, arg): + if not arg.startswith('preset=') and '=' in arg: + key, arg = arg.split('=', 1) + if key == 'color': + self.page.lineEdit_color1.setText(arg) + return + super().command(arg) diff --git a/components/image.py b/components/image.py index d0e1894..f8ae64e 100644 --- a/components/image.py +++ b/components/image.py @@ -94,18 +94,18 @@ class Component(__base__.Component): self.update() def command(self, arg): - if not arg.startswith('preset='): - if os.path.exists(arg): + if not arg.startswith('preset=') and '=' in arg: + key, arg = arg.split('=', 1) + if key == 'path' and os.path.exists(arg): try: Image.open(arg) - self.imagePath = arg - self.stretched = True - return True + self.page.lineEdit_image.setText(arg) + self.page.checkBox_stretch.setChecked(True) + return 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.') + print('Load an image:\n path=/filepath/to/image.png') diff --git a/components/original.py b/components/original.py index 328d64f..6222157 100644 --- a/components/original.py +++ b/components/original.py @@ -184,14 +184,21 @@ 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 + if not arg.startswith('preset=') and '=' in arg: + key, arg = arg.split('=', 1) + if key == 'color': + self.page.lineEdit_visColor.setText(arg) + return + elif key == 'layout': + if arg == 'classic': + self.page.comboBox_visLayout.setCurrentIndex(0) + elif arg == 'split': + self.page.comboBox_visLayout.setCurrentIndex(1) + elif arg == 'bottom': + self.page.comboBox_visLayout.setCurrentIndex(2) + return super().command(arg) def commandHelp(self): - print('Give a layout name: classic, split, or bottom') + print('Give a layout name:\n layout=[classic/split/bottom]') + print('Specify a color:\n color=255,255,255') diff --git a/components/text.py b/components/text.py index 536a9ba..2375dcd 100644 --- a/components/text.py +++ b/components/text.py @@ -149,12 +149,28 @@ class Component(__base__.Component): 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 commandHelp(self): + print('Enter a string to use as centred white text:') + print(' "title=User Error"') + print('Specify a text color:\n color=255,255,255') + print('Set custom x, y position:\n x=500 y=500') def command(self, arg): - if not arg.startswith('preset='): - self.title = arg - return True + if not arg.startswith('preset=') and '=' in arg: + key, arg = arg.split('=', 1) + if key == 'color': + self.page.lineEdit_textColor.setText(arg) + return + elif key == 'size': + self.page.spinBox_fontSize.setValue(int(arg)) + return + elif key == 'x': + self.page.spinBox_xTextAlign.setValue(int(arg)) + return + elif key == 'y': + self.page.spinBox_yTextAlign.setValue(int(arg)) + return + elif key == 'title': + self.page.lineEdit_title.setText(arg) + return super().command(arg) diff --git a/components/video.py b/components/video.py index 66c98ce..1d250bd 100644 --- a/components/video.py +++ b/components/video.py @@ -222,21 +222,21 @@ class Component(__base__.Component): self.chunkSize = 4*width*height def command(self, arg): - if not arg.startswith('preset='): - if os.path.exists(arg): + if not arg.startswith('preset=') and '=' in arg: + key, arg = arg.split('=', 1) + if key == 'path' and os.path.exists(arg): if os.path.splitext(arg)[1] in self.core.videoFormats: - self.videoPath = arg - self.scale = 100 - self.loopVideo = True - return True + self.page.lineEdit_video.setText(arg) + self.page.spinBox_scale.setValue(100) + self.page.checkBox_loop.setChecked(True) + return 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.') + print('Load a video:\n path=/filepath/to/video.mp4') def scale(scale, width, height, returntype=None): width = (float(width) / 100.0) * float(scale) -- cgit v1.2.3