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:
"""
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"]
@ -188,6 +185,7 @@ class Instrument(object):
def loadSamples(self):
"""
Convenience starter. Use this.
Used for loading save files as well as manual loading
"""
if not self.enabled:
self.enable()

126
qtgui/designer/mainwindow.py

@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1364, 977)
MainWindow.resize(1175, 651)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("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.setSpacing(0)
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.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.instruments_treeWidget.setProperty("showDropIndicator", False)
@ -86,46 +57,16 @@ class Ui_MainWindow(object):
self.instruments_treeWidget.setObjectName("instruments_treeWidget")
self.verticalLayout_2.addWidget(self.instruments_treeWidget)
self.iinstruments_tabWidget.addTab(self.Instruments, "")
self.Mixer = QtWidgets.QWidget()
self.Mixer.setObjectName("Mixer")
self.mixerVerticalLayout = QtWidgets.QVBoxLayout(self.Mixer)
self.mixerVerticalLayout.setContentsMargins(0, 0, 0, 0)
self.mixerVerticalLayout.setSpacing(0)
self.mixerVerticalLayout.setObjectName("mixerVerticalLayout")
self.mixerInstructionLabel = QtWidgets.QLabel(self.Mixer)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
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.Favorites = QtWidgets.QWidget()
self.Favorites.setObjectName("Favorites")
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.Favorites)
self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_5.setSpacing(0)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.favorites_treeWidget = QtWidgets.QTreeWidget(self.Favorites)
self.favorites_treeWidget.setObjectName("favorites_treeWidget")
self.verticalLayout_5.addWidget(self.favorites_treeWidget)
self.iinstruments_tabWidget.addTab(self.Favorites, "")
self.details_groupBox = QtWidgets.QGroupBox(self.splitter)
self.details_groupBox.setObjectName("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.setObjectName("details_scrollArea")
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.formLayout = QtWidgets.QFormLayout(self.scrollAreaWidgetContents)
self.formLayout.setObjectName("formLayout")
@ -160,6 +101,35 @@ 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)
@ -171,7 +141,7 @@ class Ui_MainWindow(object):
self.horizontalLayout_3.addWidget(self.rightFrame)
MainWindow.setCentralWidget(self.centralwidget)
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")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
@ -185,17 +155,21 @@ 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"))
self.instruments_treeWidget.headerItem().setText(4, _translate("MainWindow", "Variant"))
self.instruments_treeWidget.headerItem().setText(5, _translate("MainWindow", "Tags"))
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.iinstruments_tabWidget.setTabText(self.iinstruments_tabWidget.indexOf(self.Mixer), _translate("MainWindow", "Mixer"))
self.favorites_treeWidget.headerItem().setText(1, _translate("MainWindow", "ID"))
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.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"))

270
qtgui/designer/mainwindow.ui

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1364</width>
<height>977</height>
<width>1175</width>
<height>651</height>
</rect>
</property>
<property name="windowTitle">
@ -101,83 +101,6 @@
<property name="bottomMargin">
<number>0</number>
</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>
<widget class="QTreeWidget" name="instruments_treeWidget">
<property name="editTriggers">
@ -232,11 +155,11 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="Mixer">
<widget class="QWidget" name="Favorites">
<attribute name="title">
<string>Mixer</string>
<string>Favorites</string>
</attribute>
<layout class="QVBoxLayout" name="mixerVerticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property>
@ -253,77 +176,37 @@
<number>0</number>
</property>
<item>
<widget class="QLabel" name="mixerInstructionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<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>
<widget class="QTreeWidget" name="favorites_treeWidget">
<column>
<property name="text">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="textDirection">
<enum>QProgressBar::TopToBottom</enum>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar_2">
<property name="value">
<number>24</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<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>
</column>
<column>
<property name="text">
<string>ID</string>
</property>
</column>
<column>
<property name="text">
<string>Volume</string>
</property>
</column>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Variant</string>
</property>
</column>
<column>
<property name="text">
<string>Tags</string>
</property>
</column>
</widget>
</item>
</layout>
@ -359,8 +242,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1182</width>
<height>225</height>
<width>993</width>
<height>106</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
@ -405,6 +288,83 @@
</widget>
</widget>
</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>
<widget class="QFrame" name="horizontalPianoFrame">
<property name="minimumSize">
@ -432,7 +392,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1364</width>
<width>1175</width>
<height>20</height>
</rect>
</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
import engine.api as api
from .instrument import GuiInstrument, GuiLibrary #for the types
from .verticalpiano import WIDTH as HEIGHT
from .verticalpiano import STAFFLINEGAP as WIDTH
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
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):
"""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.numberLabels = [] #index is pitch
self._selectedInstrument = None #tuple instrumentStatus, instrumentData
self._selectedInstrument = None #instrumentStatus dict
self._leftMouseDown = False #For note preview
self.gridPen = QtGui.QPen(QtCore.Qt.SolidLine)
@ -168,7 +190,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
"""GUI callback. Data is live"""
#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
#else:
# print ("not for us", instrumentStatus["idKey"])
@ -197,7 +219,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
#self.numberLabels[keyPitch].hide()
def selectedInstrumentChanged(self, instrumentStatus, instrumentData):
def selectedInstrumentChanged(self, instrumentStatus):
"""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.
@ -207,15 +229,15 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
self.clearHorizontalPiano()
self.fakeDeactivationOverlay.show()
else:
self._selectedInstrument = (instrumentStatus, instrumentData)
self._selectedInstrument = instrumentStatus
self.instrumentStatusChanged(instrumentStatus)
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()
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()
def allHighlightsOff(self):
@ -236,7 +258,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
def _off(self):
if self._selectedInstrument and not self._lastPlayPitch is None:
status, data = self._selectedInstrument
status = self._selectedInstrument
libId, instrId = status["idKey"]
api.sendNoteOffToInstrument(status["idKey"], self._lastPlayPitch)
self._lastPlayPitch = None
@ -259,7 +281,7 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
if self._selectedInstrument and not pitch == self._lastPlayPitch:
#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:
#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.
"""
def __init__(self, parentMainWindow):
def __init__(self, parentMainWindow, treeWidget):
self.parentMainWindow = parentMainWindow
self.treeWidget = self.parentMainWindow.ui.instruments_treeWidget
self.treeWidget = treeWidget
self.reset()
#Includes:
#self._cachedData = None
#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
@ -71,12 +71,16 @@ class InstrumentTreeController(object):
self.treeWidget.setColumnCount(len(self.headerLabels))
self.treeWidget.setHeaderLabels(self.headerLabels)
self.treeWidget.setSortingEnabled(True)
self.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.treeWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.treeWidget.setAlternatingRowColors(True)
self.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.treeWidget.header().setSortIndicator(0,0)
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.itemCollapsed.connect(self.itemExpandedOrCollapsed)
self.treeWidget.customContextMenuRequested.connect(self.contextMenu)
@ -108,7 +112,7 @@ class InstrumentTreeController(object):
self._cachedData = None
self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict
#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
@ -116,20 +120,32 @@ class InstrumentTreeController(object):
if type(libraryItem) is GuiLibrary : #just in case
api.session.guiSharedDataToSave["libraryIsExpanded"][libraryItem.id] = libraryItem.isExpanded()
def itemSelectionChanged(self):
"""Only one instrument can be selected at the same time.
This function mostly informs other widgets that a different instrument was selected
def currentTreeItemChanged(self, currentTreeItem:QtWidgets.QTreeWidgetItem):
"""
selItems = self.treeWidget.selectedItems()
if not selItems:
return
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.
assert len(selItems) == 1, selItems #because our selection is set to single.
item = selItems[0]
if type(item) is GuiInstrument:
self.parentMainWindow.selectedInstrumentController.instrumentChanged(item.idKey)
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.
"""
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:
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):
"""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.
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
if data:
@ -223,7 +239,7 @@ class InstrumentTreeController(object):
if nested:
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)
if libraryId in api.session.guiSharedDataToSave["libraryIsExpanded"]:
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
the signal"""
if self.isNested():
for libid, guiLib in self.guiLibraries.items():
for idKey, guiLib in self.guiLibraries.items():
guiLib.setExpanded(state)
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)
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"]
@ -474,15 +490,22 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
def instrumentSwitchOnViaGui(self, state):
"""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:
api.loadInstrumentSamples(self.idKey)
self.parentTreeController.parentMainWindow.favoriteInstrument(self.idKey)
else:
api.unloadInstrumentSamples(self.idKey)
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
self._cachedInstrumentStatus = instrumentStatus
self.cachedInstrumentStatus = instrumentStatus
firstLoad = self.state is None
variantColumnIndex = self.columns.index("loaded")
self.currentVariant = instrumentStatus["currentVariant"]
@ -521,7 +544,7 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
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"]))
mixerMuteFunc = lambda: api.setInstrumentMixerEnabled(self.idKey, False)
else:

100
qtgui/mainwindow.py

@ -23,6 +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
@ -37,6 +38,7 @@ import engine.api as api
from engine.config import * #imports METADATA
from .instrument import InstrumentTreeController
from .favorites import FavoritesTreeController
from .auditioner import AuditionerMidiInputComboController
from .selectedinstrumentcontroller import SelectedInstrumentController
from .verticalpiano import VerticalPiano
@ -68,18 +70,27 @@ class MainWindow(TemplateMainWindow):
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
self.ui.details_groupBox.setMinimumSize(1, 50)
self.ui.splitter.setSizes([1,1]) #just a forced update
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.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.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
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", "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"])
def react_rescanSampleDir(self):
@ -227,18 +237,82 @@ class MainWindow(TemplateMainWindow):
settings.setValue("pianoVisible", state)
def selectedInstrumentChanged(self, instrumentStatus, instrumentData):
"""We receive this from selectedinstrumentcontroller.py, when the user clicks on a GUI
entry for a different instrument. This is purely a GUI function. This functions
relays the change to other widgets, except the above mentioned controller.
def currentTreeItemChanged(self, currentTreeItem:QtWidgets.QTreeWidgetItem, previousTreeItem:QtWidgets.QTreeWidgetItem):
"""Somewhere in the program the user-selected tree item changed.
It can be an instrument or a library. Send this change to all widgets
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 pianos use this to switch off.
The widgets handle the type of item themselves. We attached all engine data
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)
self.horizontalPiano.pianoScene.selectedInstrumentChanged(instrumentStatus, instrumentData)
if self.duringTabChange:
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):
pass
def stretchXCoordinates(self, factor):
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
#Our Qt
from .instrument import GuiInstrument, GuiLibrary #for the types
#Engine
import engine.api as api
@ -60,9 +61,9 @@ class SelectedInstrumentController(object):
api.callbacks.instrumentListMetadata.append(self.react_initialInstrumentList)
api.callbacks.instrumentStatusChanged.append(self.react_instrumentStatusChanged)
def directLibrary(self, idkey:tuple):
def directLibrary(self, idKey:tuple):
"""User clicked on a library treeItem"""
libraryId, instrumentId = idkey
libraryId, instrumentId = idKey
self.currentIdKey = None
self.ui.details_scrollArea.show()
self.ui.variants_comboBox.hide()
@ -77,10 +78,6 @@ class SelectedInstrumentController(object):
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:
"""Can work with instruments and libraries alike"""
@ -132,7 +129,26 @@ class SelectedInstrumentController(object):
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
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,
like the two keyboards. We will not receive this callback from the mainwindow.
"""
libraryId, instrumentId = idkey
self.currentIdKey = idkey
libraryId, instrumentId = idKey
self.currentIdKey = idKey
self.ui.details_scrollArea.show()
@ -168,9 +184,6 @@ class SelectedInstrumentController(object):
#Dynamic
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):
"""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
mouseclick in instrumentChanged and the GUI will use the cached data.
"""
idkey = instrumentStatus["idKey"]
libraryId, instrumentId = idkey
idKey = instrumentStatus["idKey"]
libraryId, instrumentId = idKey
if not libraryId in self.statusUpdates:
self.statusUpdates[libraryId] = {} #empty library. status dict
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
return

35
qtgui/verticalpiano.py

@ -30,6 +30,7 @@ from template.engine.duration import baseDurationToTraditionalNumber
#User modules
import engine.api as api
from .instrument import GuiInstrument, GuiLibrary #for the types
WIDTH = 200
@ -59,7 +60,25 @@ class VerticalPiano(QtWidgets.QGraphicsView):
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):
"""Most of this is copy paste from piano grid"""
@ -81,7 +100,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
self.numberLabels = [] #index is pitch
self._selectedInstrument = None #tuple instrumentStatus, instrumentData
self._selectedInstrument = None #instrumentStatus dict
self._leftMouseDown = False #For note preview
self.gridPen = QtGui.QPen(QtCore.Qt.SolidLine)
@ -177,7 +196,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
"""GUI callback. Data is live"""
#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
#else:
# print ("not for us", instrumentStatus["idKey"])
@ -205,7 +224,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
#keyObject.hide()
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
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.fakeDeactivationOverlay.show()
else:
self._selectedInstrument = (instrumentStatus, instrumentData)
self._selectedInstrument = instrumentStatus
self.instrumentStatusChanged(instrumentStatus)
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.show()
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.hide()
@ -245,7 +264,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
def _off(self):
if self._selectedInstrument and not self._lastPlayPitch is None:
status, data = self._selectedInstrument
status = self._selectedInstrument
libId, instrId = status["idKey"]
api.sendNoteOffToInstrument(status["idKey"], self._lastPlayPitch)
self._lastPlayPitch = None
@ -259,7 +278,7 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
if self._selectedInstrument and not pitch == self._lastPlayPitch:
#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:
#Force a note off that is currently playing but not under the cursor anymore

Loading…
Cancel
Save