#! /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 Modules import os.path from pathlib import Path #Third Party Modules from PyQt5 import QtCore, QtGui, QtWidgets translate = QtCore.QCoreApplication.translate #Template Modules from template.qtgui.midiinquickwidget import QuickMidiInputComboController #Our modules import engine.api as api from engine.midiinput.stepmidiinput import stepMidiInput #singleton instance from .constantsAndConfigs import constantsAndConfigs from .submenus import SecondaryClefMenu, SecondaryKeySignatureMenu, SecondaryDynamicsMenu, SecondaryMetricalInstructionMenu, SecondaryTempoChangeMenu, SecondaryTemporaryTempoChangeMenu, SecondarySplitMenu, TransposeMenu, pedalNoteChooser, SecondaryProperties, SecondaryProgramChangeMenu, SecondaryChannelChangeMenu, ChooseOne, forwardText class ModalKeys(object): def __init__(self): pass def keyRest(self): if stepMidiInput.midiInIsActive: baseDuration = api.session.data.cursor.prevailingBaseDuration api.insertRest(baseDuration) #else: deactivated. def key1(self): if stepMidiInput.midiInIsActive: api.prevailing1() else: api.insert1() def key2(self): if stepMidiInput.midiInIsActive: api.prevailing2() else: api.insert2() def key3(self): if stepMidiInput.midiInIsActive: api.prevailing4() else: api.insert4() def key4(self): if stepMidiInput.midiInIsActive: api.prevailing8() else: api.insert8() def key5(self): if stepMidiInput.midiInIsActive: api.prevailing16() else: api.insert16() def key6(self): if stepMidiInput.midiInIsActive: api.prevailing32() else: api.insert32() def key7(self): if stepMidiInput.midiInIsActive: api.prevailingBrevis() else: api.insertBrevis() def key8(self): if stepMidiInput.midiInIsActive: api.prevailingLonga() else: api.insertLonga() def key9(self): pass def key0(self): pass class MenuActionDatabase(object): """There are different edit and view modes in Laborejo-Qt: Main Program ScoreView NoteEdit Noteheads Rectangles BlockEdit Transparent Block Handles CCEdit CC 0 CC 1 ... CC 127 DataEditor Each level has shorcuts associated with them which depend on the context. To implement those we use the qt shortcut contexts. See QAction docs. Additionaly there is midi-in and the modal number keys, which are connected to each other. Without midi in the modal keys insert notes, in midi mode they choose the duration for the next midi key. """ def __init__(self, mainWindow): self.mainWindow = mainWindow self.modalKeys = ModalKeys() modes = QtWidgets.QActionGroup(self.mainWindow) modes.addAction(self.mainWindow.ui.actionNotation_Mode) modes.addAction(self.mainWindow.ui.actionBlock_Mode) modes.addAction(self.mainWindow.ui.actionCC_Mode) self.mainWindow.ui.actionNotation_Mode.setChecked(True) self.rememberMidi = stepMidiInput.midiInIsActive api.callbacks.historyChanged.append(self.renameUndoRedoByHistory) api.callbacks.metronomeChanged.append(lambda v: self.mainWindow.ui.actionMetronome_Enabled.setChecked(v["enabled"])) #accompanied by a normal menu action down below self.actionsWithoutMenu = { #these are only available in Note Edit Mode, not in CC Edit Mode etc. but have no menu entry. #Of course these actions have shortcuts, they are in a menu, visible and editable in qt-Designer, which gets hidden on program start. self.mainWindow.ui.actionLeft : api.left, self.mainWindow.ui.actionRight : api.right, self.mainWindow.ui.actionSelectLeft : api.selectLeft, self.mainWindow.ui.actionSelectRight : api.selectRight, self.mainWindow.ui.actionMeasureLeft : api.measureLeft, self.mainWindow.ui.actionMeasureRight : api.measureRight, self.mainWindow.ui.actionSelectMeasureLeft : api.selectMeasureLeft, self.mainWindow.ui.actionSelectMeasureRight : api.selectMeasureRight, self.mainWindow.ui.actionBlockLeft : api.blockLeft, self.mainWindow.ui.actionBlockRight : api.blockRight, self.mainWindow.ui.actionSelectBlockLeft : api.selectBlockLeft, self.mainWindow.ui.actionSelectBlockRight : api.selectBlockRight, self.mainWindow.ui.actionHead : api.head, self.mainWindow.ui.actionTail : api.tail, self.mainWindow.ui.actionSelectHead : api.selectHead, self.mainWindow.ui.actionSelectTail : api.selectTail, self.mainWindow.ui.actionTrackUp : api.trackUp, self.mainWindow.ui.actionTrackDown : api.trackDown, self.mainWindow.ui.actionTrackFirst : api.trackFirst, self.mainWindow.ui.actionTrackLast : api.trackLast, self.mainWindow.ui.actionSelectTrackUp : api.selectTrackUp, self.mainWindow.ui.actionSelectTrackDown : api.selectTrackDown, self.mainWindow.ui.actionSelectTrackFirst : api.selectTrackFirst, self.mainWindow.ui.actionSelectTrackLast : api.selectTrackLast, self.mainWindow.ui.actionUp : api.up, self.mainWindow.ui.actionDown : api.down, self.mainWindow.ui.actionUpOctave : api.upOctave, self.mainWindow.ui.actionDownOctave : api.downOctave, self.mainWindow.ui.actionSelectUp : api.up, #these are the same as up and down. But for a good user experience they should work if shift is pressed down as well. self.mainWindow.ui.actionSelectDown : api.down, self.mainWindow.ui.actionSelectUpOctave : api.upOctave, self.mainWindow.ui.actionSelectDownOctave : api.downOctave, self.mainWindow.ui.actionSelectAllTracks : api.selectAllTracks, self.mainWindow.ui.actionSelectTrack : api.selectTrack, self.mainWindow.ui.actionSelectMeasureColumn : api.selectMeasureColumn, #CC Edit Actions. Share the same shortcuts as some of the actions above. But only one of these is enabled at any time. #Don't forget to add them to the list below: self.ccEditActions #... } self.actions = { #always available in the main window. It doesn't matter if you change to CC mode or Note Edit etc. self.mainWindow.ui.actionToggle_Notehead_Rectangles : self.mainWindow.scoreView.toggleNoteheadsRectangles, self.mainWindow.ui.actionData_Editor : self.mainWindow.toggleMainView, self.mainWindow.ui.actionProperties : lambda: SecondaryProperties(self.mainWindow), self.mainWindow.ui.actionFollow_Playhead : self.mainWindow.scoreView.toggleFollowPlayhead, #Modes Submenu self.mainWindow.ui.actionNotation_Mode : self.mainWindow.scoreView.updateMode, self.mainWindow.ui.actionBlock_Mode : self.mainWindow.scoreView.updateMode, self.mainWindow.ui.actionCC_Mode : self.mainWindow.scoreView.updateMode, self.mainWindow.ui.actionChange_Grid_Rhythm : self.mainWindow.scoreView.changeGridRhythm, self.mainWindow.ui.actionAdd_Track : api.newEmptyTrack, self.mainWindow.ui.actionDelete_Current_Track : api.deleteCurrentTrack, self.mainWindow.ui.actionUse_Current_Track_as_Metronome : api.useCurrentTrackAsMetronome, self.mainWindow.ui.actionMidi_In_is_Active : self.toggleMidiInIsActive, self.mainWindow.ui.actionZoom_In_Score_View : self.mainWindow.zoomIn, self.mainWindow.ui.actionZoom_Out_Score_View : self.mainWindow.zoomOut, self.mainWindow.ui.actionWiden_Score_View : self.mainWindow.widen, self.mainWindow.ui.actionShrink_Score_View : self.mainWindow.shrinken, self.mainWindow.ui.actionSave : api.save, self.mainWindow.ui.actionQuit : self.mainWindow.nsmClient.serverSendExitToSelf, self.mainWindow.ui.actionShow_PDF : api.showPDF, self.mainWindow.ui.actionExport_to_Ly : self.exportLy, self.mainWindow.ui.actionPlayFromBeginning : api.playFromStart, self.mainWindow.ui.actionPlayPause : api.playPause, self.mainWindow.ui.actionPlayFromEditCursor : api.playFromCursor, self.mainWindow.ui.actionPlay_from_Block : api.playFromBlockStart, self.mainWindow.ui.actionMetronome_Enabled : api.toggleMetronome, #toggle is enough. The callback makes sure all the checkboxes have the correct value. self.mainWindow.ui.actionAutoconnect_Metronome : self.mainWindow.reactToAutoconnectMixerCheckbox, } self.noteEditActions = { #these are only available in Note Edit Mode, not in CC Edit Mode etc. #all xxxEditActions are mutually exclusive. self.mainWindow.ui.actionBeam : api.toggleBeam, self.mainWindow.ui.actionRemoveBeams : api.removeBeam, self.mainWindow.ui.actionLegatoSlur : api.insertLegatoSlur, self.mainWindow.ui.actionClef : SecondaryClefMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus. self.mainWindow.ui.actionKey_Signature : SecondaryKeySignatureMenu(self.mainWindow), self.mainWindow.ui.actionDynamics : SecondaryDynamicsMenu(self.mainWindow), self.mainWindow.ui.actionMulti_Measure_Rest : lambda: api.insertMultiMeasureRest(1), self.mainWindow.ui.actionMetrical_Instruction : SecondaryMetricalInstructionMenu(self.mainWindow), self.mainWindow.ui.actionTempo_Change : lambda: SecondaryTempoChangeMenu(self.mainWindow), self.mainWindow.ui.actionDelete_Tempo_Change : api.removeCurrentTempoItem, self.mainWindow.ui.actionTemporary_Tempo_Change : SecondaryTemporaryTempoChangeMenu(self.mainWindow), self.mainWindow.ui.actionSharpen_Note : api.sharpenNote, self.mainWindow.ui.actionFlatten_Note : api.flattenNote, self.mainWindow.ui.actionStep_Up : api.stepUp, self.mainWindow.ui.actionStep_Down : api.stepDown, self.mainWindow.ui.actionOctave_Up : api.stepUpOctave, self.mainWindow.ui.actionOctave_Down : api.stepDownOctave, self.mainWindow.ui.actionTransposeChord : lambda: TransposeMenu(self.mainWindow, "item"), self.mainWindow.ui.actionVelocityMore : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.moreVelocity), self.mainWindow.ui.actionVelocityLess : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.lessVelocity), self.mainWindow.ui.actionDurationModMore : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.moreDuration), self.mainWindow.ui.actionDurationModLess : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.lessDuration), self.mainWindow.ui.actionReset_Velocity_Duration_Mod : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.resetDurationVelocity), self.mainWindow.ui.actionAugment : api.augment, self.mainWindow.ui.actionDiminish : api.diminish, self.mainWindow.ui.actionDots : api.dot, self.mainWindow.ui.actionTriplet : api.triplet, #self.mainWindow.ui.actionCustomTuplet : something with a GUI that allows any type and number of nested tuplets, #TODO self.mainWindow.ui.actionStaccato : api.staccato, self.mainWindow.ui.actionTenuto : api.tenuto, self.mainWindow.ui.actionTie : api.tie, self.mainWindow.ui.actionSplit_in_2 : lambda: api.split(2), self.mainWindow.ui.actionSplit_in_3 : lambda: api.split(3), self.mainWindow.ui.actionCustom_Split : SecondarySplitMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus. self.mainWindow.ui.actionMIDI_Channel_Plus : api.midiRelativeChannelPlus, self.mainWindow.ui.actionMIDI_Channel_Minus : api.midiRelativeChannelMinus, self.mainWindow.ui.actionMIDI_Channel_Reset : api.midiRelativeChannelReset, self.mainWindow.ui.actionAppend_Block : api.appendBlock, self.mainWindow.ui.actionSplit_Current_Block : api.splitBlock, self.mainWindow.ui.actionJoin_with_next_Block : api.joinBlock, self.mainWindow.ui.actionDuplicate : api.duplicateCurrentBlock, self.mainWindow.ui.actionCreate_Linked_Copy : api.duplicateContentLinkCurrentBlock, self.mainWindow.ui.actionUnlink_Current_Block : api.unlinkCurrentBlock, self.mainWindow.ui.actionDelete_Current_Block : api.deleteCurrentBlock, self.mainWindow.ui.actionMove_Block_to_start_of_track : api.moveCurrentBlockToStartOfTrack, self.mainWindow.ui.actionMove_Block_to_end_of_track : api.moveCurrentBlockToEndOfTrack, self.mainWindow.ui.actionTranspose_Score : lambda: TransposeMenu(self.mainWindow, "score"), self.mainWindow.ui.actionDelete_All_Empty_Blocks : api.deleteEmptyBlocks, self.mainWindow.ui.actionDelete : api.delete, self.mainWindow.ui.actionBackspace : api.backspace, self.mainWindow.ui.actionAddCursorNoteToChord : api.addCursorNoteToChord, self.mainWindow.ui.actionDeleteCursorNoteFromChord : api.deleteCursorNoteFromChord, self.mainWindow.ui.actionCut : api.cutObjects, self.mainWindow.ui.actionCopy : api.copyObjects, self.mainWindow.ui.actionPaste : api.pasteObjects, self.mainWindow.ui.actionDuplicateItem : api.duplicate, self.mainWindow.ui.actionUndo : api.undo, self.mainWindow.ui.actionRedo : api.redo, self.mainWindow.ui.actionRedrawAllTracks : api.updateCallbackAllTracks, #Toolbox #Note Generation self.mainWindow.ui.actionPedalNotes : lambda: pedalNoteChooser(self.mainWindow), self.mainWindow.ui.actionRandom_chromatic_in_clef_range: api.insertRandomChromaticInClefRange, self.mainWindow.ui.actionRandom_in_scale_in_clef_range: api.insertRandomFromScaleInClefRange, self.mainWindow.ui.actionRandom_pitch_from_clipboard: api.insertRandomFromClipboard, self.mainWindow.ui.actionRandom_in_scale_in_octave_around_cursor: api.insertRandomFromScaleHypoModeCursor, self.mainWindow.ui.actionRandom_in_scale_in_cursor_plus_octave: api.insertRandomFromScaleAuthenticModeCursor, self.mainWindow.ui.actionMirror_around_Cursor: api.mirrorAroundCursor, #Note Sorting self.mainWindow.ui.actionRandom: api.reorderShuffle, self.mainWindow.ui.actionReverse: api.reorderReverse, self.mainWindow.ui.actionAscending: api.reorderAscending, self.mainWindow.ui.actionDescending: api.reoderdDescending, #Midi self.mainWindow.ui.actionInstrument_Change: SecondaryProgramChangeMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus. self.mainWindow.ui.actionChannel_Change: SecondaryChannelChangeMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus. #Lilypond #Print and Export is in self.actions self.mainWindow.ui.actionLyBarline: ChooseOne(self.mainWindow, translate("menu", "Choose a Barline"), api.getLilypondBarlineList()), self.mainWindow.ui.actionLyRepeat: ChooseOne(self.mainWindow, translate("menu", "Choose a Repeat"), api.getLilypondRepeatList()), self.mainWindow.ui.actionLyFree_Instruction: lambda: forwardText(self.mainWindow, translate("menu", "Enter Instruction"), api.lilypondText), self.mainWindow.ui.actionLyMarkAbove: lambda: forwardText(self.mainWindow, translate("menu", "Text above the Staff, will be attached to previous(!) item."), lambda tx: api.lilypondMark(tx, True)), self.mainWindow.ui.actionLyMarkBelow: lambda: forwardText(self.mainWindow, translate("menu", "Text below the Staff, will be attached to previous(!) item."), lambda tx: api.lilypondMark(tx, False)), } self.modalActions = { #these are only available in Note Edit Mode, not in CC Edit Mode etc. self.mainWindow.ui.actionModal1 : self.modalKeys.key1, self.mainWindow.ui.actionModal2 : self.modalKeys.key2, self.mainWindow.ui.actionModal3 : self.modalKeys.key3, self.mainWindow.ui.actionModal4 : self.modalKeys.key4, self.mainWindow.ui.actionModal5 : self.modalKeys.key5, self.mainWindow.ui.actionModal6 : self.modalKeys.key6, self.mainWindow.ui.actionModal7 : self.modalKeys.key7, #brevis self.mainWindow.ui.actionModal8 : self.modalKeys.key8, #longa self.mainWindow.ui.actionPrevailingRest : self.modalKeys.keyRest, self.mainWindow.ui.actionShift_modal1 : api.insertRest1, self.mainWindow.ui.actionShift_modal2 : api.insertRest2, self.mainWindow.ui.actionShift_modal3 : api.insertRest4, self.mainWindow.ui.actionShift_modal4 : api.insertRest8, self.mainWindow.ui.actionShift_modal5 : api.insertRest16, self.mainWindow.ui.actionShift_modal6 : api.insertRest32, #self.mainWindow.ui.actionShift_modal7 : api.insertRestBrevis, #self.mainWindow.ui.actionShift_modal8 : api.insertRestLonga, #self.mainWindow.ui.actionShift_modal7 : api.insertRest64, #self.mainWindow.ui.actionShift_modal8 : api.insertRest128, } #These are available in CC Edit Mode, Note Edit mode etc. #Add to this list if you want the menu actions to always be enabled self.nonEditingMusicActions = [ self.mainWindow.ui.actionPlayFromBeginning, self.mainWindow.ui.actionPlayPause, self.mainWindow.ui.actionPlayFromEditCursor, self.mainWindow.ui.actionToggle_Notehead_Rectangles, self.mainWindow.ui.actionData_Editor, self.mainWindow.ui.actionFollow_Playhead, self.mainWindow.ui.actionNotation_Mode, self.mainWindow.ui.actionBlock_Mode, self.mainWindow.ui.actionCC_Mode, self.mainWindow.ui.actionChange_Grid_Rhythm, self.mainWindow.ui.actionAdd_Track, self.mainWindow.ui.actionUndo, self.mainWindow.ui.actionRedo, self.mainWindow.ui.actionZoom_In_Score_View, self.mainWindow.ui.actionZoom_Out_Score_View, self.mainWindow.ui.actionWiden_Score_View, self.mainWindow.ui.actionShrink_Score_View, self.mainWindow.ui.actionSave, self.mainWindow.ui.actionQuit, self.mainWindow.ui.actionShow_PDF, self.mainWindow.ui.actionExport_to_Ly, self.mainWindow.ui.actionMetronome_Enabled, self.mainWindow.ui.actionProperties, ] #only in CC edit, when editing user points. #all xxxEditActions are mutually exclusive. #These are defined in non-menu actions above. self.ccEditActions = [ # self.mainWindow.ui.CCactionDelete, ] #Prepare non-designer widgets. Designer can't put normal widgets in a toolbar, but Qt can. #Eventhough this is a dict, which has no order, the CREATION has an order. So the first item in the dict will be the first item in the toolBar midiText = QtCore.QCoreApplication.translate("menu", "Step Midi Input") self.extraToolBarWidgets = { "midiInputWidget" : self.mainWindow.ui.toolBar.addWidget(QuickMidiInputComboController(self.mainWindow.ui.toolBar, text=midiText, stretch=False)), "snapToGrid" : self.mainWindow.ui.toolBar.addWidget(ToolBarSnapToGrid(mainWindow=self.mainWindow)), "metronome" : self.mainWindow.ui.toolBar.addWidget(ToolBarMetronome(mainWindow=self.mainWindow)), "playbackSpeed" : self.mainWindow.ui.toolBar.addWidget(ToolBarPlaybackSpeed(mainWindow=self.mainWindow)), "ccType" : self.mainWindow.ui.toolBar.addWidget(ToolBarCCType(mainWindow=self.mainWindow)), #keep this at the end of the toolbar because it toggles visibility } self.toolbarContexts = { "notation" : [self.extraToolBarWidgets["midiInputWidget"], self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["playbackSpeed"], ], "cc" : [self.extraToolBarWidgets["midiInputWidget"], self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["ccType"], self.extraToolBarWidgets["playbackSpeed"]], "block": [self.extraToolBarWidgets["midiInputWidget"], self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["playbackSpeed"], ], } #Now connect all actions to functions for action, function in self.actions.items(): action.triggered.connect(function) #all xxxEditActions are mutually exclusive. for action, function in self.noteEditActions.items(): action.triggered.connect(function) for action in self.ccEditActions: # a list. action.setEnabled(False) #These are the same keys as noteEditActions, so we need to disable them for now. for action, function in self.modalActions.items(): self.mainWindow.ui.centralwidget.addAction(action) #no actions without a widget action.triggered.connect(function) #Do not connect modalActionsPrevailing yet for action, function in self.actionsWithoutMenu.items(): self.mainWindow.ui.centralwidget.addAction(action) #no actions without a widget action.triggered.connect(function) self.mainWindow.ui.menubar.removeAction(self.mainWindow.ui.menuGeneric.menuAction()) #thats why we added the actions to the centralwidget above. self.loadToolbarContext("notation") def toggleMidiInIsActive(self): """That is: "toggle midiInIsActive", not "toggleMidiIn is active?" """ stepMidiInput.toggleMidiIn() self.mainWindow.ui.actionPrevailingRest.setEnabled(stepMidiInput.midiInIsActive) self.mainWindow.ui.actionMidi_In_is_Active.setChecked(stepMidiInput.midiInIsActive) def setMusicEditingEnabled(self, boolean): """This is the "reset to standard" functions for shortcuts. If you want to enable other keys, such as CC editing keys, you have to call another function after this one""" allActions = list(self.actions.keys()) + list(self.modalActions.keys()) + list(self.actionsWithoutMenu.keys()) + list(self.noteEditActions.keys()) for x in self.nonEditingMusicActions: allActions.remove(x) for editAction in allActions: editAction.setEnabled(boolean) for action in self.ccEditActions: # Call this after enabling stuff in the for-loop above. action.setEnabled(False) #These are the same keys as noteEditActions, so we need to disable them for now. Maybe they are enabled later again, after this function. self.mainWindow.ui.actionPrevailingRest.setEnabled(boolean and stepMidiInput.midiInIsActive) def noteEditMode(self): self.setMusicEditingEnabled(True) def ccEditMode(self): self.setMusicEditingEnabled(False) for action in self.ccEditActions: # a list. action.setEnabled(True) #These are the same keys as noteEditActions, which are disabled above. def writeProtection(self, boolean): """Artificial read only mode that was designed for the mouse-only track editor etc. When zoomed out the user should be able to use the mouse but the cursor is hidden and """ if boolean: #Switch off midi input if stepMidiInput.midiInIsActive: self.rememberMidi = stepMidiInput.midiInIsActive stepMidiInput.toggleMidiIn() assert not stepMidiInput.midiInIsActive self.mainWindow.ui.actionPrevailingRest.setEnabled(stepMidiInput.midiInIsActive) self.mainWindow.ui.actionMidi_In_is_Active.setChecked(stepMidiInput.midiInIsActive) #Deactivate Note Editing Actions self.setMusicEditingEnabled(False) self.mainWindow.scoreView.scoreScene.cursor.hide() #the cursor which can be used to edit notes. Since the cursor shortcuts get disabled it cannot be changed invisbly. else: self.setMusicEditingEnabled(True) self.mainWindow.scoreView.scoreScene.cursor.show() if self.rememberMidi and not stepMidiInput.midiInIsActive: self.toggleMidiInIsActive() self.rememberMidi = None def loadToolbarContext(self, nameAsString): """Hide all toolbar actions and only load those which are in the current context, defined as list""" assert nameAsString in constantsAndConfigs.availableEditModes for action in self.mainWindow.ui.toolBar.actions(): action.setVisible(False) for action in self.toolbarContexts[nameAsString]: action.setVisible(True) def renameUndoRedoByHistory(self, undoList, redoList): if undoList: self.mainWindow.ui.actionUndo.setText(translate("menu", "Undo: {}").format(undoList[-1])) self.mainWindow.ui.actionUndo.setEnabled(True) else: self.mainWindow.ui.actionUndo.setText(translate("menu", "Undo")) self.mainWindow.ui.actionUndo.setEnabled(False) if redoList: self.mainWindow.ui.actionRedo.setText(translate("menu", "Redo: {}").format(redoList[-1])) self.mainWindow.ui.actionRedo.setEnabled(True) else: self.mainWindow.ui.actionRedo.setText(translate("menu", "Redo")) self.mainWindow.ui.actionRedo.setEnabled(False) def exportLy(self): lastExportDirectory = api.session.guiSharedDataToSave["lastExportDirectory"] if "lastExportDirectory" in api.session.guiSharedDataToSave else str(Path.home()) filename = QtWidgets.QFileDialog.getSaveFileName(self.mainWindow, translate("menu", "Export Lilypond Source File"), lastExportDirectory, translate("menu", "Lilypond Source (*.ly)")) filename = filename[0] #(path, filter) if filename: if not os.path.splitext(filename)[1]: #no file extension given? filename = filename + ".ly" api.session.guiSharedDataToSave["lastExportDirectory"] = os.path.dirname(filename) api.exportLilypond(filename) class ToolBarCCType(QtWidgets.QSpinBox): def __init__(self, mainWindow): super().__init__() self.mainWindow = mainWindow self.setRange(0,127) self.setPrefix("CC ") self.valueChanged.connect(self.changed) #self.addItems([str(i).zfill(3) for i in range(128) ]) def changed(self): constantsAndConfigs.ccViewValue = self.value() self.mainWindow.scoreView.updateMode() #gets the new value itself from constantsAndConfigs class ToolBarSnapToGrid(QtWidgets.QCheckBox): def __init__(self, mainWindow): super().__init__(translate("menu", "Snap to Grid")) self.setStatus() self.stateChanged.connect(self.changed) constantsAndConfigs.snapToGridCallbacks.append(self.updateFromCallback) def setStatus(self): if constantsAndConfigs.snapToGrid: self.setCheckState(QtCore.Qt.Checked) else: self.setCheckState(QtCore.Qt.Unchecked) def isChecked(self): if self.checkState() == QtCore.Qt.Checked: return True else: return False def updateFromCallback(self): self.blockSignals(True) self.setStatus() self.blockSignals(False) def changed(self): constantsAndConfigs.snapToGrid = self.isChecked() #this sends a call back to all Snap to Grid in the whole program class ToolBarMetronome(QtWidgets.QCheckBox): """Contrary to SnapToGrid this reflects a backend state, not a pure GUI one. There is also the menu actionMetronome_Enabled , itself a normal checkbox. It's callback and state are handled in the menuDatabase classes init.""" def __init__(self, mainWindow): super().__init__(translate("menu", "Metronome")) self.setStatus() self.stateChanged.connect(self.changed) api.callbacks.metronomeChanged.append(self.updateFromCallback) def setStatus(self): if api.isMetronomeEnabled(): self.setCheckState(QtCore.Qt.Checked) else: self.setCheckState(QtCore.Qt.Unchecked) def isChecked(self): if self.checkState() == QtCore.Qt.Checked: assert api.isMetronomeEnabled(), api.isMetronomeEnabled() return True else: assert not api.isMetronomeEnabled(), api.isMetronomeEnabled() return False def updateFromCallback(self, exportDict): """Gets the metronome dict""" self.blockSignals(True) self.setStatus() assert self.isChecked() == exportDict["enabled"], (self.isChecked(), exportDict["enabled"]) self.setText(translate("menu", "Metronome:") + " " + exportDict["label"]) self.blockSignals(False) def changed(self, value): api.enableMetronome(value) #triggers callback class ToolBarPlaybackSpeed(QtWidgets.QDoubleSpinBox): def __init__(self, mainWindow): super().__init__() self.setRange(0.1,3.0) self.setDecimals(1) self.setValue(api.currentTempoScalingFactor()) self.setSingleStep(0.1) self.setPrefix(translate("menu", "Tempo ×")) #yes, this is the unicode multiplication sign × and not an x api.callbacks.tempoScalingChanged.append(self.updateFromCallback) self.valueChanged.connect(self.changed) self.setLineEdit(ToolBarPlaybackSpeed.CustomLineEdit()) class CustomLineEdit(QtWidgets.QLineEdit): def mousePressEvent(self, event): if event.button() == 4: self.parentWidget().setValue(1.0) self.parentWidget().changed() else: event.ignore() super().mousePressEvent(event) def mousePressEvent(self, event): if event.button() == 4: self.setValue(1.0) self.changed() else: event.ignore() super().mousePressEvent(event) def updateFromCallback(self, newValue): self.blockSignals(True) self.setValue(newValue) self.blockSignals(False) def changed(self): api.changeTempoScaling(self.value())