You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
578 lines
25 KiB
578 lines
25 KiB
#! /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 ),
|
|
|
|
Laborejo2 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
|
|
from sys import maxsize
|
|
|
|
#Third party
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
translate = QtCore.QCoreApplication.translate
|
|
|
|
#Template
|
|
import template.engine.pitch as pitch
|
|
from template.qtgui.helper import QHLine
|
|
from template.qtgui.submenus import *
|
|
|
|
#Our own files
|
|
import engine.api as api
|
|
from engine.midiinput.stepmidiinput import stepMidiInput #singleton instance. Don't allow midi input when submenus are open.
|
|
from .constantsAndConfigs import constantsAndConfigs
|
|
from .designer.tickWidget import Ui_tickWidget
|
|
|
|
#Wrap Submenu call to deactivate midi input while showing the menu.
|
|
orgCall = Submenu.__call__
|
|
def wrapCallDeactivateMidIn(self):
|
|
remember = stepMidiInput.midiInIsActive
|
|
stepMidiInput.setMidiInputActive(False)
|
|
orgCall(self)
|
|
stepMidiInput.setMidiInputActive(remember)
|
|
Submenu.__call__ = wrapCallDeactivateMidIn
|
|
|
|
class CombinedTickWidget(QtWidgets.QFrame):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.setFrameShape(QtWidgets.QFrame.Box)
|
|
self.setFrameShadow(QtWidgets.QFrame.Sunken)
|
|
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self)
|
|
self.horizontalLayout_3.setContentsMargins(3, 0, 3, 0)
|
|
self.horizontalLayout_3.setSpacing(0)
|
|
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
|
self.upbeatSpinBox = QtWidgets.QSpinBox(self)
|
|
self.upbeatSpinBox.setPrefix("")
|
|
self.upbeatSpinBox.setMinimum(0)
|
|
self.upbeatSpinBox.setMaximum(999999999)
|
|
self.upbeatSpinBox.setObjectName("upbeatSpinBox")
|
|
self.horizontalLayout_3.addWidget(self.upbeatSpinBox)
|
|
self.callTickWidget = QtWidgets.QPushButton(self)
|
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
|
sizePolicy.setHorizontalStretch(0)
|
|
sizePolicy.setVerticalStretch(0)
|
|
sizePolicy.setHeightForWidth(self.callTickWidget.sizePolicy().hasHeightForWidth())
|
|
self.callTickWidget.setSizePolicy(sizePolicy)
|
|
self.callTickWidget.setMaximumSize(QtCore.QSize(25, 16777215))
|
|
self.callTickWidget.setFlat(False)
|
|
self.callTickWidget.setObjectName("callTickWidget")
|
|
self.callTickWidget.setText("𝅘𝅥𝅮 ")
|
|
|
|
self.horizontalLayout_3.addWidget(self.callTickWidget)
|
|
self.callTickWidget.clicked.connect(self.callClickWidgetForUpbeat)
|
|
|
|
self.setFocusPolicy(0) #no focus
|
|
self.callTickWidget.setFocusPolicy(0) #no focus
|
|
|
|
self.valueChanged = self.upbeatSpinBox.valueChanged
|
|
|
|
def setMinimum(self, value):
|
|
self.upbeatSpinBox.setMinimum(value)
|
|
|
|
def setMaximum(self, value):
|
|
self.upbeatSpinBox.setMaximum(value)
|
|
|
|
def setValue(self, value):
|
|
self.upbeatSpinBox.setValue(value)
|
|
|
|
def value(self):
|
|
"""Make this widget behave like a spinbox signal"""
|
|
return self.upbeatSpinBox.value()
|
|
|
|
def callClickWidgetForUpbeat(self):
|
|
dialog = TickWidget(self, initValue = self.upbeatSpinBox.value())
|
|
self.upbeatSpinBox.setValue(dialog.ui.ticks.value())
|
|
|
|
class TickWidget(QtWidgets.QDialog):
|
|
def __init__(self, mainWindow, initValue = 0):
|
|
super().__init__(mainWindow)
|
|
|
|
#Set up the user interface from Designer.
|
|
self.ui = Ui_tickWidget()
|
|
self.ui.setupUi(self)
|
|
|
|
#self.ui.ticks.setValue(initValue)
|
|
self.ui.ticks.setValue(0) #TODO: easier to drawLabel this way. change back to given value when drawLabel is autogenerated and does not work by keeping track anymore.
|
|
|
|
self.ui.ok.clicked.connect(lambda: self.done(True))
|
|
self.ui.cancel.clicked.connect(lambda: self.done(False))
|
|
self.ui.reset.clicked.connect(self.reset)
|
|
|
|
self.ui.durationLabel.setText("")
|
|
|
|
self.clickedSoFar = [] #keep track
|
|
|
|
self.ui.D1.clicked.connect(lambda: self.addDuration(api.D1))
|
|
self.ui.D2.clicked.connect(lambda: self.addDuration(api.D2))
|
|
self.ui.D4.clicked.connect(lambda: self.addDuration(api.D4))
|
|
self.ui.D8.clicked.connect(lambda: self.addDuration(api.D8))
|
|
self.ui.D16.clicked.connect(lambda: self.addDuration(api.D16))
|
|
|
|
self.ui.D32.clicked.connect(lambda: self.addDuration(api.D32))
|
|
self.ui.D64.clicked.connect(lambda: self.addDuration(api.D64))
|
|
self.ui.D128.clicked.connect(lambda: self.addDuration(api.D128))
|
|
self.ui.DB.clicked.connect(lambda: self.addDuration(api.DB))
|
|
self.ui.DL.clicked.connect(lambda: self.addDuration(api.DL))
|
|
|
|
self.ui.ticks.valueChanged.connect(self.drawLabel)
|
|
|
|
self.exec() #blocks until the dialog gets closed
|
|
|
|
#TODO: better key handling. Esc in the ticks field should not close the dialog but return the keyboard focus to the durations
|
|
|
|
def reset(self):
|
|
self.ui.ticks.setValue(0)
|
|
self.clickedSoFar = []
|
|
self.ui.durationLabel.setText("")
|
|
|
|
def addDuration(self, duration):
|
|
self.clickedSoFar.append(duration)
|
|
nowTicks = self.ui.ticks.value()
|
|
self.ui.ticks.setValue(nowTicks + duration)
|
|
|
|
def drawLabel(self):
|
|
#TODO: with nice partitions of real note icons.
|
|
#Error handling. A too complex or wrong duration (off by one, not equal to a partition etc.) blocks the "OK" button. No, just gives a warning.
|
|
#backendDurationInstance = api.items.Duration.createByGuessing(self.ui.ticks.value())
|
|
#text = backendDurationInstance.lilypond()
|
|
|
|
text = []
|
|
for duration, symbol in reversed(sorted(constantsAndConfigs.realNoteDisplay.items())):
|
|
times = self.clickedSoFar.count(duration)
|
|
if times:
|
|
part = str(times) + "x" + symbol
|
|
text.append(part)
|
|
|
|
self.ui.durationLabel.setText(" + ".join(text))
|
|
|
|
class SecondaryClefMenu(Submenu):
|
|
|
|
clefs = [(translate("submenus", "[1] Treble"), lambda: api.insertClef("treble")),
|
|
(translate("submenus", "[2] Bass"), lambda: api.insertClef("bass")),
|
|
(translate("submenus", "[3] Alto"), lambda: api.insertClef("alto")),
|
|
(translate("submenus", "[4] Drum"), lambda: api.insertClef("percussion")),
|
|
(translate("submenus", "[5] Treble ^8"), lambda: api.insertClef("treble^8")),
|
|
(translate("submenus", "[6] Treble _8"), lambda: api.insertClef("treble_8")),
|
|
(translate("submenus", "[7] Bass _8 (MIDI Drums)"), lambda: api.insertClef("bass_8")),
|
|
]
|
|
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, translate("submenus", "choose a clef"), hasOkCancelButtons=2)
|
|
|
|
for number, (prettyname, function) in enumerate(SecondaryClefMenu.clefs):
|
|
button = QtWidgets.QPushButton(prettyname)
|
|
button.setShortcut(QtGui.QKeySequence(str(number+1)))
|
|
self.layout.addWidget(button)
|
|
button.clicked.connect(function)
|
|
button.clicked.connect(self.done)
|
|
|
|
class SecondarySplitMenu(Submenu):
|
|
|
|
splits = [("[2]", lambda: api.split(2)),
|
|
("[3]", lambda: api.split(3)),
|
|
("[4]", lambda: api.split(4)),
|
|
("[5]", lambda: api.split(5)),
|
|
("[6]", lambda: api.split(6)),
|
|
("[7]", lambda: api.split(7)),
|
|
("[8]", lambda: api.split(8)),
|
|
("[9]", lambda: api.split(9)),
|
|
]
|
|
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, translate("submenus", "split chord in"), hasOkCancelButtons=2)
|
|
|
|
for number, (prettyname, function) in enumerate(SecondarySplitMenu.splits):
|
|
button = QtWidgets.QPushButton(prettyname)
|
|
button.setShortcut(QtGui.QKeySequence(str(number+2))) #+1 for enumerate from 0, +2 we start at 2.
|
|
self.layout.addWidget(button)
|
|
button.clicked.connect(function)
|
|
button.clicked.connect(self.done)
|
|
|
|
class SecondaryKeySignatureMenu(Submenu):
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, translate("submenus", "root note is the cursor position"), hasOkCancelButtons=2)
|
|
l = [("[{}] {}".format(num+1, modeString.title()), lambda r, modeString=modeString: api.insertCursorCommonKeySignature(modeString)) for num, modeString in enumerate(api.commonKeySignaturesAsList())]
|
|
|
|
for number, (prettyname, function) in enumerate(l):
|
|
button = QtWidgets.QPushButton(prettyname)
|
|
button.setShortcut(QtGui.QKeySequence(str(number+1)))
|
|
self.layout.addWidget(button)
|
|
button.clicked.connect(function)
|
|
button.clicked.connect(self.done)
|
|
|
|
def __call__(self):
|
|
self.dynamicLabel.setText("Note: " + api.getCursorSimpleLyNote())
|
|
super().__call__()
|
|
|
|
|
|
|
|
class SecondaryDynamicsMenu(Submenu):
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, translate("submenus", "choose a dynamic"), hasOkCancelButtons=2)
|
|
button = QtWidgets.QPushButton(translate("submenus", "[r] Ramp"))
|
|
button.setShortcut(QtGui.QKeySequence("r"))
|
|
self.layout.addWidget(button)
|
|
button.clicked.connect(api.insertDynamicRamp)
|
|
button.clicked.connect(self.done)
|
|
|
|
l = [("[{}] {}".format(num+1, keyword), lambda r, keyword=keyword: api.insertDynamicSignature(keyword)) for num, keyword in enumerate(constantsAndConfigs.dynamics)]
|
|
|
|
for number, (prettyname, function) in enumerate(l):
|
|
button = QtWidgets.QPushButton(prettyname)
|
|
button.setShortcut(QtGui.QKeySequence(str(number+1)))
|
|
self.layout.addWidget(button)
|
|
button.clicked.connect(function)
|
|
button.clicked.connect(self.done)
|
|
|
|
class SecondaryMetricalInstructionMenu(Submenu):
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, translate("submenus", "choose a metrical instruction"), hasOkCancelButtons=2)
|
|
|
|
l = [("[{}] {}".format(num+1, modeString), lambda r, modeString=modeString: api.insertCommonMetricalInstrucions(modeString)) for num, modeString in enumerate(api.commonMetricalInstructionsAsList())]
|
|
|
|
for number, (prettyname, function) in enumerate(l):
|
|
button = QtWidgets.QPushButton(prettyname)
|
|
button.setShortcut(QtGui.QKeySequence(str(number+1)))
|
|
self.layout.addWidget(button)
|
|
button.clicked.connect(function)
|
|
button.clicked.connect(self.done)
|
|
|
|
class SecondaryTempoChangeMenu(Submenu):
|
|
"""A single tempo change where the user can decide which reference unit and how many of them
|
|
per minute.
|
|
Works as "edit tempo point" when there is already a point at this time position.
|
|
This would be the case anyway thanks to backend-behaviour but the gui has the opportunity to
|
|
present the current values as a base for editing"""
|
|
|
|
|
|
def __init__(self, mainWindow, staticExportTempoItem = None):
|
|
super().__init__(mainWindow, translate("submenus", "choose units per minute, reference note, graph type"), hasOkCancelButtons=True)
|
|
|
|
self.mainWindow = mainWindow
|
|
self.staticExportTempoItem = staticExportTempoItem
|
|
|
|
tickindex, unitsPerMinute, referenceTicks, graphType = self.getCurrentValues() #takes self.staticExportTempoItem into account
|
|
|
|
self.unitbox = QtWidgets.QSpinBox()
|
|
self.unitbox.setMinimum(1)
|
|
self.unitbox.setMaximum(999)
|
|
self.unitbox.setValue(unitsPerMinute)
|
|
self.layout.addWidget(self.unitbox)
|
|
|
|
self.referenceList = QtWidgets.QComboBox()
|
|
self.referenceList.addItems(constantsAndConfigs.prettyExtendedRhythmsStrings)
|
|
self.referenceList.setCurrentIndex(constantsAndConfigs.prettyExtendedRhythmsValues.index(referenceTicks))
|
|
self.layout.addWidget(self.referenceList)
|
|
|
|
self.interpolationList = QtWidgets.QComboBox()
|
|
l = api.getListOfGraphInterpolationTypesAsStrings()
|
|
self.interpolationList.addItems(l)
|
|
self.interpolationList.setCurrentIndex(l.index(graphType))
|
|
self.layout.addWidget(self.interpolationList)
|
|
|
|
self.__call__()
|
|
|
|
def process(self):
|
|
"""It says 'insert' but the backend is a dict. Changes are simply made by overwriting the
|
|
whole thing and the backend sends new data to draw to the GUI"""
|
|
tickindex, unitsPerMinute, referenceTicks, graphType = self.getCurrentValues()
|
|
newReferenceTicks = constantsAndConfigs.prettyExtendedRhythmsValues[self.referenceList.currentIndex()]
|
|
graphType = api.getListOfGraphInterpolationTypesAsStrings()[self.interpolationList.currentIndex()]
|
|
api.insertTempoItemAtAbsolutePosition(tickindex, self.unitbox.value(), newReferenceTicks, graphType)
|
|
self.done(True)
|
|
|
|
def getCurrentValues(self):
|
|
"""Get the current values from the note-editing backend cursor"""
|
|
if self.staticExportTempoItem:
|
|
return self.staticExportTempoItem["position"], self.staticExportTempoItem["unitsPerMinute"], self.staticExportTempoItem["referenceTicks"], self.staticExportTempoItem["graphType"],
|
|
else:
|
|
assert self.mainWindow.scoreView.scoreScene.cursor.cursorExportObject
|
|
return self.mainWindow.scoreView.scoreScene.cursor.cursorExportObject["tickindex"], self.mainWindow.scoreView.scoreScene.cursor.cursorExportObject["tempoUnitsPerMinute"], self.mainWindow.scoreView.scoreScene.cursor.cursorExportObject["tempoReferenceTicks"], self.mainWindow.scoreView.scoreScene.cursor.cursorExportObject["tempoGraphType"],
|
|
|
|
class SecondaryTemporaryTempoChangeMenu(Submenu):
|
|
"""Essentially: What kind of fermata effect do you want?"""
|
|
|
|
lastCustomValue = 0.42
|
|
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, translate("submenus", "[enter] to use value"), hasOkCancelButtons=True)
|
|
|
|
self.spinbox = QtWidgets.QDoubleSpinBox()
|
|
self.spinbox.setValue(SecondaryTemporaryTempoChangeMenu.lastCustomValue)
|
|
self.spinbox.setDecimals(2)
|
|
self.spinbox.setMinimum(0.01)
|
|
self.spinbox.setSingleStep(0.01)
|
|
self.layout.addWidget(self.spinbox)
|
|
|
|
def process(self):
|
|
v = round(self.spinbox.value(), 2)
|
|
SecondaryTemporaryTempoChangeMenu.lastCustomValue = v
|
|
api.insertTempoChangeDuringDuration(v)
|
|
self.done(True)
|
|
|
|
class BlockPropertiesEdit(Submenu):
|
|
def __init__(self, mainWindow, staticExportItem):
|
|
super().__init__(mainWindow, "", hasOkCancelButtons=True)
|
|
|
|
self.mainWindow = mainWindow
|
|
self.staticExportItem = staticExportItem
|
|
|
|
self.layout.insertRow(0, QtWidgets.QLabel(translate("submenus", "edit block #{}").format(staticExportItem["id"])))
|
|
|
|
self.name = QtWidgets.QLineEdit(self.staticExportItem["name"])
|
|
self.name.selectAll()
|
|
self.layout.addRow(translate("submenus", "name"), self.name)
|
|
|
|
#self.minimumInTicks = QtWidgets.QSpinBox()
|
|
self.minimumInTicks = CombinedTickWidget()
|
|
self.minimumInTicks.setValue(self.staticExportItem["minimumInTicks"])
|
|
self.layout.addRow(translate("submenus", "minimum in ticks"), self.minimumInTicks)
|
|
|
|
self.__call__()
|
|
|
|
def process(self):
|
|
newParametersDict = {
|
|
"minimumInTicks":self.minimumInTicks.value(),
|
|
"name":self.name.text(),
|
|
}
|
|
api.changeBlock(self.staticExportItem["id"], newParametersDict)
|
|
self.done(True)
|
|
|
|
class TempoBlockPropertiesEdit(Submenu):
|
|
def __init__(self, mainWindow, staticExportItem):
|
|
super().__init__(mainWindow, "", hasOkCancelButtons=True)
|
|
|
|
self.mainWindow = mainWindow
|
|
self.staticExportItem = staticExportItem
|
|
|
|
self.layout.insertRow(0, QtWidgets.QLabel(translate("submenus", "edit block #{}").format(staticExportItem["id"])))
|
|
|
|
self.name = QtWidgets.QLineEdit(self.staticExportItem["name"])
|
|
self.name.selectAll()
|
|
self.layout.addRow(translate("submenus", "name"), self.name)
|
|
|
|
self.duration = CombinedTickWidget()
|
|
self.duration.setValue(self.staticExportItem["duration"])
|
|
self.layout.addRow(translate("submenus", "duration in ticks"), self.duration)
|
|
|
|
self.__call__()
|
|
|
|
def process(self):
|
|
newParametersDict = {
|
|
"duration":self.duration.value(),
|
|
"name":self.name.text(),
|
|
}
|
|
api.changeTempoBlock(self.staticExportItem["id"], newParametersDict)
|
|
self.done(True)
|
|
|
|
class TransposeMenu(Submenu):
|
|
def __init__(self, mainWindow, what):
|
|
super().__init__(mainWindow, translate("submenus", "Transpose {}").format(what.title()), hasOkCancelButtons=True)
|
|
assert what in ("item", "score")
|
|
self.what = what
|
|
|
|
self.layout.insertRow(0, QtWidgets.QLabel(translate("submenus", "Construct Interval from relative distance")))
|
|
|
|
self.fromNote = QtWidgets.QComboBox()
|
|
self.fromNote.addItems(pitch.sortedNoteNameList)
|
|
|
|
self.fromNote.setCurrentIndex(pitch.sortedNoteNameList.index("c'"))
|
|
self.layout.addRow("from", self.fromNote)
|
|
|
|
self.to = QtWidgets.QComboBox()
|
|
self.to.addItems(pitch.sortedNoteNameList)
|
|
self.to.setCurrentIndex(pitch.sortedNoteNameList.index("c'"))
|
|
self.layout.addRow("to", self.to)
|
|
self.__call__()
|
|
|
|
|
|
def process(self):
|
|
fromPitch = pitch.ly2pitch[self.fromNote.currentText()]
|
|
toPitch = pitch.ly2pitch[self.to.currentText()]
|
|
|
|
if self.what == "item":
|
|
api.transpose(fromPitch, toPitch) #item on cursor position
|
|
elif self.what == "score":
|
|
api.transposeScore(fromPitch, toPitch)
|
|
self.done(True)
|
|
|
|
class SecondaryProperties(Submenu):
|
|
def __init__(self, mainWindow):
|
|
"""Directly edits the backend score meta data. There is no api and no callbacks"""
|
|
super().__init__(mainWindow, translate("submenus", "Meta Data"), hasOkCancelButtons=True)
|
|
|
|
dictionary = api.getMetadata() #TOOD: untranslated to keep relation to lilypond?
|
|
|
|
test = set(type(key) for key in dictionary.keys())
|
|
assert len(test) == 1
|
|
assert list(test)[0] == str
|
|
|
|
self.widgets = {key:self.makeValueWidget(value) for key, value in dictionary.items()}
|
|
importantKeys = ("title", "composer", "instrument", "copyright")
|
|
|
|
#Draw important metadata widgets first
|
|
for k in importantKeys:
|
|
self.layout.addRow(k.title(), self.widgets[k])
|
|
|
|
self.layout.addRow(QHLine())
|
|
|
|
#Then the rest in alphabetical order
|
|
for key, widget in sorted(self.widgets.items()):
|
|
if not key in importantKeys:
|
|
self.layout.addRow(key.title(), widget)
|
|
|
|
self.__call__()
|
|
|
|
def makeValueWidget(self, value):
|
|
types = {
|
|
str : QtWidgets.QLineEdit,
|
|
int : QtWidgets.QSpinBox,
|
|
float : QtWidgets.QDoubleSpinBox,
|
|
}
|
|
typ = type(value)
|
|
widget = types[typ]()
|
|
|
|
if typ == str:
|
|
widget.setText(value)
|
|
elif typ == int or typ == float:
|
|
widget.setValue(value)
|
|
|
|
return widget
|
|
|
|
def getValueFromWidget(self, widget):
|
|
typ = type(widget)
|
|
if typ == QtWidgets.QLineEdit:
|
|
return widget.text()
|
|
elif typ == QtWidgets.QSpinBox or typ == QtWidgets.QDoubleSpinBox:
|
|
return widget.value()
|
|
|
|
def process(self):
|
|
api.setMetadata({key:self.getValueFromWidget(widget) for key, widget in self.widgets.items()})
|
|
self.done(True)
|
|
#Instance gets killed afterwards. No need to save the new values.
|
|
|
|
class SecondaryProgramChangeMenu(Submenu):
|
|
lastProgramValue = 0
|
|
lastMsbValue = 0
|
|
lastLsbValue = 0
|
|
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, translate("submenus", "Instrument Change"), hasOkCancelButtons=True)
|
|
|
|
self.program = QtWidgets.QSpinBox()
|
|
self.program.setValue(type(self).lastProgramValue)
|
|
self.msb = QtWidgets.QSpinBox()
|
|
self.msb.setValue(type(self).lastMsbValue)
|
|
self.lsb = QtWidgets.QSpinBox()
|
|
self.lsb.setValue(type(self).lastLsbValue)
|
|
self.shortInstrumentName = QtWidgets.QLineEdit()
|
|
|
|
for label, spinbox in ((translate("submenus", "Program"), self.program), (translate("submenus", "Bank MSB"), self.msb), (translate("submenus", "Bank LSB"), self.lsb)):
|
|
spinbox.setMinimum(0)
|
|
spinbox.setMaximum(127)
|
|
spinbox.setSingleStep(1)
|
|
self.layout.addRow(label, spinbox)
|
|
|
|
self.layout.addRow(translate("submenus", "Short Name"), self.shortInstrumentName)
|
|
|
|
def process(self):
|
|
program = self.program.value()
|
|
type(self).lastProgramValue = program
|
|
msb = self.msb.value()
|
|
type(self).lastMsbValue = msb
|
|
lsb = self.lsb.value()
|
|
type(self).lastLsbValue = lsb
|
|
|
|
api.instrumentChange(program, msb, lsb, self.shortInstrumentName.text(), )
|
|
self.done(True)
|
|
|
|
class SecondaryChannelChangeMenu(Submenu):
|
|
lastCustomValue = 0
|
|
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, translate("submenus", "Channel Change 1-16. [enter] to use value"), hasOkCancelButtons=True)
|
|
|
|
self.spinbox = QtWidgets.QSpinBox()
|
|
self.spinbox.setValue(type(self).lastCustomValue)
|
|
self.spinbox.setMinimum(1)
|
|
self.spinbox.setMaximum(16)
|
|
self.spinbox.setSingleStep(1)
|
|
self.layout.addRow(translate("submenus", "Channel"), self.spinbox)
|
|
self.name = QtWidgets.QLineEdit()
|
|
self.layout.addRow(translate("submenus", "Text"), self.name)
|
|
|
|
def process(self):
|
|
v = self.spinbox.value()
|
|
type(self).lastCustomValue = v
|
|
api.channelChange(v-1, self.name.text())
|
|
self.done(True)
|
|
|
|
class GridRhytmEdit(Submenu):
|
|
def __init__(self, mainWindow):
|
|
super().__init__(mainWindow, "", hasOkCancelButtons=True)
|
|
|
|
self.mainWindow = mainWindow
|
|
|
|
self.layout.insertRow(0, QtWidgets.QLabel(translate("submenus", "Edit Grid")))
|
|
self.duration = CombinedTickWidget()
|
|
self.duration.setValue(constantsAndConfigs.gridRhythm)
|
|
self.layout.addRow(translate("submenus", "duration in ticks"), self.duration)
|
|
|
|
self.opacity = QtWidgets.QSlider(QtCore.Qt.Horizontal)
|
|
self.opacity.setMinimum(0)
|
|
self.opacity.setMaximum(50)
|
|
self.opacityLabel = QtWidgets.QLabel(translate("submenus", "opacity: {}%").format(int(constantsAndConfigs.gridOpacity * 100)))
|
|
self.layout.addRow(self.opacityLabel, self.opacity)
|
|
self.opacity.valueChanged.connect(lambda: self.opacityLabel.setText(translate("submenus", "opacity: {}%").format(self.opacity.value())))
|
|
self.opacity.setValue(int(constantsAndConfigs.gridOpacity * 100))
|
|
self.opacity.valueChanged.connect(lambda: self.mainWindow.scoreView.scoreScene.grid.setOpacity(self.opacity.value() / 100)) #only react to changes after the initial value was set.
|
|
self.__call__()
|
|
|
|
def process(self):
|
|
constantsAndConfigs.gridRhythm = self.duration.value()
|
|
constantsAndConfigs.gridOpacity = self.opacity.value() / 100
|
|
api.session.guiSharedDataToSave["grid_opacity"] = constantsAndConfigs.gridOpacity
|
|
api.session.guiSharedDataToSave["grid_rhythm"] = constantsAndConfigs.gridRhythm
|
|
self.mainWindow.scoreView.scoreScene.grid.redrawTickGrid() #opacity was already set live, but finally it will be used here again.
|
|
self.done(True)
|
|
|
|
def abortHandler(self):
|
|
self.mainWindow.scoreView.scoreScene.grid.setOpacity(constantsAndConfigs.gridOpacity) #reset to initial value and undo the live preview
|
|
|
|
|
|
#Normal Functions
|
|
############
|
|
|
|
def pedalNoteChooser(mainWindow):
|
|
try:
|
|
constantsAndConfigs.realNotesStrings[constantsAndConfigs.realNotesValues.index(constantsAndConfigs.gridRhythm)+1]
|
|
rhythmString = QtWidgets.QInputDialog.getItem(mainWindow, translate("submenus", "Insert Pedal Notes"), translate("submenus", "Use duration as base"), constantsAndConfigs.realNotesStrings, constantsAndConfigs.realNotesValues.index(constantsAndConfigs.gridRhythm)+1, False)
|
|
except IndexError:
|
|
rhythmString = QtWidgets.QInputDialog.getItem(mainWindow, translate("submenus", "Insert Pedal Notes"), translate("submenus", "Use duration as base"), constantsAndConfigs.realNotesStrings, constantsAndConfigs.realNotesValues.index(constantsAndConfigs.gridRhythm), False)
|
|
|
|
|
|
if rhythmString[1]: #bool. Canceled?
|
|
for baseDuration, v in constantsAndConfigs.commonNotes:
|
|
if v == rhythmString[0]:
|
|
api.pedalNotes(baseDuration)
|
|
|
|
def forwardText(mainWindow, title, function):
|
|
text, status = QtWidgets.QInputDialog.getText(mainWindow, title, title)
|
|
if status:
|
|
function(text)
|
|
|