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 --- components/__base__.py | 69 +++++++++++++++++++++++++++++++++++--------------- components/image.py | 17 +++++++++++++ components/original.py | 12 +++++++++ components/text.py | 10 ++++++++ components/video.py | 16 ++++++++++++ 5 files changed, 103 insertions(+), 21 deletions(-) (limited to 'components') 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) -- 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 'components') 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 'components') 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 'components') 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