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.

647 lines
33 KiB

4 years ago
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
11 months ago
Copyright 2022, Nils Hilbricht, Germany ( https://www.hilbricht.net )
4 years ago
4 years ago
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),
4 years ago
4 years ago
Laborejo2 is free software: you can redistribute it and/or modify
4 years ago
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 Modules
4 years ago
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
4 years ago
import engine.api as api
from engine.midiinput.stepmidiinput import stepMidiInput #singleton instance
4 years ago
from .constantsAndConfigs import constantsAndConfigs
from .submenus import SecondaryClefMenu, SecondaryKeySignatureMenu, SecondaryDynamicsMenu, SecondaryMetricalInstructionMenu, SecondaryTempoChangeMenu, SecondaryTemporaryTempoChangeMenu, SecondarySplitMenu, TransposeMenu, pedalNoteChooser, SecondaryProperties, SecondaryProgramChangeMenu, SecondaryChannelChangeMenu, ChooseOne, forwardText, SecondaryMultimeasureRestMenu, SecondaryDuplicateMenu
from .customkeysignature import CustomKeySignatureWidget
from .custommetricalinstruction import CustomMetricalInstructionWidget
4 years ago
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()
4 years ago
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.actionSelectUp : api.selectUp, #aka "current chord select"
self.mainWindow.ui.actionSelectDown : api.selectDown, #aka "current chord select"
4 years ago
self.mainWindow.ui.actionUpOctave : api.upOctave,
self.mainWindow.ui.actionDownOctave : api.downOctave,
self.mainWindow.ui.actionSelectUpOctave : api.upOctave, #these are the same as up and down. But for a good user experience they should work if shift is pressed down as well.
4 years ago
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
#...
}
4 years ago
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,
4 years ago
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.actionToggle_Track_Solo_Playback : api.toggleCurrentTrackSolo,
self.mainWindow.ui.actionReset_all_Solo : api.resetAllSolo,
4 years ago
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, #Already defined in template. This will trigger twice if used here.
self.mainWindow.ui.actionQuit : self.mainWindow.nsmClient.serverSendExitToSelf, #Strangely this is also defined in template but needs to be here. Wow, what a mess.
4 years ago
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,
4 years ago
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,
4 years ago
}
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.actionCustom_Key_Signature : lambda: CustomKeySignatureWidget(self.mainWindow),
4 years ago
self.mainWindow.ui.actionDynamics : SecondaryDynamicsMenu(self.mainWindow),
self.mainWindow.ui.actionMulti_Measure_Rest : lambda: api.insertMultiMeasureRest(1),
self.mainWindow.ui.actionCustom_Multi_Measure_Rest : SecondaryMultimeasureRestMenu(self.mainWindow),
4 years ago
self.mainWindow.ui.actionMetrical_Instruction : SecondaryMetricalInstructionMenu(self.mainWindow),
#self.mainWindow.ui.actionCustom_Metrical_Instruction : lambda: CustomMetricalInstructionWidget(self.mainWindow), This was set setVisible(False) at the end of init!
4 years ago
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.actionBlock_Properties : self.mainWindow.currentBlockProperties,
4 years ago
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_reserved_duration_empty_block_from_current : api.duplicateToReservedSpaceCurrentBlock,
4 years ago
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,
4 years ago
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.actionPaste_modal_transposed : api.pasteObjectsTransposedModal,
self.mainWindow.ui.actionPaste_real_transposed : api.pasteObjectsTransposedReal,
self.mainWindow.ui.actionDuplicateItem : lambda: api.duplicate(howOften=1),
self.mainWindow.ui.actionDuplicateItem_more : SecondaryDuplicateMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them.
4 years ago
self.mainWindow.ui.actionUndo : api.undo,
self.mainWindow.ui.actionRedo : api.redo,
4 years ago
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,
4 years ago
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,
4 years ago
#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)),
4 years ago
}
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.insertRest64,
self.mainWindow.ui.actionShift_modal8 : api.insertRestBrevis,
self.mainWindow.ui.actionShift_modal9 : api.insertRestLonga,
self.mainWindow.ui.actionShift_modal0 : api.insertRestMaxima,
4 years ago
}
7 months ago
#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 = [
4 years ago
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,
4 years ago
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,
4 years ago
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,
4 years ago
]
7 months ago
#only in CC edit, when editing user points. #all xxxEditActions are mutually exclusive.
#These are defined in non-menu actions above.
self.ccEditActions = [
4 years ago
# 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")
4 years ago
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)),
4 years ago
"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"], ],
4 years ago
}
#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.actionCustom_Metrical_Instruction.setVisible(False) #TODO
4 years ago
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
4 years ago
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]))
4 years ago
self.mainWindow.ui.actionUndo.setEnabled(True)
else:
self.mainWindow.ui.actionUndo.setText(translate("menu", "Undo"))
4 years ago
self.mainWindow.ui.actionUndo.setEnabled(False)
if redoList:
self.mainWindow.ui.actionRedo.setText(translate("menu", "Redo: {}").format(redoList[-1]))
4 years ago
self.mainWindow.ui.actionRedo.setEnabled(True)
else:
self.mainWindow.ui.actionRedo.setText(translate("menu", "Redo"))
4 years ago
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)"))
4 years ago
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)
4 years ago
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"))
4 years ago
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"))
4 years ago
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"""
4 years ago
self.blockSignals(True)
self.setStatus()
assert self.isChecked() == exportDict["enabled"], (self.isChecked(), exportDict["enabled"])
self.setText(translate("menu", "Metronome:") + " " + exportDict["label"])
4 years ago
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)
4 years ago
self.setValue(api.currentTempoScalingFactor())
4 years ago
self.setSingleStep(0.1)
self.setPrefix(translate("menu", "Tempo ×")) #yes, this is the unicode multiplication sign × and not an x
4 years ago
api.callbacks.tempoScalingChanged.append(self.updateFromCallback)
self.valueChanged.connect(self.changed)
self.setLineEdit(ToolBarPlaybackSpeed.CustomLineEdit())
4 years ago
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)
4 years ago
def updateFromCallback(self, newValue):
self.blockSignals(True)
self.setValue(newValue)
self.blockSignals(False)
def changed(self):
api.changeTempoScaling(self.value())