From 6d2acbe95fd8ef39e5fe8110e7e5ab68c4f03eb6 Mon Sep 17 00:00:00 2001
From: Nils <>
Date: Sun, 27 Feb 2022 18:19:54 +0100
Subject: [PATCH] Favorite instruments
---
engine/config.py | 7 +-
qtgui/chooseDownloadDirectory.py | 4 +-
qtgui/designer/mainwindow.py | 65 ++++++------
qtgui/designer/mainwindow.ui | 165 ++++++++++++++++---------------
qtgui/favorites.py | 73 ++++++++++++--
qtgui/instrument.py | 4 -
qtgui/mainwindow.py | 21 ++--
7 files changed, 198 insertions(+), 141 deletions(-)
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