''' Preset manager object handles all interactions with presets, including the context menu accessed from MainWindow. ''' from PyQt5 import QtCore, QtWidgets import string import os import logging from toolkit import badName from core import Core from gui.actions import * log = logging.getLogger('AVP.Gui.PresetManager') class PresetManager(QtWidgets.QDialog): def __init__(self, window, parent): super().__init__(parent.window) self.parent = parent self.core = parent.core self.settings = parent.settings self.presetDir = parent.presetDir if not self.settings.value('presetDir'): self.settings.setValue( "presetDir", os.path.join(parent.dataDir, 'projects')) self.findPresets() # window self.lastFilter = '*' self.presetRows = [] # list of (comp, vers, name) tuples self.window = window self.window.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) # connect button signals self.window.pushButton_delete.clicked.connect( self.openDeletePresetDialog ) self.window.pushButton_rename.clicked.connect( self.openRenamePresetDialog ) self.window.pushButton_import.clicked.connect( self.openImportDialog ) self.window.pushButton_export.clicked.connect( self.openExportDialog ) self.window.pushButton_close.clicked.connect( self.window.close ) # create filter box and preset list self.drawFilterList() self.window.comboBox_filter.currentIndexChanged.connect( lambda: self.drawPresetList( self.window.comboBox_filter.currentText(), self.window.lineEdit_search.text() ) ) # make auto-completion for search bar self.autocomplete = QtCore.QStringListModel() completer = QtWidgets.QCompleter() completer.setModel(self.autocomplete) self.window.lineEdit_search.setCompleter(completer) self.window.lineEdit_search.textChanged.connect( lambda: self.drawPresetList( self.window.comboBox_filter.currentText(), self.window.lineEdit_search.text() ) ) self.drawPresetList('*') def show(self): '''Open a new preset manager window from the mainwindow''' self.findPresets() self.drawFilterList() self.drawPresetList('*') self.window.show() def findPresets(self): parseList = [] for dirpath, dirnames, filenames in os.walk(self.presetDir): # anything without a subdirectory must be a preset folder if dirnames: continue for preset in filenames: compName = os.path.basename(os.path.dirname(dirpath)) if compName not in self.core.compNames: continue compVers = os.path.basename(dirpath) try: parseList.append((compName, int(compVers), preset)) except ValueError: continue self.presets = { compName: [ (vers, preset) for name, vers, preset in parseList if name == compName ] for compName, _, __ in parseList } def drawPresetList(self, compFilter=None, presetFilter=''): self.window.listWidget_presets.clear() if compFilter: self.lastFilter = str(compFilter) else: compFilter = str(self.lastFilter) self.presetRows = [] presetNames = [] for component, presets in self.presets.items(): if compFilter != '*' and component != compFilter: continue for vers, preset in presets: if not presetFilter or presetFilter in preset: self.window.listWidget_presets.addItem( '%s: %s' % (component, preset) ) self.presetRows.append((component, vers, preset)) if preset not in presetNames: presetNames.append(preset) self.autocomplete.setStringList(presetNames) def drawFilterList(self): self.window.comboBox_filter.clear() self.window.comboBox_filter.addItem('*') for component in self.presets: self.window.comboBox_filter.addItem(component) def clearPreset(self, compI=None): '''Functions on mainwindow level from the context menu''' compI = self.parent.window.listWidget_componentList.currentRow() action = ClearPreset(self.parent, compI) self.parent.undoStack.push(action) def openSavePresetDialog(self): '''Functions on mainwindow level from the context menu''' window = self.parent.window selectedComponents = self.core.selectedComponents componentList = self.parent.window.listWidget_componentList if componentList.currentRow() == -1: return while True: index = componentList.currentRow() currentPreset = selectedComponents[index].currentPreset newName, OK = QtWidgets.QInputDialog.getText( self.parent.window, 'Audio Visualizer', 'New Preset Name:', QtWidgets.QLineEdit.Normal, currentPreset ) if OK: if badName(newName): self.warnMessage(self.parent.window) continue if newName: if index != -1: selectedComponents[index].currentPreset = newName saveValueStore = \ selectedComponents[index].savePreset() saveValueStore['preset'] = newName componentName = str(selectedComponents[index]).strip() vers = selectedComponents[index].version self.createNewPreset( componentName, vers, newName, saveValueStore, window=self.parent.window) self.findPresets() self.drawPresetList() self.openPreset(newName, index) break def createNewPreset( self, compName, vers, filename, saveValueStore, **kwargs): path = os.path.join(self.presetDir, compName, str(vers), filename) if self.presetExists(path, **kwargs): return self.core.createPresetFile(compName, vers, filename, saveValueStore) def presetExists(self, path, **kwargs): if os.path.exists(path): window = self.window \ if 'window' not in kwargs else kwargs['window'] ch = self.parent.showMessage( msg="%s already exists! Overwrite it?" % os.path.basename(path), showCancel=True, icon='Warning', parent=window) if not ch: # user clicked cancel return True return False def openPreset(self, presetName, compPos=None): componentList = self.parent.window.listWidget_componentList index = compPos if compPos is not None else componentList.currentRow() if index == -1: return action = OpenPreset(self, presetName, index) self.parent.undoStack.push(action) def _openPreset(self, presetName, index): selectedComponents = self.core.selectedComponents componentName = selectedComponents[index].name.strip() version = selectedComponents[index].version dirname = os.path.join(self.presetDir, componentName, str(version)) filepath = os.path.join(dirname, presetName) self.core.openPreset(filepath, index, presetName) self.parent.updateComponentTitle(index) self.parent.drawPreview() def openDeletePresetDialog(self): row = self.getPresetRow() if row == -1: return comp, vers, name = self.presetRows[row] ch = self.parent.showMessage( msg='Really delete %s?' % name, showCancel=True, icon='Warning', parent=self.window ) if not ch: return self.deletePreset(comp, vers, name) def deletePreset(self, comp, vers, name): action = DeletePreset(self, comp, vers, name) self.parent.undoStack.push(action) def warnMessage(self, window=None): self.parent.showMessage( msg='Preset names must contain only letters, ' 'numbers, and spaces.', parent=window if window else self.window) def getPresetRow(self): row = self.window.listWidget_presets.currentRow() if row > -1: return row # check if component selected in MainWindow has preset loaded componentList = self.parent.window.listWidget_componentList compIndex = componentList.currentRow() if compIndex == -1: return compIndex preset = self.core.selectedComponents[compIndex].currentPreset if preset is None: return -1 else: rowTuple = ( self.core.selectedComponents[compIndex].name, self.core.selectedComponents[compIndex].version, preset ) for i, tup in enumerate(self.presetRows): if rowTuple == tup: index = i break else: return -1 return index def openRenamePresetDialog(self): presetList = self.window.listWidget_presets index = self.getPresetRow() if index == -1: return while True: newName, OK = QtWidgets.QInputDialog.getText( self.window, 'Preset Manager', 'Rename Preset:', QtWidgets.QLineEdit.Normal, self.presetRows[index][2] ) if OK: if badName(newName): self.warnMessage() continue if newName: comp, vers, oldName = self.presetRows[index] path = os.path.join( self.presetDir, comp, str(vers)) newPath = os.path.join(path, newName) if self.presetExists(newPath): return action = RenamePreset(self, path, oldName, newName) self.parent.undoStack.push(action) break def renamePreset(self, path, oldName, newName): oldPath = os.path.join(path, oldName) newPath = os.path.join(path, newName) if os.path.exists(newPath): os.remove(newPath) os.rename(oldPath, newPath) self.findPresets() self.drawPresetList() path = os.path.dirname(newPath) for i, comp in enumerate(self.core.selectedComponents): if self.core.getPresetDir(comp) == path \ and comp.currentPreset == oldName: self.core.openPreset(newPath, i, newName) self.parent.updateComponentTitle(i, False) self.parent.drawPreview() def openImportDialog(self): filename, _ = QtWidgets.QFileDialog.getOpenFileName( self.window, "Import Preset File", self.settings.value("presetDir"), "Preset Files (*.avl)") if filename: # get installed path & ask user to overwrite if needed path = '' while True: if path: if self.presetExists(path): break else: if os.path.exists(path): os.remove(path) success, path = self.core.importPreset(filename) if success: break self.findPresets() self.drawPresetList() self.settings.setValue("presetDir", os.path.dirname(filename)) def openExportDialog(self): index = self.getPresetRow() if index == -1: return filename, _ = QtWidgets.QFileDialog.getSaveFileName( self.window, "Export Preset", self.settings.value("presetDir"), "Preset Files (*.avl)") if filename: comp, vers, name = self.presetRows[index] if not self.core.exportPreset(filename, comp, vers, name): self.parent.showMessage( msg='Couldn\'t export %s.' % filename, parent=self.window ) self.settings.setValue("presetDir", os.path.dirname(filename)) def clearPresetListSelection(self): self.window.listWidget_presets.setCurrentRow(-1)