diff --git a/engine/config.py b/engine/config.py index fb95d42..3af69a2 100644 --- a/engine/config.py +++ b/engine/config.py @@ -58,11 +58,8 @@ numbering system, midi controls etc. Only "soft" settings, such as filters, can be changed dynamically and will be saved in your project. -At the moment this software is in its ALPHA phase. You need to manually download instrument -files from https://laborejo.org/downloads/tembro-instruments/ . Do not unpack the .tar files! -Download them to a location of your choice set the sample file location from within Tembros Edit -menu. Then restart the program. -""", +At the moment this software is in its ALPHA phase which means that samples libraries will still +change.""", "dependencies" : "\n".join("* "+dep for dep in ()), } diff --git a/qtgui/chooseDownloadDirectory.py b/qtgui/chooseDownloadDirectory.py index 723c717..7734e26 100644 --- a/qtgui/chooseDownloadDirectory.py +++ b/qtgui/chooseDownloadDirectory.py @@ -222,8 +222,9 @@ class ChooseDownloadDirectory(QtWidgets.QDialog): self.parentMainWindow.qtApp.processEvents() + indexUrl = "https://www.laborejo.org/downloads/tembro-instruments/downloadindex.json" - indexUrl = "http://0.0.0.0:8000/downloadindex.json" + #indexUrl = "https://download.linuxaudio.org/musical-instrument-libraries/tembro/" indexDL = SmartDL(indexUrl, progress_bar=False) # Because we didn't pass a destination path to the constructor, temporary path was chosen. try: @@ -280,7 +281,6 @@ class ChooseDownloadDirectory(QtWidgets.QDialog): logger.info(f"Downloading {entry['name']}") - obj = SmartDL(urlMirrorList, self.ui.pathComboBox.currentText(), progress_bar=False) self.currentSmartDL = obj #With Hash Verification it will not only test the download but also don't double-download an existing file. diff --git a/qtgui/designer/mainwindow.py b/qtgui/designer/mainwindow.py index 4c7593d..0ff6c04 100644 --- a/qtgui/designer/mainwindow.py +++ b/qtgui/designer/mainwindow.py @@ -35,6 +35,36 @@ class Ui_MainWindow(object): self.verticalLayout = QtWidgets.QVBoxLayout(self.rightFrame) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") + self.auditionerWidget = QtWidgets.QWidget(self.rightFrame) + self.auditionerWidget.setObjectName("auditionerWidget") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.auditionerWidget) + self.horizontalLayout.setContentsMargins(9, 9, -1, 9) + self.horizontalLayout.setObjectName("horizontalLayout") + self.auditionerVolumeDial = QtWidgets.QDial(self.auditionerWidget) + self.auditionerVolumeDial.setMaximumSize(QtCore.QSize(32, 32)) + self.auditionerVolumeDial.setSizeIncrement(QtCore.QSize(3, 0)) + self.auditionerVolumeDial.setMinimum(-40) + self.auditionerVolumeDial.setMaximum(0) + self.auditionerVolumeDial.setPageStep(3) + self.auditionerVolumeDial.setProperty("value", -3) + self.auditionerVolumeDial.setWrapping(False) + self.auditionerVolumeDial.setNotchTarget(3.0) + self.auditionerVolumeDial.setNotchesVisible(True) + self.auditionerVolumeDial.setObjectName("auditionerVolumeDial") + self.horizontalLayout.addWidget(self.auditionerVolumeDial) + self.label = QtWidgets.QLabel(self.auditionerWidget) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.auditionerMidiInputComboBox = QtWidgets.QComboBox(self.auditionerWidget) + self.auditionerMidiInputComboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents) + self.auditionerMidiInputComboBox.setObjectName("auditionerMidiInputComboBox") + self.horizontalLayout.addWidget(self.auditionerMidiInputComboBox) + self.auditionerCurrentInstrument_label = QtWidgets.QLabel(self.auditionerWidget) + self.auditionerCurrentInstrument_label.setObjectName("auditionerCurrentInstrument_label") + self.horizontalLayout.addWidget(self.auditionerCurrentInstrument_label) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.verticalLayout.addWidget(self.auditionerWidget) self.splitter = QtWidgets.QSplitter(self.rightFrame) self.splitter.setOrientation(QtCore.Qt.Vertical) self.splitter.setObjectName("splitter") @@ -77,7 +107,7 @@ class Ui_MainWindow(object): self.details_scrollArea.setWidgetResizable(True) self.details_scrollArea.setObjectName("details_scrollArea") self.scrollAreaWidgetContents = QtWidgets.QWidget() - self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 993, 106)) + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 993, 125)) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.formLayout = QtWidgets.QFormLayout(self.scrollAreaWidgetContents) self.formLayout.setObjectName("formLayout") @@ -101,35 +131,6 @@ class Ui_MainWindow(object): self.details_scrollArea.setWidget(self.scrollAreaWidgetContents) self.verticalLayout_3.addWidget(self.details_scrollArea) self.verticalLayout.addWidget(self.splitter) - self.auditionerWidget = QtWidgets.QWidget(self.rightFrame) - self.auditionerWidget.setObjectName("auditionerWidget") - self.horizontalLayout = QtWidgets.QHBoxLayout(self.auditionerWidget) - self.horizontalLayout.setObjectName("horizontalLayout") - self.auditionerVolumeDial = QtWidgets.QDial(self.auditionerWidget) - self.auditionerVolumeDial.setMaximumSize(QtCore.QSize(32, 32)) - self.auditionerVolumeDial.setSizeIncrement(QtCore.QSize(3, 0)) - self.auditionerVolumeDial.setMinimum(-40) - self.auditionerVolumeDial.setMaximum(0) - self.auditionerVolumeDial.setPageStep(3) - self.auditionerVolumeDial.setProperty("value", -3) - self.auditionerVolumeDial.setWrapping(False) - self.auditionerVolumeDial.setNotchTarget(3.0) - self.auditionerVolumeDial.setNotchesVisible(True) - self.auditionerVolumeDial.setObjectName("auditionerVolumeDial") - self.horizontalLayout.addWidget(self.auditionerVolumeDial) - self.label = QtWidgets.QLabel(self.auditionerWidget) - self.label.setObjectName("label") - self.horizontalLayout.addWidget(self.label) - self.auditionerMidiInputComboBox = QtWidgets.QComboBox(self.auditionerWidget) - self.auditionerMidiInputComboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents) - self.auditionerMidiInputComboBox.setObjectName("auditionerMidiInputComboBox") - self.horizontalLayout.addWidget(self.auditionerMidiInputComboBox) - self.auditionerCurrentInstrument_label = QtWidgets.QLabel(self.auditionerWidget) - self.auditionerCurrentInstrument_label.setObjectName("auditionerCurrentInstrument_label") - self.horizontalLayout.addWidget(self.auditionerCurrentInstrument_label) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.verticalLayout.addWidget(self.auditionerWidget) self.horizontalPianoFrame = QtWidgets.QFrame(self.rightFrame) self.horizontalPianoFrame.setMinimumSize(QtCore.QSize(0, 100)) self.horizontalPianoFrame.setFrameShape(QtWidgets.QFrame.StyledPanel) @@ -155,6 +156,8 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) + self.label.setText(_translate("MainWindow", "Auditioner MIDI Input")) + self.auditionerCurrentInstrument_label.setText(_translate("MainWindow", "TextLabel")) self.instruments_treeWidget.headerItem().setText(1, _translate("MainWindow", "ID")) self.instruments_treeWidget.headerItem().setText(2, _translate("MainWindow", "Volume")) self.instruments_treeWidget.headerItem().setText(3, _translate("MainWindow", "Name")) @@ -171,5 +174,3 @@ class Ui_MainWindow(object): self.variant_label.setText(_translate("MainWindow", "Variants")) self.info_label.setText(_translate("MainWindow", "TextLabel")) self.keySwitch_label.setText(_translate("MainWindow", "KeySwitch")) - self.label.setText(_translate("MainWindow", "Auditioner MIDI Input")) - self.auditionerCurrentInstrument_label.setText(_translate("MainWindow", "TextLabel")) diff --git a/qtgui/designer/mainwindow.ui b/qtgui/designer/mainwindow.ui index facc329..0b84ea8 100644 --- a/qtgui/designer/mainwindow.ui +++ b/qtgui/designer/mainwindow.ui @@ -69,6 +69,92 @@ 0 + + + + + 9 + + + 9 + + + 9 + + + + + + 32 + 32 + + + + + 3 + 0 + + + + -40 + + + 0 + + + 3 + + + -3 + + + false + + + 3.000000000000000 + + + true + + + + + + + Auditioner MIDI Input + + + + + + + QComboBox::AdjustToContents + + + + + + + TextLabel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + @@ -243,7 +329,7 @@ 0 0 993 - 106 + 125 @@ -288,83 +374,6 @@ - - - - - - - - 32 - 32 - - - - - 3 - 0 - - - - -40 - - - 0 - - - 3 - - - -3 - - - false - - - 3.000000000000000 - - - true - - - - - - - Auditioner MIDI Input - - - - - - - QComboBox::AdjustToContents - - - - - - - TextLabel - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - diff --git a/qtgui/favorites.py b/qtgui/favorites.py index 5faa921..c07bfe5 100644 --- a/qtgui/favorites.py +++ b/qtgui/favorites.py @@ -22,26 +22,81 @@ along with this program. If not, see . import logging; logger = logging.getLogger(__name__); logger.info("import") #Standard Library +from collections import Counter #Third Party from PyQt5 import QtCore, QtGui, QtWidgets #Our Qt -from .instrument import InstrumentTreeController - +from .instrument import InstrumentTreeController, GuiLibrary, GuiInstrument #Engine +from engine.config import * #imports METADATA import engine.api as api -class FavoritesTreeController(InstrumentTreeController): - pass - +fakeLibMostUsed = {} +fakeLibMostUsed["tarFilePath"] = "" +fakeLibMostUsed["id"] = 0 #If this is selected in the GUI it will select the default lib in the main instrument view, which is wrong but safe. +fakeLibMostUsed["name"] = QtCore.QCoreApplication.translate("Favorites", "Top 10 - Most Used") +fakeLibMostUsed["description"] = "" +fakeLibMostUsed["license"] = "" +fakeLibMostUsed["vendor"] = "" +fakeLibMostUsed["version"] = "1" -#Most Loaded -#Most Recent -#Manuelle Favoriten? +class FavoritesTreeController(InstrumentTreeController): -#Selection an den anderen weitergeben.+++++ + def buildTree(self, data:dict): + """ + Only shows some of the instruments, + depending on the current filter-mode + """ + #Reset everything except our cached data. + self.treeWidget.clear() #will delete the C++ objects. We need to delete the PyQt objects ourselves, like so: + self.guiLibraries = {} # idKey : GuiLibrary idKey is a tuple with second value -1, which would be the instrument. + self.guiInstruments = {} # idKey : GuiInstrument + + if data: + self._cachedData = data + else: + assert self._cachedData + data = self._cachedData + + #Add all available instruments to the database, but don't use them yet + for libraryId, libraryDict in data.items(): + for instrumentdId, instrumentDict in libraryDict.items(): + if instrumentdId == "library": + continue + + gi = GuiInstrument(parentTreeController=self, instrumentDict=instrumentDict) + + self.guiInstruments[instrumentDict["idKey"]] = gi + if instrumentDict["idKey"] in self._cachedLastInstrumentStatus: + gi.updateStatus(self._cachedLastInstrumentStatus[instrumentDict["idKey"]]) + + + mostUsed = GuiLibrary(parentTreeController=self, libraryDict=fakeLibMostUsed) + self.treeWidget.addTopLevelItem(mostUsed) + mostUsed.setExpanded(True) + #mostRecent = GuiLibrary(parentTreeController=self, libraryDict=libraryDict["library"]) + + top10 = self.getTop10FavoriteInstruments() # Return an ordered list of double tuple ((libid, instid), counter) + for placement, ((libId, instId), counter) in enumerate(top10): + gi = self.guiInstruments[(libId, instId)] + mostUsed.addChild(gi) + gi.injectWidgets() #only possible after gi.init() was done and item inserted. + #Misuse the id as top 10-Counter + gi.setData(gi.columns.index("idKey"), 0, str(placement+1).zfill(2)) #0 is the data role, just standard display text. We combine both IDs to a float number for sorting. If used with setData instead of setText Qt will know how to sort 11 before 1000 + + self._adjustColumnSize() + + def getTop10FavoriteInstruments(self)->list: + """Return an ordered list of double tuple ((libid, instid), counter)""" + settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) + if settings.contains("favoriteInstruments"): + database = settings.value("favoriteInstruments", type=dict) + return Counter(database).most_common(10) #python already knows how to create that + else: + return [] diff --git a/qtgui/instrument.py b/qtgui/instrument.py index 556f851..8ca94c4 100644 --- a/qtgui/instrument.py +++ b/qtgui/instrument.py @@ -417,11 +417,7 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem): for the details view. """ - - allItems = {} # instrId : GuiInstrument - def __init__(self, parentTreeController, instrumentDict): - GuiInstrument.allItems[instrumentDict["idKey"]] = self self.parentTreeController = parentTreeController self.idKey = instrumentDict["idKey"] diff --git a/qtgui/mainwindow.py b/qtgui/mainwindow.py index 9f919ac..fa649e1 100644 --- a/qtgui/mainwindow.py +++ b/qtgui/mainwindow.py @@ -23,7 +23,7 @@ import logging; logging.info("import {}".format(__file__)) #Standard Library Modules import pathlib import os -from collections import Counter + #Third Party Modules from PyQt5 import QtWidgets, QtCore, QtGui @@ -180,6 +180,10 @@ class MainWindow(TemplateMainWindow): self.menu.addMenuEntry("menuEdit", "actionSampleDirPathDialog", "Sample Files Location", lambda: ChooseDownloadDirectory(parentMainWindow=self)) self.menu.addMenuEntry("menuEdit", "actionLoadSamples", QtCore.QCoreApplication.translate("Menu", "Load all Instrument Samples (slow!)"), api.loadAllInstrumentSamples) self.menu.addMenuEntry("menuEdit", "actionUnloadSamples", QtCore.QCoreApplication.translate("Menu", "Unload all Instrument Samples (also slow.)"), api.unloadAllInstrumentSamples) + + self.menu.addSeparator("menuEdit") + self.menu.addMenuEntry("menuEdit", "actionResetFavoriteInstrumentDatabase", QtCore.QCoreApplication.translate("Menu", "Reset favorite instrument list"), self.resetFavoriteInstrumentDatabase) + #self.menu.connectMenuEntry("actionNils", lambda: print("Override")) self.menu.addSubmenu("menuView", QtCore.QCoreApplication.translate("Menu", "View")) @@ -287,7 +291,7 @@ class MainWindow(TemplateMainWindow): def favoriteInstrument(self, idKey:tuple): """Count the user-instructed, explicit loading of an instrument in the permanent database. We work directly with the qt settings without local data. A crash or sigkill will not - prevent the counting.""" + reset the counting.""" settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) if settings.contains("favoriteInstruments"): database = settings.value("favoriteInstruments", type=dict) @@ -302,17 +306,12 @@ class MainWindow(TemplateMainWindow): settings.setValue("favoriteInstruments", database) + self.favoritesTreeController.buildTree(data=None) #rebuild from cache + + def resetFavoriteInstrumentDatabase(self): settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) if settings.contains("favoriteInstruments"): settings.remove("favoriteInstruments") self.favoriteInstrumentsThisRun = set() - - def getTop10FavoriteInstruments(self)->list: - """Return an ordered list of double tuple ((libid, instid), counter) """ - settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) - if settings.contains("favoriteInstruments"): - database = settings.value("favoriteInstruments", type=dict) - return Counter(database).most_common(10) #python already knows how to create that - else: - return [] + self.favoritesTreeController.buildTree(data=None) #rebuild from cache