#! /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 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 ( ) )