add component in context menu, del/ins hotkeys

+ preset manager uses mainwindow component list
This commit is contained in:
tassaron 2017-07-20 22:37:15 -04:00
parent f454814867
commit 450b944b87
8 changed files with 123 additions and 69 deletions

View File

@ -2,7 +2,7 @@ from cx_Freeze import setup, Executable
import sys import sys
import os import os
from setup import VERSION from setup import __version__
deps = [os.path.join('src', p) for p in os.listdir('src') if p] deps = [os.path.join('src', p) for p in os.listdir('src') if p]
@ -52,7 +52,7 @@ executables = [
setup( setup(
name='audio-visualizer-python', name='audio-visualizer-python',
version=VERSION, version=__version__,
description='GUI tool to render visualization videos of audio files', description='GUI tool to render visualization videos of audio files',
options=dict(build_exe=buildOptions), options=dict(build_exe=buildOptions),
executables=executables executables=executables

View File

@ -2,7 +2,7 @@ from setuptools import setup
import os import os
VERSION = '2.0.0.rc1' __version__ = '2.0.0.rc1'
def package_files(directory): def package_files(directory):
@ -15,7 +15,7 @@ def package_files(directory):
setup( setup(
name='audio_visualizer_python', name='audio_visualizer_python',
version=VERSION, version=__version__,
url='https://github.com/djfun/audio-visualizer-python/tree/feature-newgui', url='https://github.com/djfun/audio-visualizer-python/tree/feature-newgui',
license='MIT', license='MIT',
description='Create audio visualization videos from a GUI or commandline', description='Create audio visualization videos from a GUI or commandline',

View File

@ -0,0 +1 @@

View File

@ -16,7 +16,7 @@ class Video:
'''Video Component Frame-Fetcher''' '''Video Component Frame-Fetcher'''
def __init__(self, **kwargs): def __init__(self, **kwargs):
mandatoryArgs = [ mandatoryArgs = [
'ffmpeg', # path to ffmpeg, usually core.FFMPEG_BIN 'ffmpeg', # path to ffmpeg, usually Core.FFMPEG_BIN
'videoPath', 'videoPath',
'width', 'width',
'height', 'height',
@ -28,7 +28,7 @@ class Video:
] ]
for arg in mandatoryArgs: for arg in mandatoryArgs:
try: try:
exec('self.%s = kwargs[arg]' % arg) setattr(self, arg, kwargs[arg])
except KeyError: except KeyError:
raise BadComponentInit(arg, self.__doc__) raise BadComponentInit(arg, self.__doc__)

View File

@ -15,16 +15,14 @@ import video_thread
class Core: class Core:
''' '''
MainWindow and Command module both use an instance of this class MainWindow and Command module both use an instance of this class
to store the main program state. This object tracks the components to store the core program state. This object tracks the components,
as an instance, has methods for managing the components and for talks to the components and handles opening/creating project files
opening/creating project files and presets. and presets. The class also stores constants as class variables.
''' '''
@classmethod @classmethod
def storeSettings(cls): def storeSettings(cls):
''' '''Store settings/paths to directories as class variables.'''
Stores settings/paths to directories as class variables
'''
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
# frozen # frozen
wd = os.path.dirname(sys.executable) wd = os.path.dirname(sys.executable)

View File

@ -178,7 +178,6 @@ class MainWindow(QtWidgets.QMainWindow):
# Make component buttons # Make component buttons
self.compMenu = QMenu() self.compMenu = QMenu()
self.compActions = []
for i, comp in enumerate(self.core.modules): for i, comp in enumerate(self.core.modules):
action = self.compMenu.addAction(comp.Component.name) action = self.compMenu.addAction(comp.Component.name)
action.triggered.connect( action.triggered.connect(
@ -191,6 +190,9 @@ class MainWindow(QtWidgets.QMainWindow):
componentList.itemSelectionChanged.connect( componentList.itemSelectionChanged.connect(
self.changeComponentWidget self.changeComponentWidget
) )
componentList.itemSelectionChanged.connect(
self.presetManager.clearPresetListSelection
)
self.window.pushButton_removeComponent.clicked.connect( self.window.pushButton_removeComponent.clicked.connect(
lambda: self.removeComponent() lambda: self.removeComponent()
) )
@ -313,22 +315,23 @@ class MainWindow(QtWidgets.QMainWindow):
) )
self.settings.setValue("ffmpegMsgShown", True) self.settings.setValue("ffmpegMsgShown", True)
# Setup Hotkeys # Hotkeys for projects
QtWidgets.QShortcut("Ctrl+S", self.window, self.saveCurrentProject) QtWidgets.QShortcut("Ctrl+S", self.window, self.saveCurrentProject)
QtWidgets.QShortcut("Ctrl+A", self.window, self.openSaveProjectDialog) QtWidgets.QShortcut("Ctrl+A", self.window, self.openSaveProjectDialog)
QtWidgets.QShortcut("Ctrl+O", self.window, self.openOpenProjectDialog) QtWidgets.QShortcut("Ctrl+O", self.window, self.openOpenProjectDialog)
QtWidgets.QShortcut("Ctrl+N", self.window, self.createNewProject) QtWidgets.QShortcut("Ctrl+N", self.window, self.createNewProject)
QtWidgets.QShortcut(
"Ctrl+Alt+Shift+R", self.window, self.drawPreview
)
QtWidgets.QShortcut(
"Ctrl+Alt+Shift+F", self.window, self.showFfmpegCommand
)
QtWidgets.QShortcut( # Hotkeys for component list
"Ctrl+T", self.window, for inskey in ("Ctrl+T", QtCore.Qt.Key_Insert):
activated=lambda: self.window.pushButton_addComponent.click() QtWidgets.QShortcut(
) inskey, self.window,
activated=lambda: self.window.pushButton_addComponent.click()
)
for delkey in ("Ctrl+R", QtCore.Qt.Key_Delete):
QtWidgets.QShortcut(
delkey, self.window.listWidget_componentList,
self.removeComponent
)
QtWidgets.QShortcut( QtWidgets.QShortcut(
"Ctrl+Space", self.window, "Ctrl+Space", self.window,
activated=lambda: self.window.listWidget_componentList.setFocus() activated=lambda: self.window.listWidget_componentList.setFocus()
@ -342,22 +345,29 @@ class MainWindow(QtWidgets.QMainWindow):
) )
QtWidgets.QShortcut( QtWidgets.QShortcut(
"Ctrl+Up", self.window, "Ctrl+Up", self.window.listWidget_componentList,
activated=lambda: self.moveComponent(-1) activated=lambda: self.moveComponent(-1)
) )
QtWidgets.QShortcut( QtWidgets.QShortcut(
"Ctrl+Down", self.window, "Ctrl+Down", self.window.listWidget_componentList,
activated=lambda: self.moveComponent(1) activated=lambda: self.moveComponent(1)
) )
QtWidgets.QShortcut( QtWidgets.QShortcut(
"Ctrl+Home", self.window, "Ctrl+Home", self.window.listWidget_componentList,
activated=lambda: self.moveComponent('top') activated=lambda: self.moveComponent('top')
) )
QtWidgets.QShortcut( QtWidgets.QShortcut(
"Ctrl+End", self.window, "Ctrl+End", self.window.listWidget_componentList,
activated=lambda: self.moveComponent('bottom') activated=lambda: self.moveComponent('bottom')
) )
QtWidgets.QShortcut("Ctrl+r", self.window, self.removeComponent)
# Debug Hotkeys
QtWidgets.QShortcut(
"Ctrl+Alt+Shift+R", self.window, self.drawPreview
)
QtWidgets.QShortcut(
"Ctrl+Alt+Shift+F", self.window, self.showFfmpegCommand
)
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def cleanUp(self): def cleanUp(self):
@ -677,9 +687,7 @@ class MainWindow(QtWidgets.QMainWindow):
stackedWidget.setCurrentIndex(newRow) stackedWidget.setCurrentIndex(newRow)
self.drawPreview() self.drawPreview()
@disableWhenEncoding def getComponentListRects(self):
def dragComponent(self, event):
'''Used as Qt drop event for the component listwidget'''
componentList = self.window.listWidget_componentList componentList = self.window.listWidget_componentList
modelIndexes = [ modelIndexes = [
@ -690,6 +698,13 @@ class MainWindow(QtWidgets.QMainWindow):
componentList.visualRect(modelIndex) componentList.visualRect(modelIndex)
for modelIndex in modelIndexes for modelIndex in modelIndexes
] ]
return rects
@disableWhenEncoding
def dragComponent(self, event):
'''Used as Qt drop event for the component listwidget'''
componentList = self.window.listWidget_componentList
rects = self.getComponentListRects()
rowPos = [rect.contains(event.pos()) for rect in rects] rowPos = [rect.contains(event.pos()) for rect in rects]
if not any(rowPos): if not any(rowPos):
@ -826,47 +841,63 @@ class MainWindow(QtWidgets.QMainWindow):
@disableWhenEncoding @disableWhenEncoding
def componentContextMenu(self, QPos): def componentContextMenu(self, QPos):
'''Appears when right-clicking a component in the list''' '''Appears when right-clicking the component list'''
componentList = self.window.listWidget_componentList componentList = self.window.listWidget_componentList
if not componentList.selectedItems():
return
# don't show menu if clicking empty space
parentPosition = componentList.mapToGlobal(QtCore.QPoint(0, 0))
index = componentList.currentRow() index = componentList.currentRow()
modelIndex = componentList.model().index(index)
if not componentList.visualRect(modelIndex).contains(QPos):
return
self.presetManager.findPresets()
self.menu = QMenu() self.menu = QMenu()
menuItem = self.menu.addAction("Save Preset") parentPosition = componentList.mapToGlobal(QtCore.QPoint(0, 0))
menuItem.triggered.connect(
self.presetManager.openSavePresetDialog
)
# submenu for opening presets rects = self.getComponentListRects()
try: rowPos = [rect.contains(QPos) for rect in rects]
presets = self.presetManager.presets[ if not any(rowPos):
str(self.core.selectedComponents[index]) # Insert components at the top if clicking nothing
] rowPos = 0
self.submenu = QMenu("Open Preset") else:
self.menu.addMenu(self.submenu) rowPos = rowPos.index(True)
for version, presetName in presets: if index == rowPos:
menuItem = self.submenu.addAction(presetName) # Show preset menu if clicking a component
menuItem.triggered.connect( self.presetManager.findPresets()
lambda _, presetName=presetName: menuItem = self.menu.addAction("Save Preset")
self.presetManager.openPreset(presetName)
)
except KeyError:
pass
if self.core.selectedComponents[index].currentPreset:
menuItem = self.menu.addAction("Clear Preset")
menuItem.triggered.connect( menuItem.triggered.connect(
self.presetManager.clearPreset self.presetManager.openSavePresetDialog
) )
# submenu for opening presets
try:
presets = self.presetManager.presets[
str(self.core.selectedComponents[index])
]
self.presetSubmenu = QMenu("Open Preset")
self.menu.addMenu(self.presetSubmenu)
for version, presetName in presets:
menuItem = self.presetSubmenu.addAction(presetName)
menuItem.triggered.connect(
lambda _, presetName=presetName:
self.presetManager.openPreset(presetName)
)
except KeyError:
pass
if self.core.selectedComponents[index].currentPreset:
menuItem = self.menu.addAction("Clear Preset")
menuItem.triggered.connect(
self.presetManager.clearPreset
)
self.menu.addSeparator()
# "Add Component" submenu
self.submenu = QMenu("Add")
self.menu.addMenu(self.submenu)
for i, comp in enumerate(self.core.modules):
menuItem = self.submenu.addAction(comp.Component.name)
menuItem.triggered.connect(
lambda _, item=i: self.core.insertComponent(
rowPos, item, self
)
)
self.menu.move(parentPosition + QPos) self.menu.move(parentPosition + QPos)
self.menu.show() self.menu.show()

View File

@ -245,11 +245,25 @@ class PresetManager(QtWidgets.QDialog):
def openRenamePresetDialog(self): def openRenamePresetDialog(self):
# TODO: maintain consistency by changing this to call createNewPreset() # TODO: maintain consistency by changing this to call createNewPreset()
presetList = self.window.listWidget_presets presetList = self.window.listWidget_presets
if presetList.currentRow() == -1: index = presetList.currentRow()
return if index == -1:
# check if component selected in MainWindow has preset loaded
componentList = self.parent.window.listWidget_componentList
compIndex = componentList.currentRow()
if compIndex == -1:
return
preset = self.core.selectedComponents[compIndex].currentPreset
if not preset:
return
else:
for i, tup in enumerate(self.presetRows):
if preset == tup[2]:
index = i
break
else:
return
while True: while True:
index = presetList.currentRow()
newName, OK = QtWidgets.QInputDialog.getText( newName, OK = QtWidgets.QInputDialog.getText(
self.window, self.window,
'Preset Manager', 'Preset Manager',
@ -321,3 +335,6 @@ class PresetManager(QtWidgets.QDialog):
parent=self.window parent=self.window
) )
self.settings.setValue("presetDir", os.path.dirname(filename)) self.settings.setValue("presetDir", os.path.dirname(filename))
def clearPresetListSelection(self):
self.window.listWidget_presets.setCurrentRow(-1)

View File

@ -113,7 +113,7 @@ def createFfmpegCommand(inputFile, outputFile, components, duration=-1):
'-t', safeDuration, '-t', safeDuration,
# Tell ffmpeg about shorter clips (seemingly not needed) # Tell ffmpeg about shorter clips (seemingly not needed)
# streamDuration = getAudioDuration(extraInputFile) # streamDuration = getAudioDuration(extraInputFile)
# if streamDuration > float(safeDuration) # if streamDuration and streamDuration > float(safeDuration)
# else "{0:.3f}".format(streamDuration), # else "{0:.3f}".format(streamDuration),
'-i', extraInputFile '-i', extraInputFile
]) ])
@ -228,11 +228,18 @@ def getAudioDuration(filename):
d = d.split(' ')[3] d = d.split(' ')[3]
d = d.split(':') d = d.split(':')
duration = float(d[0])*3600 + float(d[1])*60 + float(d[2]) duration = float(d[0])*3600 + float(d[1])*60 + float(d[2])
break
else:
# String not found in output
return False
return duration return duration
def readAudioFile(filename, parent): def readAudioFile(filename, parent):
duration = getAudioDuration(filename) duration = getAudioDuration(filename)
if not duration:
print('Audio file doesn\'t exist or unreadable.')
return
command = [ command = [
Core.FFMPEG_BIN, Core.FFMPEG_BIN,