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.

603 lines
30 KiB

5 years ago
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
5 years ago
Copyright 2019, Nils Hilbricht, Germany ( https://www.hilbricht.net )
5 years ago
5 years ago
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),
more specifically its template base application.
5 years ago
5 years ago
Laborejo2 is free software: you can redistribute it and/or modify
5 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/>.
"""
#Standard Library Modules
5 years ago
import os.path
from pathlib import Path
#Third Party Modules
from PyQt5 import QtCore, QtGui, QtWidgets
#Template Modules
#Our modules
5 years ago
import engine.api as api
from 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
5 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()
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 : self.mainWindow.scoreView.zoomIn,
#self.mainWindow.ui.actionZoom_Out_Score : self.mainWindow.scoreView.zoomOut,
self.mainWindow.ui.actionWiden_Score_View : self.mainWindow.scoreView.widen,
self.mainWindow.ui.actionShrink_Score_View : self.mainWindow.scoreView.shrinken,
self.mainWindow.ui.actionSave : api.save,
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.actionMetronome_Enabled : api.toggleMetronome, #toggle is enough. The callback makes sure all the checkboxes have the correct value.
}
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.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.insertRandomFromScaleAuthenticModeCursor,
self.mainWindow.ui.actionRandom_in_scale_in_cursor_plus_octave: api.insertRandomFromScaleHypoModeCursor,
self.mainWindow.ui.actionMirror_around_Cursor: api.mirrorAroundCursor,
#Note Sorting
self.mainWindow.ui.actionRandom: api.nothing,
self.mainWindow.ui.actionReverse: api.nothing,
self.mainWindow.ui.actionAscending: api.nothing,
self.mainWindow.ui.actionDescending: api.nothing,
#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, "Choose a Barline", api.getLilypondBarlineList()),
self.mainWindow.ui.actionLyRepeat: ChooseOne(self.mainWindow, "Choose a Repeat", api.getLilypondRepeatList()),
self.mainWindow.ui.actionLyFree_Instruction: lambda: forwardText(self.mainWindow, "Enter Instruction", api.lilypondText),
5 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.insertRestBrevis,
#self.mainWindow.ui.actionShift_modal8 : api.insertRestLonga,
#self.mainWindow.ui.actionShift_modal7 : api.insertRest64,
#self.mainWindow.ui.actionShift_modal8 : api.insertRest128,
}
self.nonEditingMusicActions = [ #These are available in CC Edit Mode, Note Edit mode etc.
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,
#self.mainWindow.ui.actionZoom_Out_Score,
self.mainWindow.ui.actionWiden_Score_View,
self.mainWindow.ui.actionShrink_Score_View,
self.mainWindow.ui.actionSave,
]
self.ccEditActions = [ #only in CC edit, when editing user points. #all xxxEditActions are mutually exclusive. These are defined in non-menu actions above.
# 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
self.extraToolBarWidgets = {
"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["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["playbackSpeed"], ],
"cc" : [self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["ccType"], self.extraToolBarWidgets["playbackSpeed"]],
"block": [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("Undo: {}".format(undoList[-1]))
self.mainWindow.ui.actionUndo.setEnabled(True)
else:
self.mainWindow.ui.actionUndo.setText("Undo")
self.mainWindow.ui.actionUndo.setEnabled(False)
if redoList:
self.mainWindow.ui.actionRedo.setText("Redo: {}".format(redoList[-1]))
self.mainWindow.ui.actionRedo.setEnabled(True)
else:
self.mainWindow.ui.actionRedo.setText("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, "Export Lilypond Source File", lastExportDirectory, "Lilypond Source (*.ly)")
5 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)
5 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__("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__("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):
5 years ago
"""Gets the metronome dict"""
5 years ago
self.blockSignals(True)
self.setStatus()
assert self.isChecked() == exportDict["enabled"], (self.isChecked(), exportDict["enabled"])
5 years ago
self.setText("Metronome: " + exportDict["label"])
5 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)
5 years ago
self.setValue(api.currentTempoScalingFactor())
5 years ago
self.setSingleStep(0.1)
self.setPrefix("Tempo ×") #yes, this is the unicode multiplication sign × and not an x
5 years ago
api.callbacks.tempoScalingChanged.append(self.updateFromCallback)
5 years ago
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)
5 years ago
def updateFromCallback(self, newValue):
self.blockSignals(True)
self.setValue(newValue)
self.blockSignals(False)
5 years ago
def changed(self):
5 years ago
api.changeTempoScaling(self.value())