#! /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 . """ 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, makeValueWidget, getValueFromWidget 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, mainWindow): super().__init__(mainWindow) self.mainWindow = mainWindow 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(int(value)) def setMaximum(self, value): self.upbeatSpinBox.setMaximum(int(value)) def setValue(self, value): self.upbeatSpinBox.setValue(int(value)) def value(self): """Make this widget behave like a spinbox signal""" return self.upbeatSpinBox.value() def callClickWidgetForUpbeat(self): dialog = TickWidget(self.mainWindow, initValue = self.upbeatSpinBox.value()) self.upbeatSpinBox.setValue(int(dialog.ui.ticks.value())) class TickWidget(QtWidgets.QDialog): def __init__(self, parentWindow, initValue = 0): super().__init__() #Set up the user interface from Designer. self.ui = Ui_tickWidget() self.ui.setupUi(self) #This without init(parentWindow) will result in a nicely positioned window, that is not accesible :( """ self.setParent(parentWindow) self.setAutoFillBackground(True) self.setModal(False) self.raise_() self.activateWindow() """ #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. This is purely cosmetical to remember the click history. self.multipliers = [] #keep track. This is purely cosmetical to remember the click history. 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.x2.clicked.connect(lambda: self.multiply(2)) self.ui.x3.clicked.connect(lambda: self.multiply(3)) self.ui.x5.clicked.connect(lambda: self.multiply(5)) self.ui.x7.clicked.connect(lambda: self.multiply(7)) 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.multipliers = [] self.ui.durationLabel.setText("") def addDuration(self, duration:int): self.clickedSoFar.append(duration) nowTicks = self.ui.ticks.value() self.ui.ticks.setValue(nowTicks + duration) def multiply(self, times:int): self.multipliers.append("x" + str(times)) nowTicks = self.ui.ticks.value() self.ui.ticks.setValue(nowTicks * times) def drawLabel(self): """This is purely cosmetical""" #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) + " ) " + " ".join(self.multipliers)) 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 SecondaryDuplicateMenu(Submenu): duplicates = [("[1]", lambda: api.duplicate(1)), ("[2]", lambda: api.duplicate(2)), ("[3]", lambda: api.duplicate(3)), ("[4]", lambda: api.duplicate(4)), ("[5]", lambda: api.duplicate(5)), ("[6]", lambda: api.duplicate(6)), ("[7]", lambda: api.duplicate(7)), ("[8]", lambda: api.duplicate(8)), ("[9]", lambda: api.duplicate(9)), ] def __init__(self, mainWindow): super().__init__(mainWindow, translate("submenus", "duplicate how often?"), hasOkCancelButtons=2) for number, (prettyname, function) in enumerate(SecondaryDuplicateMenu.duplicates): button = QtWidgets.QPushButton(prettyname) button.setShortcut(QtGui.QKeySequence(str(number+1))) #+1 for enumerate from 0, +1 we start at 1. self.layout.addWidget(button) button.clicked.connect(function) button.clicked.connect(self.done) class SecondaryKeySignatureMenu(Submenu): def __init__(self, mainWindow): """Init is only called once per program, during startup""" 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, setInitialInsteadCursorInsert:bool=False): super().__init__(mainWindow, translate("submenus", "choose a metrical instruction"), hasOkCancelButtons=2) if setInitialInsteadCursorInsert: #different api function l = [("[{}] {}".format(num+1, modeString), lambda r, modeString=modeString: api.insertCommonMetricalInstrucions(modeString, setInitialInsteadCursorInsert=True)) for num, modeString in enumerate(api.commonMetricalInstructionsAsList())] else: 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\nand an optional description like 'Allegro'"), hasOkCancelButtons=True) self.mainWindow = mainWindow self.staticExportTempoItem = staticExportTempoItem tickindex, unitsPerMinute, referenceTicks, graphType, description = 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.description = QtWidgets.QLineEdit(description) self.layout.addWidget(self.description) 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, description = self.getCurrentValues() newReferenceTicks = constantsAndConfigs.prettyExtendedRhythmsValues[self.referenceList.currentIndex()] graphType = api.getListOfGraphInterpolationTypesAsStrings()[self.interpolationList.currentIndex()] description = self.description.text() api.insertTempoItemAtAbsolutePosition(tickindex, self.unitbox.value(), newReferenceTicks, graphType, description) 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"], self.staticExportTempoItem["lilypondParameters"]["tempo"] 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"], "" #empty string is the description 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(mainWindow) self.minimumInTicks.setValue(self.staticExportItem["minimumInTicks"]) self.layout.addRow(translate("submenus", "minimum in ticks"), self.minimumInTicks) self.setMinimumToCurrentButton = QtWidgets.QPushButton(translate("submenus","Set min. to current")) self.setMinimumToCurrentButton.clicked.connect(self.setMinimumToCurrent) self.layout.addRow(None, self.setMinimumToCurrentButton) self.__call__() def setMinimumToCurrent(self): self.minimumInTicks.setValue(self.staticExportItem["completeDuration"]) 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(mainWindow) 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): """Lilypond Settings and Properties. Directly edits the backend score meta data. There is no api and no callbacks""" def __init__(self, mainWindow): super().__init__(mainWindow, translate("submenus", "Lilypond Properties and Metada"), hasOkCancelButtons=True) dictionary = api.getMetadata() #Do not confuse with template/config METADATA. This is Lilypond title, composer etc. #The dictionary is mutable. We edit in place. #TODO: This might bite us in the future. But maybe it will not... test = set(type(key) for key in dictionary.keys()) assert len(test) == 1 assert list(test)[0] == str self.widgets = {key: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.dynamicLabel.setText("") self.__call__() def process(self): api.setMetadata({key: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(mainWindow) 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 class SecondaryMultimeasureRestMenu(Submenu): lastCustomValue = 0 def __init__(self, mainWindow): super().__init__(mainWindow, translate("submenus", "Rest for how many measures?\n\n(Only works if there is a metrical instruction.)"), hasOkCancelButtons=True) self.spinbox = QtWidgets.QSpinBox() self.spinbox.setValue(type(self).lastCustomValue) self.spinbox.setMinimum(1) self.spinbox.setMaximum(9999) self.spinbox.setSingleStep(1) self.layout.addRow(translate("submenus", "Measures"), 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.insertMultiMeasureRest(v) self.done(True) #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)