aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--command.py244
-rw-r--r--components/__base__.py66
-rw-r--r--components/color.py11
-rw-r--r--components/image.py17
-rw-r--r--components/original.py19
-rw-r--r--components/text.py30
-rw-r--r--components/video.py17
-rw-r--r--core.py65
-rw-r--r--main.py71
-rw-r--r--mainwindow.py77
-rw-r--r--video_thread.py31
11 files changed, 408 insertions, 240 deletions
diff --git a/command.py b/command.py
index a610d8c..1a1e810 100644
--- a/command.py
+++ b/command.py
@@ -1,122 +1,126 @@
-# 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 sys
+
+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.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 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')
+ self.parser.add_argument(
+ '-o', '--output', metavar='OUTPUT',
+ help='output video file')
+
+ # 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', '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(
+ os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat)
+ LoadDefaultSettings(self)
+
+ 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:
+ pos = comp[0]
+ name = comp[1]
+ args = comp[2:]
+ 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(1)
+ modI = self.core.moduleIndexFor(realName)
+ i = self.core.insertComponent(pos, modI, self)
+ for arg in args:
+ self.core.selectedComponents[i].command(arg)
+
+ 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)
+ 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,
+ list(reversed(self.core.selectedComponents))
+ )
+
+ def videoCreated(self):
+ self.videoThread.quit()
+ self.videoThread.wait()
+ quit(0)
+
+ def showMessage(self, **kwargs):
+ print(kwargs['msg'])
+ if 'detail' in kwargs:
+ print(kwargs['detail'])
+
+ def drawPreview(self, *args):
+ pass
+
+ def parseCompName(self, name):
+ '''Deduces a proper component name out of a commandline arg'''
+
+ 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]
+ return
+
+ return None
diff --git a/components/__base__.py b/components/__base__.py
index 88f22d4..bef7f0e 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,60 @@ 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(
+ self.__doc__, 'Usage:\n'
+ 'Open a preset for this component:\n'
+ ' "preset=Preset Name"')
+ 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 +101,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,7 +121,7 @@ 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
@@ -102,12 +140,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/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 b6aa29b..f8ae64e 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=') and '=' in arg:
+ key, arg = arg.split('=', 1)
+ if key == 'path' and os.path.exists(arg):
+ try:
+ Image.open(arg)
+ 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('Load an image:\n path=/filepath/to/image.png')
diff --git a/components/original.py b/components/original.py
index 5e2f9d4..6222157 100644
--- a/components/original.py
+++ b/components/original.py
@@ -183,3 +183,22 @@ class Component(__base__.Component):
return im
+ def command(self, arg):
+ 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:\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 f8ef7b3..2375dcd 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(
@@ -146,3 +148,29 @@ class Component(__base__.Component):
return
self.page.lineEdit_textColor.setText(RGBstring)
self.page.pushButton_textColor.setStyleSheet(btnStyle)
+
+ 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=') 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 3d43a18..1d250bd 100644
--- a/components/video.py
+++ b/components/video.py
@@ -221,6 +221,23 @@ 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=') 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.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('Load a video:\n path=/filepath/to/video.mp4')
+
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 e5a9b70..de6ed99 100644
--- a/core.py
+++ b/core.py
@@ -43,6 +43,7 @@ class Core():
'*.wav',
'*.ogg',
'*.fla',
+ '*.flac',
'*.aac',
])
self.imageFormats = Core.appendUppercase([
@@ -77,24 +78,32 @@ 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):
component.compPos = i
- def insertComponent(self, compPos, moduleIndex):
- if compPos < 0:
- compPos = len(self.selectedComponents) -1
+ def insertComponent(self, compPos, moduleIndex, loader):
+ '''Creates a new component'''
+ if compPos < 0 or compPos > len(self.selectedComponents):
+ compPos = len(self.selectedComponents)
if len(self.selectedComponents) > 50:
return None
component = self.modules[moduleIndex].Component(
- moduleIndex, compPos)
+ moduleIndex, compPos, self)
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):
@@ -117,15 +126,11 @@ 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, 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'''
@@ -143,6 +148,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):
@@ -154,8 +163,13 @@ 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.
+ '''
+ if not os.path.exists(filepath):
+ loader.showMessage(msg='Project file not found')
+ return
+
errcode, data = self.parseAvFile(filepath)
if errcode == 0:
try:
@@ -175,10 +189,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:
@@ -196,7 +213,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()
@@ -208,7 +227,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,
@@ -218,12 +238,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 = ''
@@ -307,8 +329,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 08add50..106bd29 100644
--- a/main.py
+++ b/main.py
@@ -1,16 +1,10 @@
-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
import core
import preview_thread
import video_thread
-from mainwindow import *
def LoadDefaultSettings(self):
@@ -36,42 +30,59 @@ 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")
- if getattr(sys, 'frozen', False):
- # frozen
- wd = os.path.dirname(sys.executable)
- else:
- # unfrozen
- wd = os.path.dirname(os.path.realpath(__file__))
+ if mode == 'cmd':
+ from command import *
+
+ main = Command()
+
+ elif mode == 'gui':
+ from mainwindow import *
+ import atexit
+ import signal
+
+ if getattr(sys, 'frozen', False):
+ # frozen
+ wd = os.path.dirname(sys.executable)
+ else:
+ # unfrozen
+ wd = os.path.dirname(os.path.realpath(__file__))
- window = uic.loadUi(os.path.join(wd, "mainwindow.ui"))
- # window.adjustSize()
- desc = QtGui.QDesktopWidget()
- dpi = desc.physicalDpiX()
+ window = uic.loadUi(os.path.join(wd, "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)
+ 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)
+ main = MainWindow(window, proj)
- signal.signal(signal.SIGINT, main.cleanUp)
- atexit.register(main.cleanUp)
+ signal.signal(signal.SIGINT, main.cleanUp)
+ atexit.register(main.cleanUp)
+ # applicable to both modes
sys.exit(app.exec_())
diff --git a/mainwindow.py b/mainwindow.py
index d21ca49..f3d45e6 100644
--- a/mainwindow.py
+++ b/mainwindow.py
@@ -45,7 +45,7 @@ class MainWindow(QtGui.QMainWindow):
processTask = QtCore.pyqtSignal()
videoTask = QtCore.pyqtSignal(str, str, list)
- def __init__(self, window):
+ def __init__(self, window, project):
QtGui.QMainWindow.__init__(self)
# print('main thread id: {}'.format(QtCore.QThread.currentThreadId()))
@@ -149,7 +149,7 @@ class MainWindow(QtGui.QMainWindow):
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)
@@ -209,24 +209,36 @@ class MainWindow(QtGui.QMainWindow):
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)
# Setup Hotkeys
QtGui.QShortcut("Ctrl+S", self.window, self.saveCurrentProject)
@@ -261,7 +273,8 @@ class MainWindow(QtGui.QMainWindow):
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)
@@ -273,7 +286,6 @@ class MainWindow(QtGui.QMainWindow):
else:
modified = (presetStore != self.core.savedPresets[name])
else:
- print(pos, presetStore)
modified = bool(presetStore)
if pos < 0:
pos = len(self.core.selectedComponents)-1
@@ -327,10 +339,14 @@ class MainWindow(QtGui.QMainWindow):
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):
@@ -432,6 +448,7 @@ class MainWindow(QtGui.QMainWindow):
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)
@@ -458,19 +475,11 @@ class MainWindow(QtGui.QMainWindow):
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)
@@ -479,11 +488,10 @@ class MainWindow(QtGui.QMainWindow):
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):
@@ -620,7 +628,6 @@ class MainWindow(QtGui.QMainWindow):
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 9740641..265feee 100644
--- a/video_thread.py
+++ b/video_thread.py
@@ -27,10 +27,9 @@ 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
+ self.sampleSize = 1470 # 44100 / 30 = 1470
self.canceled = False
self.error = False
self.stopped = False
@@ -100,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")
@@ -121,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
@@ -162,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):
@@ -191,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"))
@@ -280,7 +282,6 @@ class Worker(QtCore.QObject):
self.error = False
self.canceled = False
- self.parent.drawPreview()
self.stopped = True
self.encoding.emit(False)
self.videoCreated.emit()