
8 changed files with 373 additions and 69 deletions
@ -0,0 +1,75 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2021, 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; logging.info("import {}".format(__file__)) |
|||
|
|||
#Standard Library Modules |
|||
|
|||
#Third Party Modules |
|||
from PyQt5 import QtWidgets, QtCore, QtGui |
|||
|
|||
#Template Modules |
|||
|
|||
#Our modules |
|||
import engine.api as api |
|||
|
|||
|
|||
class AuditionerMidiInputComboController(object): |
|||
|
|||
def __init__(self, parentMainWindow): |
|||
self.parentMainWindow = parentMainWindow |
|||
self.comboBox = parentMainWindow.ui.auditionerMidiInputComboBox |
|||
self.wholePanel = parentMainWindow.ui.auditionerWidget |
|||
self.currentInstrumentLabel = parentMainWindow.ui.auditionerCurrentInstrument_label |
|||
self.currentInstrumentLabel.setText("") |
|||
|
|||
#if not api.isStandaloneMode(): |
|||
#self.wholePanel.hide() |
|||
#return |
|||
|
|||
self.wholePanel.show() #explicit is better than implicit |
|||
self.originalShowPopup = self.comboBox.showPopup |
|||
self.comboBox.showPopup = self.showPopup |
|||
self.comboBox.activated.connect(self._newPortChosen) |
|||
|
|||
api.callbacks.auditionerInstrumentChanged.append(self.callback_auditionerInstrumentChanged) |
|||
|
|||
def callback_auditionerInstrumentChanged(self, exportMetadata:dict): |
|||
key = exportMetadata["id-key"] |
|||
t = f"➜ [{key[0]}-{key[1]}] {exportMetadata['name']}" |
|||
self.currentInstrumentLabel.setText(t) |
|||
|
|||
def _newPortChosen(self, index:int): |
|||
assert self.comboBox.currentIndex() == index |
|||
api.connectAuditionerPort(self.comboBox.currentText()) |
|||
|
|||
def showPopup(self): |
|||
"""When the combobox is opened quickly update the port list before showing it""" |
|||
self._fill() |
|||
self.originalShowPopup() |
|||
|
|||
def _fill(self): |
|||
self.comboBox.clear() |
|||
availablePorts = api.getAvailableAuditionerPorts() |
|||
self.comboBox.addItem("") # Not only a more visible seaparator than the Qt one, but also doubles as "disconnect" |
|||
self.comboBox.addItems(availablePorts["hardware"]) |
|||
#self.comboBox.insertSeparator(len(availablePorts["hardware"])+1) |
|||
self.comboBox.addItem("") # Not only a more visible seaparator than the Qt one, but also doubles as "disconnect" |
|||
self.comboBox.addItems(availablePorts["software"]) |
@ -0,0 +1,166 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2021, 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 |
|||
|
|||
#Engine |
|||
import engine.api as api |
|||
|
|||
|
|||
|
|||
class SelectedInstrumentController(object): |
|||
"""Not a qt class. We externally control a collection of widgets. |
|||
There is only one set of widgets. We change their contents dynamically. |
|||
""" |
|||
|
|||
def __init__(self, parentMainWindow): |
|||
self.parentMainWindow = parentMainWindow |
|||
self.currentIdKey = None |
|||
self.engineData = {} # id-key tuple : engine library metadata dict. |
|||
self.statusUpdates = {} # same as engineData, but with incremental status updates. One is guranteed to exist at startup |
|||
|
|||
#Our Widgets |
|||
self.ui = parentMainWindow.ui |
|||
self.ui.details_groupBox.setTitle("") |
|||
self.ui.details_scrollArea.hide() #until the first instrument was selected |
|||
|
|||
self.ui.variants_comboBox.activated.connect(self._newVariantChosen) |
|||
|
|||
|
|||
#Callbacks |
|||
api.callbacks.instrumentListMetadata.append(self.react_initialInstrumentList) |
|||
api.callbacks.instrumentStatusChanged.append(self.react_instrumentStatusChanged) |
|||
|
|||
|
|||
|
|||
def directLibrary(self, idkey:tuple): |
|||
"""User clicked on a library treeItem""" |
|||
libraryId, instrumentId = idkey |
|||
self.currentIdKey = None |
|||
self.ui.details_scrollArea.show() |
|||
self.ui.variants_comboBox.hide() |
|||
self.ui.variant_label.hide() |
|||
|
|||
metadata = self.engineData[libraryId]["library"] |
|||
|
|||
self.ui.details_groupBox.setTitle(metadata["name"]) |
|||
|
|||
|
|||
self.ui.info_label.setText(self._metadataToDescriptionLabel(metadata)) |
|||
|
|||
def _metadataToDescriptionLabel(self, metadata:dict)->str: |
|||
"""Can work with instruments and libraries alike""" |
|||
|
|||
if "variants" in metadata: #this is an instrument |
|||
fullText = metadata["description"] + "\n\nVendor: " + metadata["vendor"] + "\n\nLicense: " + metadata["license"] |
|||
else: |
|||
fullText = metadata["description"] + "\n\nVendor: " + metadata["vendor"] |
|||
return fullText |
|||
|
|||
def _newVariantChosen(self, index:int): |
|||
"""User chose a new variant through the combo box""" |
|||
assert self.ui.variants_comboBox.currentIndex() == index |
|||
assert self.currentIdKey |
|||
api.chooseVariantByIndex(self.currentIdKey, index) |
|||
|
|||
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. |
|||
|
|||
We combine static metadata, which we saved ourselves, with the current instrument status |
|||
(e.g. which variant was chosen). |
|||
""" |
|||
libraryId, instrumentId = idkey |
|||
self.currentIdKey = idkey |
|||
|
|||
self.ui.details_scrollArea.show() |
|||
|
|||
#Static |
|||
instrumentData = self.engineData[libraryId][instrumentId] |
|||
self.ui.details_groupBox.setTitle(instrumentData["name"]) |
|||
|
|||
self.ui.variant_label.show() |
|||
self.ui.variants_comboBox.show() |
|||
self.ui.variants_comboBox.clear() |
|||
self.ui.variants_comboBox.addItems(instrumentData["variantsWithoutSfzExtension"]) |
|||
|
|||
self.ui.info_label.setText(self._metadataToDescriptionLabel(instrumentData)) |
|||
|
|||
#Dynamic |
|||
self.react_instrumentStatusChanged(self.statusUpdates[libraryId][instrumentId]) |
|||
|
|||
|
|||
def react_instrumentStatusChanged(self, instrumentStatus:dict): |
|||
"""Callback from the api. Has nothing to do with any GUI state or selection. |
|||
Data: |
|||
#Static ids |
|||
result["id"] = self.metadata["id"] |
|||
result["id-key"] = self.idKey #redundancy for convenience. |
|||
|
|||
#Dynamic data |
|||
result["currentVariant"] = self.currentVariant # str |
|||
result["state"] = self.enabled #bool |
|||
""" |
|||
idkey = instrumentStatus["id-key"] |
|||
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 |
|||
|
|||
loadState = instrumentStatus["state"] |
|||
|
|||
instrumentData = self.engineData[libraryId][instrumentId] |
|||
instrumentStatus = self.statusUpdates[libraryId][instrumentId] |
|||
if loadState: #None if not loaded |
|||
self.ui.variants_comboBox.setEnabled(True) |
|||
currentVariantIndex = instrumentData["variantsWithoutSfzExtension"].index(instrumentStatus["currentVariantWithoutSfzExtension"]) |
|||
self.ui.variants_comboBox.setCurrentIndex(currentVariantIndex) |
|||
else: |
|||
self.ui.variants_comboBox.setEnabled(False) |
|||
defaultVariantIndex = instrumentData["variantsWithoutSfzExtension"].index(instrumentData["defaultVariantWithoutSfzExtension"]) |
|||
self.ui.variants_comboBox.setCurrentIndex(defaultVariantIndex) |
|||
|
|||
|
|||
|
|||
|
|||
def react_initialInstrumentList(self, data:dict): |
|||
"""For data form see docstring of instrument.py buildTree() |
|||
Summary: |
|||
libraryid : dict -> |
|||
instrumentid : metadatadict |
|||
|
|||
Dict-Keys are always the same. Some always have data, some can be empty. |
|||
|
|||
We receive this once at program start and build our permanent GUI widgets from it. |
|||
The additional status update callback for dynamic data is handled in |
|||
self.react_instrumentStatusChanged |
|||
""" |
|||
self.engineData = data |
|||
|
|||
|
Loading…
Reference in new issue