From 307d499f9ae2729c790fe9258d88aca72331cdf6 Mon Sep 17 00:00:00 2001 From: tassaron Date: Mon, 12 Jun 2017 22:34:37 -0400 Subject: [PATCH] adding an asterisk to modified, unsaved presets flags for unsaved changes saved in project files --- components/__base__.py | 38 ++++++++++++------ components/color.py | 7 +++- components/image.py | 6 ++- components/original.py | 10 +++-- components/text.py | 11 ++++-- components/video.py | 6 ++- core.py | 87 +++++++++++++++++++++++++++++++++++++++--- mainwindow.py | 26 ++++++++----- presetmanager.py | 32 +++++++++------- 9 files changed, 175 insertions(+), 48 deletions(-) diff --git a/components/__base__.py b/components/__base__.py index 3ccb5dc..b32c120 100644 --- a/components/__base__.py +++ b/components/__base__.py @@ -1,9 +1,15 @@ -from PyQt4 import QtGui +from PyQt4 import QtGui, QtCore +class Component(QtCore.QObject): + '''A base class for components to inherit from''' -class Component: - def __init__(self): + # modified = QtCore.pyqtSignal(int, bool) + + def __init__(self, moduleIndex, compPos): + super().__init__() self.currentPreset = None + self.moduleIndex = moduleIndex + self.compPos = compPos def __str__(self): return self.__doc__ @@ -13,12 +19,27 @@ class Component: return 1 def cancel(self): - # make sure your component responds to these variables in frameRender() + # please stop any lengthy process in response to this variable self.canceled = True def reset(self): self.canceled = False + def update(self): + self.modified.emit(self.compPos, True) + # use super().update() then read your widget values here + + 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 + self.currentPreset = presetName \ + if presetName != None else presetDict['preset'] + + def savePreset(self): + return {} + def preFrameRender(self, **kwargs): for var, value in kwargs.items(): exec('self.%s = value' % var) @@ -62,7 +83,7 @@ class Component: return page def update(self): - # read widget values + super().update() self.parent.drawPreview() def previewRender(self, previewWorker): @@ -77,13 +98,6 @@ class Component: image = Image.new("RGBA", (width, height), (0,0,0,0)) return image - def loadPreset(self, presetDict, presetName=None): - self.currentPreset = presetName - # update widgets using a preset dict - - def savePreset(self): - return {} - def cancel(self): self.canceled = True diff --git a/components/color.py b/components/color.py index 11c1b19..b6105c8 100644 --- a/components/color.py +++ b/components/color.py @@ -7,6 +7,9 @@ from . import __base__ class Component(__base__.Component): '''Color''' + + modified = QtCore.pyqtSignal(int, bool) + def widget(self, parent): self.parent = parent page = uic.loadUi(os.path.join( @@ -45,6 +48,7 @@ class Component(__base__.Component): return page def update(self): + super().update() self.color1 = self.RGBFromString(self.page.lineEdit_color1.text()) self.color2 = self.RGBFromString(self.page.lineEdit_color2.text()) self.x = self.page.spinBox_x.value() @@ -70,7 +74,8 @@ class Component(__base__.Component): return Image.new("RGBA", (width, height), (r, g, b, 255)) def loadPreset(self, pr, presetName=None): - self.currentPreset = presetName if presetName else pr['preset'] + super().loadPreset(pr, presetName) + self.page.lineEdit_color1.setText('%s,%s,%s' % pr['color1']) self.page.lineEdit_color2.setText('%s,%s,%s' % pr['color2']) diff --git a/components/image.py b/components/image.py index 441e0e1..ed5f243 100644 --- a/components/image.py +++ b/components/image.py @@ -6,6 +6,9 @@ from . import __base__ class Component(__base__.Component): '''Image''' + + modified = QtCore.pyqtSignal(int, bool) + def widget(self, parent): self.parent = parent self.settings = parent.settings @@ -22,6 +25,7 @@ class Component(__base__.Component): return page def update(self): + super().update() self.imagePath = self.page.lineEdit_image.text() self.parent.drawPreview() @@ -49,7 +53,7 @@ class Component(__base__.Component): return frame def loadPreset(self, pr, presetName=None): - self.currentPreset = presetName if presetName else pr['preset'] + super().loadPreset(pr, presetName) self.page.lineEdit_image.setText(pr['image']) def savePreset(self): diff --git a/components/original.py b/components/original.py index 7873f43..f3f578d 100644 --- a/components/original.py +++ b/components/original.py @@ -1,9 +1,8 @@ import numpy from PIL import Image, ImageDraw -from PyQt4 import uic, QtGui +from PyQt4 import uic, QtGui, QtCore from PyQt4.QtGui import QColor import os -import random from . import __base__ import time from copy import copy @@ -11,6 +10,9 @@ from copy import copy class Component(__base__.Component): '''Original Audio Visualization''' + + modified = QtCore.pyqtSignal(int, bool) + def widget(self, parent): self.parent = parent self.visColor = (255, 255, 255) @@ -33,12 +35,14 @@ class Component(__base__.Component): return page def update(self): + super().update() self.layout = self.page.comboBox_visLayout.currentIndex() self.visColor = self.RGBFromString(self.page.lineEdit_visColor.text()) self.parent.drawPreview() def loadPreset(self, pr, presetName=None): - self.currentPreset = presetName if presetName else pr['preset'] + super().loadPreset(pr, presetName) + self.page.lineEdit_visColor.setText('%s,%s,%s' % pr['visColor']) btnStyle = "QPushButton { background-color : %s; outline: none; }" \ % QColor(*pr['visColor']).name() diff --git a/components/text.py b/components/text.py index e8fb3d5..a40e2a9 100644 --- a/components/text.py +++ b/components/text.py @@ -9,8 +9,11 @@ from . import __base__ class Component(__base__.Component): '''Title Text''' - def __init__(self): - super().__init__() + + modified = QtCore.pyqtSignal(int, bool) + + def __init__(self, *args): + super().__init__(*args) self.titleFont = QFont() def widget(self, parent): @@ -53,6 +56,7 @@ class Component(__base__.Component): return page def update(self): + super().update() self.title = self.page.lineEdit_title.text() self.alignment = self.page.comboBox_textAlign.currentIndex() self.titleFont = self.page.fontComboBox_titleFont.currentFont() @@ -79,7 +83,8 @@ class Component(__base__.Component): return x, self.yPosition def loadPreset(self, pr, presetName=None): - self.currentPreset = presetName if presetName else pr['preset'] + super().loadPreset(pr, presetName) + self.page.lineEdit_title.setText(pr['title']) font = QFont() font.fromString(pr['titleFont']) diff --git a/components/video.py b/components/video.py index bd1bf96..ff06329 100644 --- a/components/video.py +++ b/components/video.py @@ -86,6 +86,9 @@ class Video: class Component(__base__.Component): '''Video''' + + modified = QtCore.pyqtSignal(int, bool) + def widget(self, parent): self.parent = parent self.settings = parent.settings @@ -106,6 +109,7 @@ class Component(__base__.Component): return page def update(self): + super().update() self.videoPath = self.page.lineEdit_video.text() self.loopVideo = self.page.checkBox_loop.isChecked() self.parent.drawPreview() @@ -136,7 +140,7 @@ class Component(__base__.Component): return self.video.frame(frameNo) def loadPreset(self, pr, presetName=None): - self.currentPreset = presetName if presetName else pr['preset'] + super().loadPreset(pr, presetName) self.page.lineEdit_video.setText(pr['video']) self.page.checkBox_loop.setChecked(pr['loop']) diff --git a/core.py b/core.py index c47068c..5617c81 100644 --- a/core.py +++ b/core.py @@ -34,6 +34,7 @@ class Core(): self.findComponents() self.selectedComponents = [] + self.modifiedComponents = {} def findComponents(self): def findComponents(): @@ -50,24 +51,86 @@ class Core(): for name in findComponents()] self.moduleIndexes = [i for i in range(len(self.modules))] + def componentListChanged(self): + for i, component in enumerate(self.selectedComponents): + component.compPos = i + # print('Modified Components: ', self.modifiedComponents) + def insertComponent(self, compPos, moduleIndex): if compPos < 0: compPos = len(self.selectedComponents) -1 + + component = self.modules[moduleIndex].Component( + moduleIndex, compPos) self.selectedComponents.insert( compPos, - self.modules[moduleIndex].Component() - ) + component) + + newDict = {} + for i, val in self.modifiedComponents.items(): + if i >= compPos: + newDict[i+1] = bool(val) + else: + newDict[i] = bool(val) + self.modifiedComponents.clear() + self.modifiedComponents = newDict + + self.componentListChanged() return compPos def moveComponent(self, startI, endI): + def insert(target, comp): + self.selectedComponents.insert(target, comp) + comp = self.selectedComponents.pop(startI) - self.selectedComponents.insert(endI, comp) + insert(endI, comp) + + try: + oldModified = self.modifiedComponents.pop(startI) + if endI in self.modifiedComponents: + self.modifiedComponents[startI] = \ + self.modifiedComponents.pop(endI) + self.modifiedComponents[endI] = oldModified + except KeyError: + pass + + self.componentListChanged() return endI + def removeComponent(self, i): + self.selectedComponents.pop(i) + try: + self.modifiedComponents.pop(i) + except KeyError: + pass + + newDict = {} + for index, val in self.modifiedComponents.items(): + if index >= i: + newDict[index-1] = bool(val) + else: + newDict[index] = bool(val) + self.modifiedComponents.clear() + self.modifiedComponents = newDict + + self.componentListChanged() + def updateComponent(self, i): # print('updating %s' % self.selectedComponents[i]) self.selectedComponents[i].update() + def componentModified(self, i): + '''Triggered by mainwindow.updateComponentTitle() + Tracks temporary state of whether components are modified or not + for retrieval upon loading a project file''' + self.modifiedComponents[i] = True + + def componentUnmodified(self, i): + try: + self.modifiedComponents.pop(i) + except KeyError: + pass + def moduleIndexFor(self, compName): compNames = [mod.Component.__doc__ for mod in self.modules] index = compNames.index(compName) @@ -78,11 +141,17 @@ class Core(): which implements an insertComponent method''' errcode, data = self.parseAvFile(filepath) if errcode == 0: - for name, vers, preset in data['Components']: + for i, tup in enumerate(data['Components']): + name, vers, preset = tup loader.insertComponent( self.moduleIndexFor(name), -1) self.selectedComponents[-1].loadPreset( preset) + if data['Modified'][i] == True: + self.componentModified(i) + loader.updateComponentTitle(i, True) + else: + loader.updateComponentTitle(i) elif errcode == 1: typ, value, _ = data if typ.__name__ == KeyError: @@ -105,7 +174,7 @@ class Core(): with open(filepath, 'r') as f: def parseLine(line): '''Decides if a given avp or avl line is a section header''' - validSections = ('Components') + validSections = ('Components', 'Modified') line = line.strip() newSection = '' @@ -138,6 +207,8 @@ class Core(): lastCompPreset) ) i = 0 + if line and section == 'Modified': + data[section].append(eval(line)) return 0, data except: return 1, sys.exc_info() @@ -223,6 +294,12 @@ class Core(): f.write('%s\n' % str(comp)) f.write('%s\n' % str(comp.version())) f.write('%s\n' % Core.presetToString(saveValueStore)) + f.write('[Modified]\n') + for i in range(len(self.selectedComponents)): + if i in self.modifiedComponents: + f.write('%s\n' % repr(True)) + else: + f.write('%s\n' % repr(False)) return True except: return False diff --git a/mainwindow.py b/mainwindow.py index 42105d1..2a04a4a 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -235,13 +235,21 @@ class MainWindow(QtCore.QObject): self.previewThread.wait() self.autosave() - def updateComponentTitle(self, pos): + @QtCore.pyqtSlot(int, bool) + def updateComponentTitle(self, pos, modified=False): + #print(pos, modified) if pos < 0: pos = len(self.core.selectedComponents)-1 title = str(self.core.selectedComponents[pos]) if self.core.selectedComponents[pos].currentPreset: title += ' - %s' % self.core.selectedComponents[pos].currentPreset + if modified: + title += '*' self.window.listWidget_componentList.item(pos).setText(title) + if modified: + self.core.componentModified(pos) + else: + self.core.componentUnmodified(pos) def updateCodecs(self): containerWidget = self.window.comboBox_videoContainer @@ -344,9 +352,6 @@ class MainWindow(QtCore.QObject): self.showMessage( msg="You must select an audio file and output filename.") - def progressBarUpdated(self, value): - self.window.progressBar_createVideo.setValue(value) - def changeEncodingStatus(self, status): if status: self.window.pushButton_createVideo.setEnabled(False) @@ -385,6 +390,9 @@ class MainWindow(QtCore.QObject): self.window.pushButton_presets.setEnabled(True) self.window.listWidget_componentList.setEnabled(True) + def progressBarUpdated(self, value): + self.window.progressBar_createVideo.setValue(value) + def progressBarSetText(self, value): self.window.progressBar_createVideo.setFormat(value) @@ -420,6 +428,10 @@ class MainWindow(QtCore.QObject): self.core.selectedComponents[index].__doc__) componentList.setCurrentRow(index) + # connect to signal that adds an asterisk when modified + self.core.selectedComponents[index].modified.connect( + self.updateComponentTitle) + self.pages.insert(index, self.core.selectedComponents[index].widget(self)) stackedWidget.insertWidget(index, self.pages[index]) stackedWidget.setCurrentIndex(index) @@ -433,7 +445,7 @@ class MainWindow(QtCore.QObject): index = componentList.row(selected) self.window.stackedWidget.removeWidget(self.pages[index]) componentList.takeItem(index) - self.core.selectedComponents.pop(index) + self.core.removeComponent(index) self.pages.pop(index) self.changeComponentWidget() self.drawPreview() @@ -533,10 +545,6 @@ class MainWindow(QtCore.QObject): # actually load the project using core method self.core.openProject(self, filepath) - for i in range(self.window.listWidget_componentList.count()): - # update listwidget titles to indicate loaded presets - self.updateComponentTitle(i) - def showMessage(self, **kwargs): parent = kwargs['parent'] if 'parent' in kwargs else self.window msg = QtGui.QMessageBox(parent) diff --git a/presetmanager.py b/presetmanager.py index 91dc373..4300ce1 100644 --- a/presetmanager.py +++ b/presetmanager.py @@ -105,14 +105,14 @@ class PresetManager(QtGui.QDialog): def openSavePresetDialog(self): '''Functions on mainwindow level from the context menu''' window = self.parent.window - self.selectedComponents = self.parent.core.selectedComponents + selectedComponents = self.parent.core.selectedComponents componentList = self.parent.window.listWidget_componentList if componentList.currentRow() == -1: return while True: index = componentList.currentRow() - currentPreset = self.selectedComponents[index].currentPreset + currentPreset = selectedComponents[index].currentPreset newName, OK = QtGui.QInputDialog.getText( self.parent.window, 'Audio Visualizer', @@ -127,30 +127,35 @@ class PresetManager(QtGui.QDialog): if newName: if index != -1: saveValueStore = \ - self.selectedComponents[index].savePreset() - componentName = str(self.selectedComponents[index]).strip() - vers = self.selectedComponents[index].version() + selectedComponents[index].savePreset() + componentName = str(selectedComponents[index]).strip() + vers = selectedComponents[index].version() self.createNewPreset( - componentName, vers, newName, saveValueStore) - self.selectedComponents[index].currentPreset = newName - self.findPresets() - self.drawPresetList() + componentName, vers, newName, + saveValueStore, window=self.parent.window) + selectedComponents[index].currentPreset = newName + #self.findPresets() + #self.drawPresetList() + self.parent.updateComponentTitle(index) break - def createNewPreset(self, compName, vers, filename, saveValueStore): + def createNewPreset( + self, compName, vers, filename, saveValueStore, **kwargs): path = os.path.join(self.presetDir, compName, str(vers), filename) - if self.presetExists(path): + if self.presetExists(path, **kwargs): return self.core.createPresetFile(compName, vers, filename, saveValueStore) - def presetExists(self, path): + 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=QtGui.QMessageBox.Warning, - parent=self.window) + parent=window) if not ch: # user clicked cancel return True @@ -204,6 +209,7 @@ class PresetManager(QtGui.QDialog): os.remove(filepath) def warnMessage(self, window=None): + print(window) self.parent.showMessage( msg='Preset names must contain only letters, ' 'numbers, and spaces.',