diff --git a/qtgui/instrument.py b/qtgui/instrument.py index f03b1ee..bc51261 100644 --- a/qtgui/instrument.py +++ b/qtgui/instrument.py @@ -57,7 +57,6 @@ class InstrumentTreeController(object): #self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict #self.guiLibraries = {} # idKey : GuiLibrary #self.guiInstruments = {} # idKey : GuiInstrument - self.currentlyNested = None #is the view nested in libraries or just all instruments? self.headerLabels = [ @@ -93,7 +92,13 @@ class InstrumentTreeController(object): #self.treeWidget.setStyleSheet("QTreeWidget::item { border-bottom: 1px solid black;}") #sadly for all items. We want a line below top level items. - if not "libraryIsExpanded" in api.session.guiSharedDataToSave: + if "libraryIsExpanded" in api.session.guiSharedDataToSave: + #This is loaded from JSON, where keys MUST be strings. Our ids got converted into strings on save so we have to convert back to int here. + correctTypesDict = {} + for key, value in api.session.guiSharedDataToSave["libraryIsExpanded"].items(): + correctTypesDict[int(key)] = value + api.session.guiSharedDataToSave["libraryIsExpanded"] = correctTypesDict + else: api.session.guiSharedDataToSave["libraryIsExpanded"] = {} # libId : bool if expanded or not. Also used when switching from nested to flat and back. #Default values are used in self.buildTree @@ -108,7 +113,6 @@ class InstrumentTreeController(object): def itemExpandedOrCollapsed(self, libraryItem:QtWidgets.QTreeWidgetItem): - #print (libraryItem.name, libraryItem.isExpanded()) api.session.guiSharedDataToSave["libraryIsExpanded"][libraryItem.id] = libraryItem.isExpanded() def itemSelectionChanged(self): @@ -131,9 +135,13 @@ class InstrumentTreeController(object): if type(item) is GuiInstrument: api.auditionerInstrument(item.idKey) + def isNested(self)->bool: + return self.parentMainWindow.ui.actionFlatNested.isChecked() + def isShowOnlyLoadedInstruments(self)->bool: + return self.parentMainWindow.ui.actionShowOnlyLoadedInstruments.isChecked() - def buildTree(self, data:dict, nested:bool=True): + def buildTree(self, data:dict): """ Create the tree. Can be called multiple times and it will re-create itself destructively. If you call it with data, once at program start, data will get cached. If you call @@ -199,7 +207,6 @@ class InstrumentTreeController(object): self.treeWidget.clear() #will delete the C++ objects. We need to delete the PyQt objects ourselves, like so: self.guiLibraries = {} # idKey : GuiLibrary self.guiInstruments = {} # idKey : GuiInstrument - self.currentlyNested = nested if data: self._cachedData = data @@ -207,7 +214,9 @@ class InstrumentTreeController(object): assert self._cachedData data = self._cachedData - self.currentlyNested = nested + + nested = self.isNested() + showOnlyLoadedInstruments = self.isShowOnlyLoadedInstruments() for libraryId, libraryDict in data.items(): @@ -219,30 +228,49 @@ class InstrumentTreeController(object): parentLibraryWidget.setExpanded(api.session.guiSharedDataToSave["libraryIsExpanded"][libraryId]) #only possible after gi.init() was done and item inserted. #parentLibraryWidget.setHidden(True) #only possible after insert + atLeastOneVisible = False for instrumentdId, instrumentDict in libraryDict.items(): if instrumentdId == "library": #Top level item was already created. Ignore here. - pass + continue + + if showOnlyLoadedInstruments and instrumentDict["idKey"] in self._cachedLastInstrumentStatus and not self._cachedLastInstrumentStatus[instrumentDict["idKey"]]["state"]: #don't create if we only want to see enabled instrument + instrumentVisible = False + elif showOnlyLoadedInstruments and not instrumentDict["status"]["state"]: + instrumentVisible = False + else: + instrumentVisible = True + atLeastOneVisible = True + + gi = GuiInstrument(parentTreeController=self, instrumentDict=instrumentDict) + if nested: + parentLibraryWidget.addChild(gi) else: - gi = GuiInstrument(parentTreeController=self, instrumentDict=instrumentDict) - if nested: - parentLibraryWidget.addChild(gi) - else: - self.treeWidget.addTopLevelItem(gi) - gi.injectWidgets() #only possible after gi.init() was done and item inserted. - self.guiInstruments[instrumentDict["idKey"]] = gi - if instrumentDict["idKey"] in self._cachedLastInstrumentStatus: - gi.updateStatus(self._cachedLastInstrumentStatus[instrumentDict["idKey"]]) + self.treeWidget.addTopLevelItem(gi) + gi.injectWidgets() #only possible after gi.init() was done and item inserted. + self.guiInstruments[instrumentDict["idKey"]] = gi + if instrumentDict["idKey"] in self._cachedLastInstrumentStatus: + gi.updateStatus(self._cachedLastInstrumentStatus[instrumentDict["idKey"]]) + gi.setHidden(not instrumentVisible) + + #If no instruments were added maybe the lib needs hiding because it is empty + if showOnlyLoadedInstruments and nested and not atLeastOneVisible: + parentLibraryWidget.setHidden(True) + self._adjustColumnSize() + def showOnlyLoadedInstruments(self, state:bool): + """The logic is backwards. We receive state=True if instruments should be hidden""" + self.buildTree(data=None) #uses the menu state for everything except data + def setAllExpanded(self, state:bool): """We do not use the qt function collapseAll and expandAll because they do not trigger the signal""" - if self.currentlyNested: + if self.isNested(): for libid, guiLib in self.guiLibraries.items(): - guiLib.setExpanded(state) #triggers signal which will trigger self.toggleNestedFlat + guiLib.setExpanded(state) def _adjustColumnSize(self): self.treeWidget.sortItems(self.sortByColumnValue, self.sortDescendingValue) @@ -548,7 +576,7 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem): libId, instrId = instrumentDict[key] zeros = int(instrumentDict["instrumentsInLibraryCount"]/10)+1 instIdZFilled = str(instrId).zfill(zeros) - if self.parentTreeController.currentlyNested: + if self.parentTreeController.isNested(): self.setText(index, instIdZFilled) else: #full id #self.setText(index, f"{libId}-{str(instrId).zfill(zeros)}") diff --git a/qtgui/mainwindow.py b/qtgui/mainwindow.py index 751b1d3..58c16ec 100644 --- a/qtgui/mainwindow.py +++ b/qtgui/mainwindow.py @@ -145,14 +145,15 @@ class MainWindow(TemplateMainWindow): self.pianoRollToggleVisibleAndRemember(settings.value("pianoRollVisible", type=bool)) if settings.contains("pianoVisible"): self.pianoToggleVisibleAndRemember(settings.value("pianoVisible", type=bool)) + if settings.contains("instrumentTreeIsNested"): + self.ui.actionFlatNested.setChecked(settings.value("instrumentTreeIsNested", type=bool)) + if settings.contains("showOnlyLoadedInstruments"): + self.ui.actionShowOnlyLoadedInstruments.setChecked(settings.value("showOnlyLoadedInstruments", type=bool)) self.start(additionalData) #This shows the GUI, or not, depends on the NSM gui save setting. We need to call that after the menu, otherwise the about dialog will block and then we get new menu entries, which looks strange. api.callbacks.rescanSampleDir.append(self.react_rescanSampleDir) #This only happens on actual, manually instructed rescanning through the api. We instruct this through our Rescan-Dialog. - if settings.contains("instrumentTreeIsNested"): #Only call after the instruments got parsed - self.toggleNestedFlatAndRemember(settings.value("instrumentTreeIsNested", type=bool)) - #Statusbar will show possible actions, such as "use scrollwheel to transpose" #self.statusBar().showMessage(QtCore.QCoreApplication.translate("Statusbar", "")) self.statusBar().showMessage("") @@ -174,6 +175,7 @@ class MainWindow(TemplateMainWindow): self.menu.addMenuEntry("menuView", "actionExpandAll", QtCore.QCoreApplication.translate("Menu", "Expand all Libraries"), lambda: self.instrumentTreeController.setAllExpanded(True)) self.menu.addMenuEntry("menuView", "actionCollapseAll", QtCore.QCoreApplication.translate("Menu", "Collapse all Libraries"), lambda: self.instrumentTreeController.setAllExpanded(False)) + self.menu.addMenuEntry("menuView", "actionShowOnlyLoadedInstruments", QtCore.QCoreApplication.translate("Menu", "Show only loaded instruments"), self.toggleShowOnlyLoadedInstrumentsAndRemember, checkable=True, startChecked=False) #function receives check state as automatic parameter self.menu.addMenuEntry("menuView", "actionFlatNested", QtCore.QCoreApplication.translate("Menu", "Nested Instrument List"), self.toggleNestedFlatAndRemember, checkable=True, startChecked=True) #function receives check state as automatic parameter self.menu.addMenuEntry("menuView", "actionPianoRollVisible", QtCore.QCoreApplication.translate("Menu", "Piano Roll"), self.pianoRollToggleVisibleAndRemember, shortcut="Ctrl+R", checkable=True, startChecked=True) #function receives check state as automatic parameter self.menu.addMenuEntry("menuView", "actionPianoVisible", QtCore.QCoreApplication.translate("Menu", "Piano"), self.pianoToggleVisibleAndRemember, shortcut="Ctrl+P", checkable=True, startChecked=True) #function receives check state as automatic parameter @@ -189,28 +191,41 @@ class MainWindow(TemplateMainWindow): The program start happens without that and just sends data into a prepared but empty GUI.""" self.instrumentTreeController.reset() + def toggleShowOnlyLoadedInstrumentsAndRemember(self, state:bool): + """ + This function can only be called after the instrument tree has been build. + """ + self.ui.actionShowOnlyLoadedInstruments.setChecked(state) #if called from outside the menu, e.g. load + self.instrumentTreeController.buildTree(data=None) #with data=None it will used the cache data we received once, at startup. All other values use the menu check marks + settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) + settings.setValue("showOnlyLoadedInstruments", state) + + def toggleNestedFlatAndRemember(self, state:bool): """We receive state as automatic parameter from Qt from the calling menu action. This function can only be called after the instrument tree has been build. instrumentTreeController.buildTree(data=None) relies on cached data. """ - self.instrumentTreeController.buildTree(data=None, nested=state) #with data=None it will used the cache data we received once, at startup + self.ui.actionFlatNested.setChecked(state) #if called from outside the menu, e.g. load + self.instrumentTreeController.buildTree(data=None) #with data=None it will used the cache data we received once, at startup. All other values use the menu check marks settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) settings.setValue("instrumentTreeIsNested", state) - self.ui.actionFlatNested.setChecked(state) #if called from outside the menu, e.g. load + def pianoRollToggleVisibleAndRemember(self, state:bool): + self.ui.actionPianoRollVisible.setChecked(state) #if called from outside the menu, e.g. load self.ui.verticalPianoFrame.setVisible(state) settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) settings.setValue("pianoRollVisible", state) - self.ui.actionPianoRollVisible.setChecked(state) #if called from outside the menu, e.g. load + def pianoToggleVisibleAndRemember(self, state:bool): + self.ui.actionPianoVisible.setChecked(state) #if called from outside the menu, e.g. load self.ui.horizontalPianoFrame.setVisible(state) settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) settings.setValue("pianoVisible", state) - self.ui.actionPianoVisible.setChecked(state) #if called from outside the menu, e.g. load + def selectedInstrumentChanged(self, instrumentStatus, instrumentData): """We receive this from selectedinstrumentcontroller.py, when the user clicks on a GUI