From 044fddfa9c5063f61e4a97993efe7cd5b2bae066 Mon Sep 17 00:00:00 2001 From: tassaron Date: Sun, 18 Jun 2017 14:46:08 -0400 Subject: [PATCH 1/6] basic commandline functionality using 3 args needs more args so components can be modified without gui --- command.py | 189 +++++++++++++++++++++--------------------------- core.py | 40 ++++++---- main.py | 54 ++++++++------ mainwindow.py | 76 ++++++++++--------- video_thread.py | 2 - 5 files changed, 183 insertions(+), 178 deletions(-) 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) + videoTask = QtCore.pyqtSignal(str, str, list) - def __init__(self): - QtCore.QObject.__init__(self) - self.modules = [] - self.selectedComponents = [] + def __init__(self): + QtCore.QObject.__init__(self) + self.core = core.Core() + self.dataDir = self.core.dataDir - 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.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) - self.settings = QSettings('settings.ini', QSettings.IniFormat) - LoadDefaultSettings(self) + # optional arguments + self.parser.add_argument( + 'projpath', metavar='path-to-project', + help='open a project file (.avp)', nargs='?') - # 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) + ''' + 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]) + ''' - # font settings - if self.args.font: - self.font = QFont(self.args.font) - else: - self.font = QFont(self.settings.value("titleFont", QFont())) + self.args = self.parser.parse_args() + self.settings = QSettings( + os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat) + LoadDefaultSettings(self) - 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.projpath: + self.core.openProject(self, self.args.projpath) - if self.args.xposition: - self.textX = int(self.args.xposition) - else: - self.textX = int(self.settings.value("xPosition", 70)) + self.createAudioVisualisation() - if self.args.yposition: - self.textY = int(self.args.yposition) - else: - self.textY = int(self.settings.value("yPosition", 375)) + 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) - ffmpeg_cmd = self.settings.value("ffmpeg_cmd", expanduser("~")) + self.videoThread.start() + self.videoTask.emit( + self.args.input, + self.args.output, + self.core.selectedComponents) - self.videoThread = QtCore.QThread(self) - self.videoWorker = video_thread.Worker(self) + def videoCreated(self): + self.videoThread.quit() + self.videoThread.wait() + self.cleanUp() - self.videoWorker.moveToThread(self.videoThread) - self.videoWorker.videoCreated.connect(self.videoCreated) + def showMessage(self, **kwargs): + print(kwargs['msg']) + if 'detail' in kwargs: + print(kwargs['detail']) - 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 drawPreview(self, *args): + pass - 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) -''' + def cleanUp(self, *args): + pass diff --git a/core.py b/core.py index e4a7a6c..5e4071a 100644 --- a/core.py +++ b/core.py @@ -37,6 +37,7 @@ class Core(): '*.wav', '*.ogg', '*.fla', + '*.flac', '*.aac', ]) self.imageFormats = Core.appendUppercase([ @@ -76,9 +77,10 @@ class Core(): for i, component in enumerate(self.selectedComponents): component.compPos = i - def insertComponent(self, compPos, moduleIndex): + def insertComponent(self, compPos, moduleIndex, loader): + '''Creates a new component''' if compPos < 0: - compPos = len(self.selectedComponents) -1 + compPos = len(self.selectedComponents) if len(self.selectedComponents) > 50: return None @@ -87,8 +89,14 @@ class Core(): self.selectedComponents.insert( compPos, component) - self.componentListChanged() + + # init component's widget for loading/saving presets + self.selectedComponents[compPos].widget(loader) + self.updateComponent(compPos) + + if hasattr(loader, 'insertComponent'): + loader.insertComponent(compPos) return compPos def moveComponent(self, startI, endI): @@ -115,11 +123,8 @@ class Core(): index = compNames.index(compName) return self.moduleIndexes[index] - def clearPreset(self, compIndex, loader=None): - '''Clears a preset from a component''' + def clearPreset(self, compIndex): self.selectedComponents[compIndex].currentPreset = None - if loader: - loader.updateComponentTitle(compIndex) def openPreset(self, filepath, compIndex, presetName): '''Applies a preset to a specific component''' @@ -148,9 +153,10 @@ class Core(): return saveValueStore def openProject(self, loader, filepath): - '''loader is the object calling this method (mainwindow/command) - which implements an insertComponent method''' + '''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) if errcode == 0: try: for i, tup in enumerate(data['Components']): @@ -169,10 +175,13 @@ class Core(): # saved preset was renamed or deleted clearThis = True - # insert component into the loader - i = loader.insertComponent( - self.moduleIndexFor(name), -1) + # create the actual component object & get its index + i = self.insertComponent( + -1, + self.moduleIndexFor(name), + loader) if i == None: + loader.showMessage(msg="Too many components!") break try: @@ -190,7 +199,9 @@ class Core(): (self.selectedComponents[i], e)) if clearThis: - self.clearPreset(i, loader) + self.clearPreset(i) + if hasattr(loader, 'updateComponentTitle'): + loader.updateComponentTitle(i) except: errcode = 1 data = sys.exc_info() @@ -202,7 +213,8 @@ class Core(): # probably just an old version, still loadable print('file missing value: %s' % value) return - loader.createNewProject() + if hasattr(loader, 'createNewProject'): + loader.createNewProject() msg = '%s: %s' % (typ.__name__, value) loader.showMessage( msg="Project file '%s' is corrupted." % filepath, diff --git a/main.py b/main.py index 7c0727b..140392c 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,5 @@ -from importlib import import_module from PyQt4 import QtGui, uic -from PyQt4.QtCore import Qt import sys -import io import os import atexit import signal @@ -10,7 +7,6 @@ import signal import core import preview_thread import video_thread -from mainwindow import * def LoadDefaultSettings(self): @@ -36,34 +32,50 @@ def LoadDefaultSettings(self): } for parm, value in default.items(): + print(parm, self.settings.value(parm)) if self.settings.value(parm) is None: self.settings.setValue(parm, value) if __name__ == "__main__": - ''' FIXME commandline functionality broken until we decide how to implement - if len(sys.argv) > 1: - # command line mode - app = QtGui.QApplication(sys.argv, False) - command = Command() - signal.signal(signal.SIGINT, command.cleanUp) - sys.exit(app.exec_()) + mode = 'gui' + if len(sys.argv) > 2: + mode = 'cmd' + + elif len(sys.argv) == 2: + if sys.argv[1].startswith('-'): + mode = 'cmd' + else: + # opening a project file with gui + proj = sys.argv[1] else: - ''' + # normal gui launch + proj = None + app = QtGui.QApplication(sys.argv) app.setApplicationName("audio-visualizer") app.setOrganizationName("audio-visualizer") - window = uic.loadUi(os.path.join( - os.path.dirname(os.path.realpath(__file__)), "mainwindow.ui")) - # window.adjustSize() - desc = QtGui.QDesktopWidget() - dpi = desc.physicalDpiX() - topMargin = 0 if (dpi == 96) else int(10 * (dpi / 96)) - window.resize(window.width() * (dpi / 96), window.height() * (dpi / 96)) - # window.verticalLayout_2.setContentsMargins(0, topMargin, 0, 0) + if mode == 'cmd': + from command import * - main = MainWindow(window) + main = Command() + elif mode == 'gui': + from mainwindow import * + + window = uic.loadUi(os.path.join( + os.path.dirname(os.path.realpath(__file__)), "mainwindow.ui")) + # window.adjustSize() + desc = QtGui.QDesktopWidget() + dpi = desc.physicalDpiX() + + topMargin = 0 if (dpi == 96) else int(10 * (dpi / 96)) + window.resize(window.width() * (dpi / 96), window.height() * (dpi / 96)) + # window.verticalLayout_2.setContentsMargins(0, topMargin, 0, 0) + + main = MainWindow(window, proj) + + # applicable to both modes signal.signal(signal.SIGINT, main.cleanUp) atexit.register(main.cleanUp) diff --git a/mainwindow.py b/mainwindow.py index f722158..2a8762d 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -45,7 +45,7 @@ class MainWindow(QtCore.QObject): processTask = QtCore.pyqtSignal() videoTask = QtCore.pyqtSignal(str, str, list) - def __init__(self, window): + def __init__(self, window, project): QtCore.QObject.__init__(self) # print('main thread id: {}'.format(QtCore.QThread.currentThreadId())) @@ -151,7 +151,7 @@ class MainWindow(QtCore.QObject): for i, comp in enumerate(self.core.modules): action = self.compMenu.addAction(comp.Component.__doc__) action.triggered[()].connect( - lambda item=i: self.insertComponent(item)) + lambda item=i: self.core.insertComponent(0, item, self)) self.window.pushButton_addComponent.setMenu(self.compMenu) @@ -211,24 +211,36 @@ class MainWindow(QtCore.QObject): self.openPresetManager ) - # Show the window and load current project window.show() - self.currentProject = self.settings.value("currentProject") - if self.autosaveExists(identical=True): - # delete autosave if it's identical to the project - os.remove(self.autosavePath) - if self.currentProject and os.path.exists(self.autosavePath): - ch = self.showMessage( - msg="Restore unsaved changes in project '%s'?" - % os.path.basename(self.currentProject)[:-4], - showCancel=True) - if ch: - self.saveProjectChanges() - else: + if project and project != self.autosavePath: + # open a project from the commandline + if not os.path.dirname(project): + project = os.path.join(os.path.expanduser('~'), project) + self.currentProject = project + self.settings.setValue("currentProject", project) + if os.path.exists(self.autosavePath): os.remove(self.autosavePath) + else: + # open the last currentProject from settings + self.currentProject = self.settings.value("currentProject") + + # delete autosave if it's identical to this project + if self.autosaveExists(identical=True): + os.remove(self.autosavePath) + + if self.currentProject and os.path.exists(self.autosavePath): + ch = self.showMessage( + msg="Restore unsaved changes in project '%s'?" + % os.path.basename(self.currentProject)[:-4], + showCancel=True) + if ch: + self.saveProjectChanges() + else: + os.remove(self.autosavePath) + self.openProject(self.currentProject, prompt=False) - self.drawPreview() + self.drawPreview(True) def cleanUp(self): self.timer.stop() @@ -240,7 +252,8 @@ class MainWindow(QtCore.QObject): appName = 'Audio Visualizer' if self.currentProject: appName += ' - %s' % \ - os.path.basename(self.currentProject)[:-4] + os.path.splitext( + os.path.basename(self.currentProject))[0] self.window.setWindowTitle(appName) @QtCore.pyqtSlot(int, dict) @@ -252,7 +265,6 @@ class MainWindow(QtCore.QObject): else: modified = (presetStore != self.core.savedPresets[name]) else: - print(pos, presetStore) modified = bool(presetStore) if pos < 0: pos = len(self.core.selectedComponents)-1 @@ -306,10 +318,14 @@ class MainWindow(QtCore.QObject): self.lastAutosave = time.time() def autosaveExists(self, identical=True): - if self.currentProject and os.path.exists(self.autosavePath) \ - and filecmp.cmp( - self.autosavePath, self.currentProject) == identical: - return True + try: + if self.currentProject and os.path.exists(self.autosavePath) \ + and filecmp.cmp( + self.autosavePath, self.currentProject) == identical: + return True + except FileNotFoundError: + print('project file couldn\'t be located:', self.currentProject) + return identical return False def saveProjectChanges(self): @@ -411,6 +427,7 @@ class MainWindow(QtCore.QObject): self.window.listWidget_componentList.setEnabled(True) self.window.menuButton_newProject.setEnabled(True) self.window.menuButton_openProject.setEnabled(True) + self.drawPreview(True) def progressBarUpdated(self, value): self.window.progressBar_createVideo.setValue(value) @@ -437,19 +454,11 @@ class MainWindow(QtCore.QObject): def showPreviewImage(self, image): self.previewWindow.changePixmap(image) - def insertComponent(self, moduleIndex, compPos=0): + def insertComponent(self, index): componentList = self.window.listWidget_componentList stackedWidget = self.window.stackedWidget - if compPos < 0: - compPos = componentList.count() - index = self.core.insertComponent( - compPos, moduleIndex) - if index == None: - self.showMessage(msg="Too many components!") - return None - - row = componentList.insertItem( + componentList.insertItem( index, self.core.selectedComponents[index].__doc__) componentList.setCurrentRow(index) @@ -458,11 +467,10 @@ class MainWindow(QtCore.QObject): self.core.selectedComponents[index].modified.connect( self.updateComponentTitle) - self.pages.insert(index, self.core.selectedComponents[index].widget(self)) + self.pages.insert(index, self.core.selectedComponents[index].page) stackedWidget.insertWidget(index, self.pages[index]) stackedWidget.setCurrentIndex(index) - self.core.updateComponent(index) return index def removeComponent(self): diff --git a/video_thread.py b/video_thread.py index d7220f1..2255259 100644 --- a/video_thread.py +++ b/video_thread.py @@ -27,7 +27,6 @@ class Worker(QtCore.QObject): self.core = core.Core() self.core.settings = parent.settings self.modules = parent.core.modules - self.stackedWidget = parent.window.stackedWidget self.parent = parent parent.videoTask.connect(self.createVideo) self.sampleSize = 1470 @@ -280,7 +279,6 @@ class Worker(QtCore.QObject): self.error = False self.canceled = False - self.parent.drawPreview() self.stopped = True self.encoding.emit(False) self.videoCreated.emit() From 82011de966f95afa88ec9e11e0ce86cbd04d5fc0 Mon Sep 17 00:00:00 2001 From: tassaron Date: Sun, 18 Jun 2017 21:49:00 -0400 Subject: [PATCH 2/6] 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(-) 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): From 5c74d496a960042ed4a4279328dc81e23dfdc1d9 Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 22 Jun 2017 18:40:34 -0400 Subject: [PATCH 3/6] 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(-) 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")) From b21a953dda4ec54d494c813af8f687d53d3675d9 Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 22 Jun 2017 19:59:31 -0400 Subject: [PATCH 4/6] 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(-) 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_()) From 49cda1bf3aa1800459d1085496291bec90ae6a5a Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 22 Jun 2017 20:31:04 -0400 Subject: [PATCH 5/6] can send multiple arguments to a component --- command.py | 15 +++++++++------ components/__base__.py | 3 ++- core.py | 5 ++++- 3 files changed, 15 insertions(+), 8 deletions(-) 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']): From 3c903794e3588560f2b9d342214009d55a675d5a Mon Sep 17 00:00:00 2001 From: tassaron Date: Thu, 22 Jun 2017 22:23:04 -0400 Subject: [PATCH 6/6] 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(-) 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)