Browse Source

Refactor selectCurrentInstrument chain to prepare favorites widget

master
Nils 2 years ago
parent
commit
cc4a4bb471
  1. 4
      engine/instrument.py
  2. 126
      qtgui/designer/mainwindow.py
  3. 270
      qtgui/designer/mainwindow.ui
  4. 47
      qtgui/favorites.py
  5. 38
      qtgui/horizontalpiano.py
  6. 69
      qtgui/instrument.py
  7. 100
      qtgui/mainwindow.py
  8. 43
      qtgui/selectedinstrumentcontroller.py
  9. 35
      qtgui/verticalpiano.py

4
engine/instrument.py

@ -151,9 +151,6 @@ class Instrument(object):
def exportMetadata(self)->dict: def exportMetadata(self)->dict:
""" """
This is the big update that sends everything to build a GUI database This is the big update that sends everything to build a GUI database
Please note that we don't add the default variant here. It is only important for the
external world to know what the current variant is. Which is handled by self.exportStatus()
""" """
parentMetadata = self.parentLibrary.config["library"] parentMetadata = self.parentLibrary.config["library"]
@ -188,6 +185,7 @@ class Instrument(object):
def loadSamples(self): def loadSamples(self):
""" """
Convenience starter. Use this. Convenience starter. Use this.
Used for loading save files as well as manual loading
""" """
if not self.enabled: if not self.enabled:
self.enable() self.enable()

126
qtgui/designer/mainwindow.py

@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object): class Ui_MainWindow(object):
def setupUi(self, MainWindow): def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow") MainWindow.setObjectName("MainWindow")
MainWindow.resize(1364, 977) MainWindow.resize(1175, 651)
self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget") self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.centralwidget) self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.centralwidget)
@ -47,35 +47,6 @@ class Ui_MainWindow(object):
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_2.setSpacing(0) self.verticalLayout_2.setSpacing(0)
self.verticalLayout_2.setObjectName("verticalLayout_2") self.verticalLayout_2.setObjectName("verticalLayout_2")
self.auditionerWidget = QtWidgets.QWidget(self.Instruments)
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_2.addWidget(self.auditionerWidget)
self.instruments_treeWidget = QtWidgets.QTreeWidget(self.Instruments) self.instruments_treeWidget = QtWidgets.QTreeWidget(self.Instruments)
self.instruments_treeWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.instruments_treeWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.instruments_treeWidget.setProperty("showDropIndicator", False) self.instruments_treeWidget.setProperty("showDropIndicator", False)
@ -86,46 +57,16 @@ class Ui_MainWindow(object):
self.instruments_treeWidget.setObjectName("instruments_treeWidget") self.instruments_treeWidget.setObjectName("instruments_treeWidget")
self.verticalLayout_2.addWidget(self.instruments_treeWidget) self.verticalLayout_2.addWidget(self.instruments_treeWidget)
self.iinstruments_tabWidget.addTab(self.Instruments, "") self.iinstruments_tabWidget.addTab(self.Instruments, "")
self.Mixer = QtWidgets.QWidget() self.Favorites = QtWidgets.QWidget()
self.Mixer.setObjectName("Mixer") self.Favorites.setObjectName("Favorites")
self.mixerVerticalLayout = QtWidgets.QVBoxLayout(self.Mixer) self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.Favorites)
self.mixerVerticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
self.mixerVerticalLayout.setSpacing(0) self.verticalLayout_5.setSpacing(0)
self.mixerVerticalLayout.setObjectName("mixerVerticalLayout") self.verticalLayout_5.setObjectName("verticalLayout_5")
self.mixerInstructionLabel = QtWidgets.QLabel(self.Mixer) self.favorites_treeWidget = QtWidgets.QTreeWidget(self.Favorites)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) self.favorites_treeWidget.setObjectName("favorites_treeWidget")
sizePolicy.setHorizontalStretch(0) self.verticalLayout_5.addWidget(self.favorites_treeWidget)
sizePolicy.setVerticalStretch(0) self.iinstruments_tabWidget.addTab(self.Favorites, "")
sizePolicy.setHeightForWidth(self.mixerInstructionLabel.sizePolicy().hasHeightForWidth())
self.mixerInstructionLabel.setSizePolicy(sizePolicy)
self.mixerInstructionLabel.setWordWrap(True)
self.mixerInstructionLabel.setObjectName("mixerInstructionLabel")
self.mixerVerticalLayout.addWidget(self.mixerInstructionLabel)
self.scrollArea = QtWidgets.QScrollArea(self.Mixer)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.scrollArea.setObjectName("scrollArea")
self.mixerAreaWidget = QtWidgets.QWidget()
self.mixerAreaWidget.setGeometry(QtCore.QRect(0, 0, 98, 113))
self.mixerAreaWidget.setObjectName("mixerAreaWidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.mixerAreaWidget)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.progressBar = QtWidgets.QProgressBar(self.mixerAreaWidget)
self.progressBar.setProperty("value", 24)
self.progressBar.setOrientation(QtCore.Qt.Vertical)
self.progressBar.setTextDirection(QtWidgets.QProgressBar.TopToBottom)
self.progressBar.setObjectName("progressBar")
self.horizontalLayout_2.addWidget(self.progressBar)
self.progressBar_2 = QtWidgets.QProgressBar(self.mixerAreaWidget)
self.progressBar_2.setProperty("value", 24)
self.progressBar_2.setOrientation(QtCore.Qt.Vertical)
self.progressBar_2.setObjectName("progressBar_2")
self.horizontalLayout_2.addWidget(self.progressBar_2)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem1)
self.scrollArea.setWidget(self.mixerAreaWidget)
self.mixerVerticalLayout.addWidget(self.scrollArea)
self.iinstruments_tabWidget.addTab(self.Mixer, "")
self.details_groupBox = QtWidgets.QGroupBox(self.splitter) self.details_groupBox = QtWidgets.QGroupBox(self.splitter)
self.details_groupBox.setObjectName("details_groupBox") self.details_groupBox.setObjectName("details_groupBox")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.details_groupBox) self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.details_groupBox)
@ -136,7 +77,7 @@ class Ui_MainWindow(object):
self.details_scrollArea.setWidgetResizable(True) self.details_scrollArea.setWidgetResizable(True)
self.details_scrollArea.setObjectName("details_scrollArea") self.details_scrollArea.setObjectName("details_scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget() self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 1182, 225)) self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 993, 106))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.formLayout = QtWidgets.QFormLayout(self.scrollAreaWidgetContents) self.formLayout = QtWidgets.QFormLayout(self.scrollAreaWidgetContents)
self.formLayout.setObjectName("formLayout") self.formLayout.setObjectName("formLayout")
@ -160,6 +101,35 @@ class Ui_MainWindow(object):
self.details_scrollArea.setWidget(self.scrollAreaWidgetContents) self.details_scrollArea.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout_3.addWidget(self.details_scrollArea) self.verticalLayout_3.addWidget(self.details_scrollArea)
self.verticalLayout.addWidget(self.splitter) 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 = QtWidgets.QFrame(self.rightFrame)
self.horizontalPianoFrame.setMinimumSize(QtCore.QSize(0, 100)) self.horizontalPianoFrame.setMinimumSize(QtCore.QSize(0, 100))
self.horizontalPianoFrame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.horizontalPianoFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
@ -171,7 +141,7 @@ class Ui_MainWindow(object):
self.horizontalLayout_3.addWidget(self.rightFrame) self.horizontalLayout_3.addWidget(self.rightFrame)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1364, 20)) self.menubar.setGeometry(QtCore.QRect(0, 0, 1175, 20))
self.menubar.setObjectName("menubar") self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar) MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar = QtWidgets.QStatusBar(MainWindow)
@ -185,17 +155,21 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow): def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) 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(1, _translate("MainWindow", "ID"))
self.instruments_treeWidget.headerItem().setText(2, _translate("MainWindow", "Volume")) self.instruments_treeWidget.headerItem().setText(2, _translate("MainWindow", "Volume"))
self.instruments_treeWidget.headerItem().setText(3, _translate("MainWindow", "Name")) self.instruments_treeWidget.headerItem().setText(3, _translate("MainWindow", "Name"))
self.instruments_treeWidget.headerItem().setText(4, _translate("MainWindow", "Variant")) self.instruments_treeWidget.headerItem().setText(4, _translate("MainWindow", "Variant"))
self.instruments_treeWidget.headerItem().setText(5, _translate("MainWindow", "Tags")) self.instruments_treeWidget.headerItem().setText(5, _translate("MainWindow", "Tags"))
self.iinstruments_tabWidget.setTabText(self.iinstruments_tabWidget.indexOf(self.Instruments), _translate("MainWindow", "Instruments")) self.iinstruments_tabWidget.setTabText(self.iinstruments_tabWidget.indexOf(self.Instruments), _translate("MainWindow", "Instruments"))
self.mixerInstructionLabel.setText(_translate("MainWindow", "This mixer controls the amount of each loaded instrument in the optional mixer output-ports. Idividual instrument outputs are unaffected.")) self.favorites_treeWidget.headerItem().setText(1, _translate("MainWindow", "ID"))
self.iinstruments_tabWidget.setTabText(self.iinstruments_tabWidget.indexOf(self.Mixer), _translate("MainWindow", "Mixer")) self.favorites_treeWidget.headerItem().setText(2, _translate("MainWindow", "Volume"))
self.favorites_treeWidget.headerItem().setText(3, _translate("MainWindow", "Name"))
self.favorites_treeWidget.headerItem().setText(4, _translate("MainWindow", "Variant"))
self.favorites_treeWidget.headerItem().setText(5, _translate("MainWindow", "Tags"))
self.iinstruments_tabWidget.setTabText(self.iinstruments_tabWidget.indexOf(self.Favorites), _translate("MainWindow", "Favorites"))
self.details_groupBox.setTitle(_translate("MainWindow", "NamePlaceholder")) self.details_groupBox.setTitle(_translate("MainWindow", "NamePlaceholder"))
self.variant_label.setText(_translate("MainWindow", "Variants")) self.variant_label.setText(_translate("MainWindow", "Variants"))
self.info_label.setText(_translate("MainWindow", "TextLabel")) self.info_label.setText(_translate("MainWindow", "TextLabel"))
self.keySwitch_label.setText(_translate("MainWindow", "KeySwitch")) self.keySwitch_label.setText(_translate("MainWindow", "KeySwitch"))
self.label.setText(_translate("MainWindow", "Auditioner MIDI Input"))
self.auditionerCurrentInstrument_label.setText(_translate("MainWindow", "TextLabel"))

270
qtgui/designer/mainwindow.ui

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1364</width> <width>1175</width>
<height>977</height> <height>651</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -101,83 +101,6 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item>
<widget class="QWidget" name="auditionerWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QDial" name="auditionerVolumeDial">
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>3</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>-40</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="pageStep">
<number>3</number>
</property>
<property name="value">
<number>-3</number>
</property>
<property name="wrapping">
<bool>false</bool>
</property>
<property name="notchTarget">
<double>3.000000000000000</double>
</property>
<property name="notchesVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Auditioner MIDI Input</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="auditionerMidiInputComboBox">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="auditionerCurrentInstrument_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QTreeWidget" name="instruments_treeWidget"> <widget class="QTreeWidget" name="instruments_treeWidget">
<property name="editTriggers"> <property name="editTriggers">
@ -232,11 +155,11 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="Mixer"> <widget class="QWidget" name="Favorites">
<attribute name="title"> <attribute name="title">
<string>Mixer</string> <string>Favorites</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="mixerVerticalLayout"> <layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
@ -253,77 +176,37 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="mixerInstructionLabel"> <widget class="QTreeWidget" name="favorites_treeWidget">
<property name="sizePolicy"> <column>
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <property name="text">
<horstretch>0</horstretch> <string/>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>This mixer controls the amount of each loaded instrument in the optional mixer output-ports. Idividual instrument outputs are unaffected.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<widget class="QWidget" name="mixerAreaWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>98</width>
<height>113</height>
</rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_2"> </column>
<item> <column>
<widget class="QProgressBar" name="progressBar"> <property name="text">
<property name="value"> <string>ID</string>
<number>24</number> </property>
</property> </column>
<property name="orientation"> <column>
<enum>Qt::Vertical</enum> <property name="text">
</property> <string>Volume</string>
<property name="textDirection"> </property>
<enum>QProgressBar::TopToBottom</enum> </column>
</property> <column>
</widget> <property name="text">
</item> <string>Name</string>
<item> </property>
<widget class="QProgressBar" name="progressBar_2"> </column>
<property name="value"> <column>
<number>24</number> <property name="text">
</property> <string>Variant</string>
<property name="orientation"> </property>
<enum>Qt::Vertical</enum> </column>
</property> <column>
</widget> <property name="text">
</item> <string>Tags</string>
<item> </property>
<spacer name="horizontalSpacer_2"> </column>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -359,8 +242,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1182</width> <width>993</width>
<height>225</height> <height>106</height>
</rect> </rect>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
@ -405,6 +288,83 @@
</widget> </widget>
</widget> </widget>
</item> </item>
<item>
<widget class="QWidget" name="auditionerWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QDial" name="auditionerVolumeDial">
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>3</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>-40</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="pageStep">
<number>3</number>
</property>
<property name="value">
<number>-3</number>
</property>
<property name="wrapping">
<bool>false</bool>
</property>
<property name="notchTarget">
<double>3.000000000000000</double>
</property>
<property name="notchesVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Auditioner MIDI Input</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="auditionerMidiInputComboBox">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="auditionerCurrentInstrument_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QFrame" name="horizontalPianoFrame"> <widget class="QFrame" name="horizontalPianoFrame">
<property name="minimumSize"> <property name="minimumSize">
@ -432,7 +392,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1364</width> <width>1175</width>
<height>20</height> <height>20</height>
</rect> </rect>
</property> </property>

47
qtgui/favorites.py

@ -0,0 +1,47 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022, Nils Hilbricht, Germany ( https://www.hilbricht.net )
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),
This application is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging; logger = logging.getLogger(__name__); logger.info("import")
#Standard Library
#Third Party
from PyQt5 import QtCore, QtGui, QtWidgets
#Our Qt
from .instrument import InstrumentTreeController
#Engine
import engine.api as api
class FavoritesTreeController(InstrumentTreeController):
pass
#Most Loaded
#Most Recent
#Manuelle Favoriten?
#Selection an den anderen weitergeben.+++++

38
qtgui/horizontalpiano.py

@ -30,6 +30,7 @@ from template.engine.duration import baseDurationToTraditionalNumber
#User modules #User modules
import engine.api as api import engine.api as api
from .instrument import GuiInstrument, GuiLibrary #for the types
from .verticalpiano import WIDTH as HEIGHT from .verticalpiano import WIDTH as HEIGHT
from .verticalpiano import STAFFLINEGAP as WIDTH from .verticalpiano import STAFFLINEGAP as WIDTH
WIDTH = WIDTH * 1.5 WIDTH = WIDTH * 1.5
@ -64,6 +65,27 @@ class HorizontalPiano(QtWidgets.QGraphicsView):
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() + event.pixelDelta().y()) #y because it is the original vert. scroll self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() + event.pixelDelta().y()) #y because it is the original vert. scroll
def currentTreeItemChanged(self, currentTreeItem:QtWidgets.QTreeWidgetItem):
"""
Program wide GUI-only callback from
widget.currentItemChanged->mainWindow.currentTreeItemChanged. We set the currentItem
ourselves, so we need to block our signals to avoid recursion.
Only one item can be selected at a time.
The currentTreeItem we receive is not a global instance but from a widget different to ours.
We need to find our local version of the same instrument/library/idKey first.
"""
isLibrary = type(currentTreeItem) is GuiLibrary
idKey = currentTreeItem.idKey
if isLibrary:
self.pianoScene.selectedInstrumentChanged(None)
else:
self.pianoScene.selectedInstrumentChanged(currentTreeItem.cachedInstrumentStatus)
class _HorizontalPianoScene(QtWidgets.QGraphicsScene): class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
"""Most of this is copy paste from piano grid""" """Most of this is copy paste from piano grid"""
@ -80,7 +102,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
self.allKeys = {} # pitch/int : BlackKey or WhiteKey self.allKeys = {} # pitch/int : BlackKey or WhiteKey
self.numberLabels = [] #index is pitch self.numberLabels = [] #index is pitch
self._selectedInstrument = None #tuple instrumentStatus, instrumentData self._selectedInstrument = None #instrumentStatus dict
self._leftMouseDown = False #For note preview self._leftMouseDown = False #For note preview
self.gridPen = QtGui.QPen(QtCore.Qt.SolidLine) self.gridPen = QtGui.QPen(QtCore.Qt.SolidLine)
@ -168,7 +190,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
"""GUI callback. Data is live""" """GUI callback. Data is live"""
#Is this for us? #Is this for us?
if instrumentStatus and self._selectedInstrument and not instrumentStatus["idKey"] == self._selectedInstrument[0]["idKey"]: if instrumentStatus and self._selectedInstrument and not instrumentStatus["idKey"] == self._selectedInstrument["idKey"]:
return return
#else: #else:
# print ("not for us", instrumentStatus["idKey"]) # print ("not for us", instrumentStatus["idKey"])
@ -197,7 +219,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
#self.numberLabels[keyPitch].hide() #self.numberLabels[keyPitch].hide()
def selectedInstrumentChanged(self, instrumentStatus, instrumentData): def selectedInstrumentChanged(self, instrumentStatus):
"""GUI click to different instrument. The arguments are cached GUI data """GUI click to different instrument. The arguments are cached GUI data
If a library is clicked, and not an instrument, both parameters will be None. If a library is clicked, and not an instrument, both parameters will be None.
@ -207,15 +229,15 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
self.clearHorizontalPiano() self.clearHorizontalPiano()
self.fakeDeactivationOverlay.show() self.fakeDeactivationOverlay.show()
else: else:
self._selectedInstrument = (instrumentStatus, instrumentData) self._selectedInstrument = instrumentStatus
self.instrumentStatusChanged(instrumentStatus) self.instrumentStatusChanged(instrumentStatus)
def highlightNoteOn(self, idKey:tuple, pitch:int, velocity:int): def highlightNoteOn(self, idKey:tuple, pitch:int, velocity:int):
if self._selectedInstrument and self._selectedInstrument[0]["idKey"] == idKey: if self._selectedInstrument and self._selectedInstrument["idKey"] == idKey:
self.allKeys[pitch].highlightOn() self.allKeys[pitch].highlightOn()
def highlightNoteOff(self, idKey:tuple, pitch:int, velocity:int): def highlightNoteOff(self, idKey:tuple, pitch:int, velocity:int):
if self._selectedInstrument and self._selectedInstrument[0]["idKey"] == idKey: if self._selectedInstrument and self._selectedInstrument["idKey"] == idKey:
self.allKeys[pitch].highlightOff() self.allKeys[pitch].highlightOff()
def allHighlightsOff(self): def allHighlightsOff(self):
@ -236,7 +258,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
def _off(self): def _off(self):
if self._selectedInstrument and not self._lastPlayPitch is None: if self._selectedInstrument and not self._lastPlayPitch is None:
status, data = self._selectedInstrument status = self._selectedInstrument
libId, instrId = status["idKey"] libId, instrId = status["idKey"]
api.sendNoteOffToInstrument(status["idKey"], self._lastPlayPitch) api.sendNoteOffToInstrument(status["idKey"], self._lastPlayPitch)
self._lastPlayPitch = None self._lastPlayPitch = None
@ -259,7 +281,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
if self._selectedInstrument and not pitch == self._lastPlayPitch: if self._selectedInstrument and not pitch == self._lastPlayPitch:
#TODO: Play note on at a different instrument than note off? Possible? #TODO: Play note on at a different instrument than note off? Possible?
status, data = self._selectedInstrument status = self._selectedInstrument
if not self._lastPlayPitch is None: if not self._lastPlayPitch is None:
#Force a note off that is currently playing but not under the cursor anymore #Force a note off that is currently playing but not under the cursor anymore

69
qtgui/instrument.py

@ -47,15 +47,15 @@ class InstrumentTreeController(object):
to use. You need to add an Item to each cell. While in TreeWidget you just create one item. to use. You need to add an Item to each cell. While in TreeWidget you just create one item.
""" """
def __init__(self, parentMainWindow): def __init__(self, parentMainWindow, treeWidget):
self.parentMainWindow = parentMainWindow self.parentMainWindow = parentMainWindow
self.treeWidget = self.parentMainWindow.ui.instruments_treeWidget self.treeWidget = treeWidget
self.reset() self.reset()
#Includes: #Includes:
#self._cachedData = None #self._cachedData = None
#self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict #self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict
#self.guiLibraries = {} # idKey : GuiLibrary #self.guiLibraries = {} # idKey : GuiLibrary. idKey is a tuple with second value -1, which would be the instrument.
#self.guiInstruments = {} # idKey : GuiInstrument #self.guiInstruments = {} # idKey : GuiInstrument
@ -71,12 +71,16 @@ class InstrumentTreeController(object):
self.treeWidget.setColumnCount(len(self.headerLabels)) self.treeWidget.setColumnCount(len(self.headerLabels))
self.treeWidget.setHeaderLabels(self.headerLabels) self.treeWidget.setHeaderLabels(self.headerLabels)
self.treeWidget.setSortingEnabled(True) self.treeWidget.setSortingEnabled(True)
self.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.treeWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.treeWidget.setAlternatingRowColors(True) self.treeWidget.setAlternatingRowColors(True)
self.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.treeWidget.header().setSortIndicator(0,0) self.treeWidget.header().setSortIndicator(0,0)
self.treeWidget.itemDoubleClicked.connect(self.itemDoubleClicked) self.treeWidget.itemDoubleClicked.connect(self.itemDoubleClicked)
self.treeWidget.itemSelectionChanged.connect(self.itemSelectionChanged) self.treeWidget.currentItemChanged.connect(self.parentMainWindow.currentTreeItemChanged)
#self.treeWidget.itemSelectionChanged.connect(self.itemSelectionChanged) #also triggers on tab change between favorits and instruments
#self.treeWidget.itemClicked.connect(self.itemSelectionChanged) #This will not activate when using the arrow keys to select
self.treeWidget.itemExpanded.connect(self.itemExpandedOrCollapsed) self.treeWidget.itemExpanded.connect(self.itemExpandedOrCollapsed)
self.treeWidget.itemCollapsed.connect(self.itemExpandedOrCollapsed) self.treeWidget.itemCollapsed.connect(self.itemExpandedOrCollapsed)
self.treeWidget.customContextMenuRequested.connect(self.contextMenu) self.treeWidget.customContextMenuRequested.connect(self.contextMenu)
@ -108,7 +112,7 @@ class InstrumentTreeController(object):
self._cachedData = None self._cachedData = None
self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict
#The next two will delete all children through the garbage collector. #The next two will delete all children through the garbage collector.
self.guiLibraries = {} # idKey : GuiLibrary self.guiLibraries = {} # idKey : GuiLibrary idKey is a tuple with second value -1, which would be the instrument.
self.guiInstruments = {} # idKey : GuiInstrument self.guiInstruments = {} # idKey : GuiInstrument
@ -116,20 +120,32 @@ class InstrumentTreeController(object):
if type(libraryItem) is GuiLibrary : #just in case if type(libraryItem) is GuiLibrary : #just in case
api.session.guiSharedDataToSave["libraryIsExpanded"][libraryItem.id] = libraryItem.isExpanded() api.session.guiSharedDataToSave["libraryIsExpanded"][libraryItem.id] = libraryItem.isExpanded()
def itemSelectionChanged(self):
"""Only one instrument can be selected at the same time. def currentTreeItemChanged(self, currentTreeItem:QtWidgets.QTreeWidgetItem):
This function mostly informs other widgets that a different instrument was selected
""" """
selItems = self.treeWidget.selectedItems() Program wide GUI-only callback from
if not selItems: widget.currentItemChanged->mainWindow.currentTreeItemChanged. We set the currentItem
return ourselves, so we need to block our signals to avoid recursion.
assert len(selItems) == 1, selItems #because our selection is set to single. Only one item can be selected at a time.
item = selItems[0]
if type(item) is GuiInstrument: The currentTreeItem we receive is not a global instance but from a widget different to ours.
self.parentMainWindow.selectedInstrumentController.instrumentChanged(item.idKey) We need to find our local version of the same instrument/library/idKey first.
"""
self.treeWidget.blockSignals(True)
isLibrary = type(currentTreeItem) is GuiLibrary
idKey = currentTreeItem.idKey
if isLibrary:
assert idKey in self.guiLibraries, (idKey, self.guiLibraries)
item = self.guiLibraries[idKey]
else: else:
self.parentMainWindow.selectedInstrumentController.directLibrary(item.idKey) assert idKey in self.guiInstruments, (idKey, self.guiInstruments)
item = self.guiInstruments[idKey]
self.treeWidget.setCurrentItem(item) #This will work at first, but as soon as the tab instr/favorites is changed this will jump back to a wrong item (first of the group). We have a tabWidget.changed signal in the main window to reset this
self.treeWidget.blockSignals(False)
def itemDoubleClicked(self, item:QtWidgets.QTreeWidgetItem, column:int): def itemDoubleClicked(self, item:QtWidgets.QTreeWidgetItem, column:int):
"""This chooses the auditioner. Callbacks are handled in the auditioner widget itself""" """This chooses the auditioner. Callbacks are handled in the auditioner widget itself"""
@ -206,7 +222,7 @@ class InstrumentTreeController(object):
""" """
#Reset everything except our cached data. #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.treeWidget.clear() #will delete the C++ objects. We need to delete the PyQt objects ourselves, like so:
self.guiLibraries = {} # idKey : GuiLibrary self.guiLibraries = {} # idKey : GuiLibrary idKey is a tuple with second value -1, which would be the instrument.
self.guiInstruments = {} # idKey : GuiInstrument self.guiInstruments = {} # idKey : GuiInstrument
if data: if data:
@ -223,7 +239,7 @@ class InstrumentTreeController(object):
if nested: if nested:
parentLibraryWidget = GuiLibrary(parentTreeController=self, libraryDict=libraryDict["library"]) parentLibraryWidget = GuiLibrary(parentTreeController=self, libraryDict=libraryDict["library"])
self.guiLibraries[libraryId] = parentLibraryWidget self.guiLibraries[(libraryId, -1)] = parentLibraryWidget #-1 marks the library but keeps it a tuple.
self.treeWidget.addTopLevelItem(parentLibraryWidget) self.treeWidget.addTopLevelItem(parentLibraryWidget)
if libraryId in api.session.guiSharedDataToSave["libraryIsExpanded"]: if libraryId in api.session.guiSharedDataToSave["libraryIsExpanded"]:
parentLibraryWidget.setExpanded(api.session.guiSharedDataToSave["libraryIsExpanded"][libraryId]) #only possible after gi.init() was done and item inserted. parentLibraryWidget.setExpanded(api.session.guiSharedDataToSave["libraryIsExpanded"][libraryId]) #only possible after gi.init() was done and item inserted.
@ -270,7 +286,7 @@ class InstrumentTreeController(object):
"""We do not use the qt function collapseAll and expandAll because they do not trigger """We do not use the qt function collapseAll and expandAll because they do not trigger
the signal""" the signal"""
if self.isNested(): if self.isNested():
for libid, guiLib in self.guiLibraries.items(): for idKey, guiLib in self.guiLibraries.items():
guiLib.setExpanded(state) guiLib.setExpanded(state)
def _adjustColumnSize(self): def _adjustColumnSize(self):
@ -334,7 +350,7 @@ class GuiLibrary(QtWidgets.QTreeWidgetItem):
super().__init__([], type=1000) #type 0 is default qt type. 1000 is subclassed user type) super().__init__([], type=1000) #type 0 is default qt type. 1000 is subclassed user type)
self.id = libraryDict["id"] self.id = libraryDict["id"]
self.idKey = (libraryDict["id"], 0) #fake it for compatibility self.idKey = (libraryDict["id"], -1) #fake it for compatibility. -1 means library
self.name = libraryDict["name"] self.name = libraryDict["name"]
@ -474,15 +490,22 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
def instrumentSwitchOnViaGui(self, state): def instrumentSwitchOnViaGui(self, state):
"""Only GUI clicks. Does not react to the engine callback that switches on instruments. For """Only GUI clicks. Does not react to the engine callback that switches on instruments. For
example one that arrives through "load group" or "load all" """ example one that arrives through "load group" or "load all"
We use this to count, across program runs, how many times this instrument was activated
and present it as favourites. The engine does not know about this.
Loading states from save files, "load all" and other algorithmic loaders do not contribute
to this counting.
"""
if state: if state:
api.loadInstrumentSamples(self.idKey) api.loadInstrumentSamples(self.idKey)
self.parentTreeController.parentMainWindow.favoriteInstrument(self.idKey)
else: else:
api.unloadInstrumentSamples(self.idKey) api.unloadInstrumentSamples(self.idKey)
def updateStatus(self, instrumentStatus:dict): def updateStatus(self, instrumentStatus:dict):
#Before we set the state permanently we use the opportunity to see if this is program state (state == None) or unloading #Before we set the state permanently we use the opportunity to see if this is program state (state == None) or unloading
self._cachedInstrumentStatus = instrumentStatus self.cachedInstrumentStatus = instrumentStatus
firstLoad = self.state is None firstLoad = self.state is None
variantColumnIndex = self.columns.index("loaded") variantColumnIndex = self.columns.index("loaded")
self.currentVariant = instrumentStatus["currentVariant"] self.currentVariant = instrumentStatus["currentVariant"]
@ -521,7 +544,7 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
def _mixSendDialContextMenuEvent(self, event): def _mixSendDialContextMenuEvent(self, event):
if self._cachedInstrumentStatus["mixerEnabled"]: if self.cachedInstrumentStatus["mixerEnabled"]:
mixerMuteText = QtCore.QCoreApplication.translate("InstrumentMixerLevelContextMenu", "Mute/Disable Mixer-Send for {}".format(self.instrumentDict["name"])) mixerMuteText = QtCore.QCoreApplication.translate("InstrumentMixerLevelContextMenu", "Mute/Disable Mixer-Send for {}".format(self.instrumentDict["name"]))
mixerMuteFunc = lambda: api.setInstrumentMixerEnabled(self.idKey, False) mixerMuteFunc = lambda: api.setInstrumentMixerEnabled(self.idKey, False)
else: else:

100
qtgui/mainwindow.py

@ -23,6 +23,7 @@ import logging; logging.info("import {}".format(__file__))
#Standard Library Modules #Standard Library Modules
import pathlib import pathlib
import os import os
from collections import Counter
#Third Party Modules #Third Party Modules
from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5 import QtWidgets, QtCore, QtGui
@ -37,6 +38,7 @@ import engine.api as api
from engine.config import * #imports METADATA from engine.config import * #imports METADATA
from .instrument import InstrumentTreeController from .instrument import InstrumentTreeController
from .favorites import FavoritesTreeController
from .auditioner import AuditionerMidiInputComboController from .auditioner import AuditionerMidiInputComboController
from .selectedinstrumentcontroller import SelectedInstrumentController from .selectedinstrumentcontroller import SelectedInstrumentController
from .verticalpiano import VerticalPiano from .verticalpiano import VerticalPiano
@ -68,18 +70,27 @@ class MainWindow(TemplateMainWindow):
super().__init__() super().__init__()
#To prevent double counting of instruments loads we remember what we already loaded this time. The permanent database is in the qt settings.
self.favoriteInstrumentsThisRun = set() #idKey Tuple
#Make the description field at least a bit visible #Make the description field at least a bit visible
self.ui.details_groupBox.setMinimumSize(1, 50) self.ui.details_groupBox.setMinimumSize(1, 50)
self.ui.splitter.setSizes([1,1]) #just a forced update self.ui.splitter.setSizes([1,1]) #just a forced update
self.auditionerMidiInputComboController = AuditionerMidiInputComboController(parentMainWindow=self) self.auditionerMidiInputComboController = AuditionerMidiInputComboController(parentMainWindow=self)
self.instrumentTreeController = InstrumentTreeController(parentMainWindow=self) self.instrumentTreeController = InstrumentTreeController(parentMainWindow=self, treeWidget=self.ui.instruments_treeWidget)
self.favoritesTreeController = FavoritesTreeController(parentMainWindow=self, treeWidget=self.ui.favorites_treeWidget)
self.selectedInstrumentController = SelectedInstrumentController(parentMainWindow=self) self.selectedInstrumentController = SelectedInstrumentController(parentMainWindow=self)
self.tabWidget = self.ui.iinstruments_tabWidget #with this ugly name it is only a matter of time that it gets changed self.tabWidget = self.ui.iinstruments_tabWidget #with this ugly name it is only a matter of time that it gets changed. Better save in a permanent var.
self.tabWidget.setTabBarAutoHide(True) self.tabWidget.setTabBarAutoHide(True)
self.tabWidget.setTabVisible(1, False) #Hide Mixer until we decide if we need it. #TODO #self.tabWidget.setTabVisible(2, False) #Hide Favorites until we decide if we need it.
#The tab widget / treewidget combination triggers a bug in Qt. The treewidget will actively switch to the wrongly selected item.
#We added a signal on tabChanged to set it back as a hack/workaround. Remember the real item here:
self.rememberCurrentItem = None #idKey
self.duringTabChange = False #our own "block signal"
self.tabWidget.tabBarClicked.connect(self.signalBlockTabAboutToChange)
self.tabWidget.currentChanged.connect(self.bugWorkaroundRestoreCurrentItem)
#Set up the two pianos #Set up the two pianos
self.verticalPiano = VerticalPiano(self) self.verticalPiano = VerticalPiano(self)
@ -180,7 +191,6 @@ class MainWindow(TemplateMainWindow):
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", "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 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
self.menu.orderSubmenus(["menuFile", "menuEdit", "menuView", "menuHelp"]) self.menu.orderSubmenus(["menuFile", "menuEdit", "menuView", "menuHelp"])
def react_rescanSampleDir(self): def react_rescanSampleDir(self):
@ -227,18 +237,82 @@ class MainWindow(TemplateMainWindow):
settings.setValue("pianoVisible", state) settings.setValue("pianoVisible", state)
def selectedInstrumentChanged(self, instrumentStatus, instrumentData): def currentTreeItemChanged(self, currentTreeItem:QtWidgets.QTreeWidgetItem, previousTreeItem:QtWidgets.QTreeWidgetItem):
"""We receive this from selectedinstrumentcontroller.py, when the user clicks on a GUI """Somewhere in the program the user-selected tree item changed.
entry for a different instrument. This is purely a GUI function. This functions It can be an instrument or a library. Send this change to all widgets
relays the change to other widgets, except the above mentioned controller. that deal with instruments and libraries, which are most of them.
If a library is clicked, and not an instrument, both parameters will be None. The widgets handle the type of item themselves. We attached all engine data
The pianos use this to switch off. to the items themselves. The items are not universal for the whole program but children
of one specific widget. The receivers of this callback need to extract their information
from the treeItem.
Technically this is the signal QTreeWidget.currentTreeItemChanged so we receive their
parameters.
""" """
self.verticalPiano.pianoScene.selectedInstrumentChanged(instrumentStatus, instrumentData) if self.duringTabChange:
self.horizontalPiano.pianoScene.selectedInstrumentChanged(instrumentStatus, instrumentData) currentTreeItem = self.rememberCurrentItem #There is a bug in tabChange signal. The TreeWidget will send an old item
self.rememberCurrentItem = currentTreeItem
if not currentTreeItem:
return
widgets = (
self.verticalPiano,
self.horizontalPiano,
self.selectedInstrumentController,
self.instrumentTreeController,
self.favoritesTreeController,
)
for widget in widgets:
widget.currentTreeItemChanged(currentTreeItem)
def signalBlockTabAboutToChange(self, tabIndex:int):
"""Click on a tab.
Finalized through bugWorkaroundRestoreCurrentItem"""
self.duringTabChange = True
def bugWorkaroundRestoreCurrentItem(self):
"""The tab already has changed.
Preceded by signalBlockTabAboutToChange"""
self.duringTabChange = False
def zoom(self, scaleFactor:float): def zoom(self, scaleFactor:float):
pass pass
def stretchXCoordinates(self, factor): def stretchXCoordinates(self, factor):
pass pass
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."""
settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"])
if settings.contains("favoriteInstruments"):
database = settings.value("favoriteInstruments", type=dict)
else:
database = {}
if not idKey in self.favoriteInstrumentsThisRun:
if not idKey in database:
database[idKey] = 0
database[idKey] += 1
self.favoriteInstrumentsThisRun.add(idKey)
settings.setValue("favoriteInstruments", database)
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 []

43
qtgui/selectedinstrumentcontroller.py

@ -27,6 +27,7 @@ import logging; logger = logging.getLogger(__name__); logger.info("import")
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
#Our Qt #Our Qt
from .instrument import GuiInstrument, GuiLibrary #for the types
#Engine #Engine
import engine.api as api import engine.api as api
@ -60,9 +61,9 @@ class SelectedInstrumentController(object):
api.callbacks.instrumentListMetadata.append(self.react_initialInstrumentList) api.callbacks.instrumentListMetadata.append(self.react_initialInstrumentList)
api.callbacks.instrumentStatusChanged.append(self.react_instrumentStatusChanged) api.callbacks.instrumentStatusChanged.append(self.react_instrumentStatusChanged)
def directLibrary(self, idkey:tuple): def directLibrary(self, idKey:tuple):
"""User clicked on a library treeItem""" """User clicked on a library treeItem"""
libraryId, instrumentId = idkey libraryId, instrumentId = idKey
self.currentIdKey = None self.currentIdKey = None
self.ui.details_scrollArea.show() self.ui.details_scrollArea.show()
self.ui.variants_comboBox.hide() self.ui.variants_comboBox.hide()
@ -77,10 +78,6 @@ class SelectedInstrumentController(object):
self.ui.info_label.setText(self._metadataToDescriptionLabel(metadata)) self.ui.info_label.setText(self._metadataToDescriptionLabel(metadata))
#Inform other widgets (but not recursively ourselves) that we clicked on no instrument.
#e.g. to switch off the gui keyboards.
self.parentMainWindow.selectedInstrumentChanged(None, None)
def _metadataToDescriptionLabel(self, metadata:dict)->str: def _metadataToDescriptionLabel(self, metadata:dict)->str:
"""Can work with instruments and libraries alike""" """Can work with instruments and libraries alike"""
@ -132,7 +129,26 @@ class SelectedInstrumentController(object):
self.ui.keySwitch_comboBox.setCurrentIndex(curIdx) self.ui.keySwitch_comboBox.setCurrentIndex(curIdx)
def instrumentChanged(self, idkey:tuple): def currentTreeItemChanged(self, currentTreeItem:QtWidgets.QTreeWidgetItem):
"""
Program wide GUI-only callback from
widget.currentItemChanged->mainWindow.currentTreeItemChanged. We set the currentItem
ourselves, so we need to block our signals to avoid recursion.
Only one item can be selected at a time.
The currentTreeItem we receive is not a global instance but from a widget different to ours.
We need to find our local version of the same instrument/library/idKey first.
"""
isLibrary = type(currentTreeItem) is GuiLibrary
idKey = currentTreeItem.idKey
if isLibrary:
self.directLibrary(idKey)
else:
self.instrumentChanged(idKey)
def instrumentChanged(self, idKey:tuple):
"""This is a GUI-internal function. The user selected a different instrument from """This is a GUI-internal function. The user selected a different instrument from
the list. Single click, arrow keys etc. the list. Single click, arrow keys etc.
@ -142,8 +158,8 @@ class SelectedInstrumentController(object):
We also relay this information to the main window which can send it to other widgets, We also relay this information to the main window which can send it to other widgets,
like the two keyboards. We will not receive this callback from the mainwindow. like the two keyboards. We will not receive this callback from the mainwindow.
""" """
libraryId, instrumentId = idkey libraryId, instrumentId = idKey
self.currentIdKey = idkey self.currentIdKey = idKey
self.ui.details_scrollArea.show() self.ui.details_scrollArea.show()
@ -168,9 +184,6 @@ class SelectedInstrumentController(object):
#Dynamic #Dynamic
self.react_instrumentStatusChanged(self.statusUpdates[libraryId][instrumentId]) self.react_instrumentStatusChanged(self.statusUpdates[libraryId][instrumentId])
#Relay to Main Window. We will not receive this ourselves.
self.parentMainWindow.selectedInstrumentChanged(instrumentStatus, instrumentData)
def react_instrumentStatusChanged(self, instrumentStatus:dict): def react_instrumentStatusChanged(self, instrumentStatus:dict):
"""Callback from the api. Has nothing to do with any GUI state or selection. """Callback from the api. Has nothing to do with any GUI state or selection.
@ -194,14 +207,14 @@ class SelectedInstrumentController(object):
This callback is called again by the GUI directly when switching the instrument with a This callback is called again by the GUI directly when switching the instrument with a
mouseclick in instrumentChanged and the GUI will use the cached data. mouseclick in instrumentChanged and the GUI will use the cached data.
""" """
idkey = instrumentStatus["idKey"] idKey = instrumentStatus["idKey"]
libraryId, instrumentId = idkey libraryId, instrumentId = idKey
if not libraryId in self.statusUpdates: if not libraryId in self.statusUpdates:
self.statusUpdates[libraryId] = {} #empty library. status dict self.statusUpdates[libraryId] = {} #empty library. status dict
self.statusUpdates[libraryId][instrumentId] = instrumentStatus #create or overwrite / keep up to date self.statusUpdates[libraryId][instrumentId] = instrumentStatus #create or overwrite / keep up to date
if not self.currentIdKey == idkey: if not self.currentIdKey == idKey:
#Callback for an instrument currently not selected #Callback for an instrument currently not selected
return return

35
qtgui/verticalpiano.py

@ -30,6 +30,7 @@ from template.engine.duration import baseDurationToTraditionalNumber
#User modules #User modules
import engine.api as api import engine.api as api
from .instrument import GuiInstrument, GuiLibrary #for the types
WIDTH = 200 WIDTH = 200
@ -59,7 +60,25 @@ class VerticalPiano(QtWidgets.QGraphicsView):
self.centerOn(0, 64*STAFFLINEGAP) self.centerOn(0, 64*STAFFLINEGAP)
def currentTreeItemChanged(self, currentTreeItem:QtWidgets.QTreeWidgetItem):
"""
Program wide GUI-only callback from
widget.currentItemChanged->mainWindow.currentTreeItemChanged. We set the currentItem
ourselves, so we need to block our signals to avoid recursion.
Only one item can be selected at a time.
The currentTreeItem we receive is not a global instance but from a widget different to ours.
We need to find our local version of the same instrument/library/idKey first.
"""
isLibrary = type(currentTreeItem) is GuiLibrary
idKey = currentTreeItem.idKey
if isLibrary:
self.pianoScene.selectedInstrumentChanged(None)
else:
self.pianoScene.selectedInstrumentChanged(currentTreeItem.cachedInstrumentStatus)
class _VerticalPianoScene(QtWidgets.QGraphicsScene): class _VerticalPianoScene(QtWidgets.QGraphicsScene):
"""Most of this is copy paste from piano grid""" """Most of this is copy paste from piano grid"""
@ -81,7 +100,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
self.numberLabels = [] #index is pitch self.numberLabels = [] #index is pitch
self._selectedInstrument = None #tuple instrumentStatus, instrumentData self._selectedInstrument = None #instrumentStatus dict
self._leftMouseDown = False #For note preview self._leftMouseDown = False #For note preview
self.gridPen = QtGui.QPen(QtCore.Qt.SolidLine) self.gridPen = QtGui.QPen(QtCore.Qt.SolidLine)
@ -177,7 +196,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
"""GUI callback. Data is live""" """GUI callback. Data is live"""
#Is this for us? #Is this for us?
if instrumentStatus and self._selectedInstrument and not instrumentStatus["idKey"] == self._selectedInstrument[0]["idKey"]: if instrumentStatus and self._selectedInstrument and not instrumentStatus["idKey"] == self._selectedInstrument["idKey"]:
return return
#else: #else:
# print ("not for us", instrumentStatus["idKey"]) # print ("not for us", instrumentStatus["idKey"])
@ -205,7 +224,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
#keyObject.hide() #keyObject.hide()
keyObject.setPlayable(keyPitch in instrumentStatus["playableKeys"]) keyObject.setPlayable(keyPitch in instrumentStatus["playableKeys"])
def selectedInstrumentChanged(self, instrumentStatus, instrumentData): def selectedInstrumentChanged(self, instrumentStatus):
"""GUI click to different instrument. The arguments are cached GUI data """GUI click to different instrument. The arguments are cached GUI data
If a library is clicked, and not an instrument, both parameters will be None. If a library is clicked, and not an instrument, both parameters will be None.
@ -215,16 +234,16 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
self.clearVerticalPiano() self.clearVerticalPiano()
self.fakeDeactivationOverlay.show() self.fakeDeactivationOverlay.show()
else: else:
self._selectedInstrument = (instrumentStatus, instrumentData) self._selectedInstrument = instrumentStatus
self.instrumentStatusChanged(instrumentStatus) self.instrumentStatusChanged(instrumentStatus)
def highlightNoteOn(self, idKey:tuple, pitch:int, velocity:int): def highlightNoteOn(self, idKey:tuple, pitch:int, velocity:int):
if self._selectedInstrument and self._selectedInstrument[0]["idKey"] == idKey: if self._selectedInstrument and self._selectedInstrument["idKey"] == idKey:
highlight = self.highlights[pitch] highlight = self.highlights[pitch]
highlight.show() highlight.show()
def highlightNoteOff(self, idKey:tuple, pitch:int, velocity:int): def highlightNoteOff(self, idKey:tuple, pitch:int, velocity:int):
if self._selectedInstrument and self._selectedInstrument[0]["idKey"] == idKey: if self._selectedInstrument and self._selectedInstrument["idKey"] == idKey:
highlight = self.highlights[pitch] highlight = self.highlights[pitch]
highlight.hide() highlight.hide()
@ -245,7 +264,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
def _off(self): def _off(self):
if self._selectedInstrument and not self._lastPlayPitch is None: if self._selectedInstrument and not self._lastPlayPitch is None:
status, data = self._selectedInstrument status = self._selectedInstrument
libId, instrId = status["idKey"] libId, instrId = status["idKey"]
api.sendNoteOffToInstrument(status["idKey"], self._lastPlayPitch) api.sendNoteOffToInstrument(status["idKey"], self._lastPlayPitch)
self._lastPlayPitch = None self._lastPlayPitch = None
@ -259,7 +278,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
if self._selectedInstrument and not pitch == self._lastPlayPitch: if self._selectedInstrument and not pitch == self._lastPlayPitch:
#TODO: Play note on at a different instrument than note off? Possible? #TODO: Play note on at a different instrument than note off? Possible?
status, data = self._selectedInstrument status = self._selectedInstrument
if not self._lastPlayPitch is None: if not self._lastPlayPitch is None:
#Force a note off that is currently playing but not under the cursor anymore #Force a note off that is currently playing but not under the cursor anymore

Loading…
Cancel
Save