#! /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
from sys import maxsize
#Third party
from PyQt5 import QtCore , QtGui , QtWidgets
translate = QtCore . QCoreApplication . translate
#Template
import template . engine . pitch as pitch
from template . qtgui . helper import QHLine , makeValueWidget , getValueFromWidget
from template . qtgui . submenus import *
#Our own files
import engine . api as api
from engine . midiinput . stepmidiinput import stepMidiInput #singleton instance. Don't allow midi input when submenus are open.
from . constantsAndConfigs import constantsAndConfigs
from . designer . tickWidget import Ui_tickWidget
#Wrap Submenu call to deactivate midi input while showing the menu.
orgCall = Submenu . __call__
def wrapCallDeactivateMidIn ( self ) :
remember = stepMidiInput . midiInIsActive
stepMidiInput . setMidiInputActive ( False )
orgCall ( self )
stepMidiInput . setMidiInputActive ( remember )
Submenu . __call__ = wrapCallDeactivateMidIn
class CombinedTickWidget ( QtWidgets . QFrame ) :
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow )
self . mainWindow = mainWindow
self . setFrameShape ( QtWidgets . QFrame . Box )
self . setFrameShadow ( QtWidgets . QFrame . Sunken )
self . horizontalLayout_3 = QtWidgets . QHBoxLayout ( self )
self . horizontalLayout_3 . setContentsMargins ( 3 , 0 , 3 , 0 )
self . horizontalLayout_3 . setSpacing ( 0 )
self . horizontalLayout_3 . setObjectName ( " horizontalLayout_3 " )
self . upbeatSpinBox = QtWidgets . QSpinBox ( self )
self . upbeatSpinBox . setPrefix ( " " )
self . upbeatSpinBox . setMinimum ( 0 )
self . upbeatSpinBox . setMaximum ( 999999999 )
self . upbeatSpinBox . setObjectName ( " upbeatSpinBox " )
self . horizontalLayout_3 . addWidget ( self . upbeatSpinBox )
self . callTickWidget = QtWidgets . QPushButton ( self )
sizePolicy = QtWidgets . QSizePolicy ( QtWidgets . QSizePolicy . Fixed , QtWidgets . QSizePolicy . Fixed )
sizePolicy . setHorizontalStretch ( 0 )
sizePolicy . setVerticalStretch ( 0 )
sizePolicy . setHeightForWidth ( self . callTickWidget . sizePolicy ( ) . hasHeightForWidth ( ) )
self . callTickWidget . setSizePolicy ( sizePolicy )
self . callTickWidget . setMaximumSize ( QtCore . QSize ( 25 , 16777215 ) )
self . callTickWidget . setFlat ( False )
self . callTickWidget . setObjectName ( " callTickWidget " )
self . callTickWidget . setText ( " 𝅘𝅥𝅮 " )
self . horizontalLayout_3 . addWidget ( self . callTickWidget )
self . callTickWidget . clicked . connect ( self . callClickWidgetForUpbeat )
self . setFocusPolicy ( 0 ) #no focus
self . callTickWidget . setFocusPolicy ( 0 ) #no focus
self . valueChanged = self . upbeatSpinBox . valueChanged
def setMinimum ( self , value ) :
self . upbeatSpinBox . setMinimum ( int ( value ) )
def setMaximum ( self , value ) :
self . upbeatSpinBox . setMaximum ( int ( value ) )
def setValue ( self , value ) :
self . upbeatSpinBox . setValue ( int ( value ) )
def value ( self ) :
""" Make this widget behave like a spinbox signal """
return self . upbeatSpinBox . value ( )
def callClickWidgetForUpbeat ( self ) :
dialog = TickWidget ( self . mainWindow , initValue = self . upbeatSpinBox . value ( ) )
self . upbeatSpinBox . setValue ( int ( dialog . ui . ticks . value ( ) ) )
class TickWidget ( QtWidgets . QDialog ) :
def __init__ ( self , parentWindow , initValue = 0 ) :
super ( ) . __init__ ( )
#Set up the user interface from Designer.
self . ui = Ui_tickWidget ( )
self . ui . setupUi ( self )
#This without init(parentWindow) will result in a nicely positioned window, that is not accesible :(
"""
self . setParent ( parentWindow )
self . setAutoFillBackground ( True )
self . setModal ( False )
self . raise_ ( )
self . activateWindow ( )
"""
#self.ui.ticks.setValue(initValue)
self . ui . ticks . setValue ( 0 ) #TODO: easier to drawLabel this way. change back to given value when drawLabel is autogenerated and does not work by keeping track anymore.
self . ui . ok . clicked . connect ( lambda : self . done ( True ) )
self . ui . cancel . clicked . connect ( lambda : self . done ( False ) )
self . ui . reset . clicked . connect ( self . reset )
self . ui . durationLabel . setText ( " " )
self . clickedSoFar = [ ] #keep track. This is purely cosmetical to remember the click history.
self . multipliers = [ ] #keep track. This is purely cosmetical to remember the click history.
self . ui . D1 . clicked . connect ( lambda : self . addDuration ( api . D1 ) )
self . ui . D2 . clicked . connect ( lambda : self . addDuration ( api . D2 ) )
self . ui . D4 . clicked . connect ( lambda : self . addDuration ( api . D4 ) )
self . ui . D8 . clicked . connect ( lambda : self . addDuration ( api . D8 ) )
self . ui . D16 . clicked . connect ( lambda : self . addDuration ( api . D16 ) )
self . ui . D32 . clicked . connect ( lambda : self . addDuration ( api . D32 ) )
self . ui . D64 . clicked . connect ( lambda : self . addDuration ( api . D64 ) )
self . ui . D128 . clicked . connect ( lambda : self . addDuration ( api . D128 ) )
self . ui . DB . clicked . connect ( lambda : self . addDuration ( api . DB ) )
self . ui . DL . clicked . connect ( lambda : self . addDuration ( api . DL ) )
self . ui . x2 . clicked . connect ( lambda : self . multiply ( 2 ) )
self . ui . x3 . clicked . connect ( lambda : self . multiply ( 3 ) )
self . ui . x5 . clicked . connect ( lambda : self . multiply ( 5 ) )
self . ui . x7 . clicked . connect ( lambda : self . multiply ( 7 ) )
self . ui . ticks . valueChanged . connect ( self . drawLabel )
self . exec ( ) #blocks until the dialog gets closed
#TODO: better key handling. Esc in the ticks field should not close the dialog but return the keyboard focus to the durations
def reset ( self ) :
self . ui . ticks . setValue ( 0 )
self . clickedSoFar = [ ]
self . multipliers = [ ]
self . ui . durationLabel . setText ( " " )
def addDuration ( self , duration : int ) :
self . clickedSoFar . append ( duration )
nowTicks = self . ui . ticks . value ( )
self . ui . ticks . setValue ( nowTicks + duration )
def multiply ( self , times : int ) :
self . multipliers . append ( " x " + str ( times ) )
nowTicks = self . ui . ticks . value ( )
self . ui . ticks . setValue ( nowTicks * times )
def drawLabel ( self ) :
""" This is purely cosmetical """
#TODO: with nice partitions of real note icons.
#Error handling. A too complex or wrong duration (off by one, not equal to a partition etc.) blocks the "OK" button. No, just gives a warning.
#backendDurationInstance = api.items.Duration.createByGuessing(self.ui.ticks.value())
#text = backendDurationInstance.lilypond()
text = [ ]
for duration , symbol in reversed ( sorted ( constantsAndConfigs . realNoteDisplay . items ( ) ) ) :
times = self . clickedSoFar . count ( duration )
if times :
part = str ( times ) + " x " + symbol
text . append ( part )
self . ui . durationLabel . setText ( " ( " + " + " . join ( text ) + " ) " + " " . join ( self . multipliers ) )
class SecondaryClefMenu ( Submenu ) :
clefs = [ ( translate ( " submenus " , " [1] Treble " ) , lambda : api . insertClef ( " treble " ) ) ,
( translate ( " submenus " , " [2] Bass " ) , lambda : api . insertClef ( " bass " ) ) ,
( translate ( " submenus " , " [3] Alto " ) , lambda : api . insertClef ( " alto " ) ) ,
( translate ( " submenus " , " [4] Drum " ) , lambda : api . insertClef ( " percussion " ) ) ,
( translate ( " submenus " , " [5] Treble ^8 " ) , lambda : api . insertClef ( " treble^8 " ) ) ,
( translate ( " submenus " , " [6] Treble _8 " ) , lambda : api . insertClef ( " treble_8 " ) ) ,
( translate ( " submenus " , " [7] Bass _8 (MIDI Drums) " ) , lambda : api . insertClef ( " bass_8 " ) ) ,
]
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " choose a clef " ) , hasOkCancelButtons = 2 )
for number , ( prettyname , function ) in enumerate ( SecondaryClefMenu . clefs ) :
button = QtWidgets . QPushButton ( prettyname )
button . setShortcut ( QtGui . QKeySequence ( str ( number + 1 ) ) )
self . layout . addWidget ( button )
button . clicked . connect ( function )
button . clicked . connect ( self . done )
class SecondarySplitMenu ( Submenu ) :
splits = [ ( " [2] " , lambda : api . split ( 2 ) ) ,
( " [3] " , lambda : api . split ( 3 ) ) ,
( " [4] " , lambda : api . split ( 4 ) ) ,
( " [5] " , lambda : api . split ( 5 ) ) ,
( " [6] " , lambda : api . split ( 6 ) ) ,
( " [7] " , lambda : api . split ( 7 ) ) ,
( " [8] " , lambda : api . split ( 8 ) ) ,
( " [9] " , lambda : api . split ( 9 ) ) ,
]
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " split chord in " ) , hasOkCancelButtons = 2 )
for number , ( prettyname , function ) in enumerate ( SecondarySplitMenu . splits ) :
button = QtWidgets . QPushButton ( prettyname )
button . setShortcut ( QtGui . QKeySequence ( str ( number + 2 ) ) ) #+1 for enumerate from 0, +2 we start at 2.
self . layout . addWidget ( button )
button . clicked . connect ( function )
button . clicked . connect ( self . done )
class SecondaryDuplicateMenu ( Submenu ) :
duplicates = [ ( " [1] " , lambda : api . duplicate ( 1 ) ) ,
( " [2] " , lambda : api . duplicate ( 2 ) ) ,
( " [3] " , lambda : api . duplicate ( 3 ) ) ,
( " [4] " , lambda : api . duplicate ( 4 ) ) ,
( " [5] " , lambda : api . duplicate ( 5 ) ) ,
( " [6] " , lambda : api . duplicate ( 6 ) ) ,
( " [7] " , lambda : api . duplicate ( 7 ) ) ,
( " [8] " , lambda : api . duplicate ( 8 ) ) ,
( " [9] " , lambda : api . duplicate ( 9 ) ) ,
]
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " duplicate how often? " ) , hasOkCancelButtons = 2 )
for number , ( prettyname , function ) in enumerate ( SecondaryDuplicateMenu . duplicates ) :
button = QtWidgets . QPushButton ( prettyname )
button . setShortcut ( QtGui . QKeySequence ( str ( number + 1 ) ) ) #+1 for enumerate from 0, +1 we start at 1.
self . layout . addWidget ( button )
button . clicked . connect ( function )
button . clicked . connect ( self . done )
class SecondaryKeySignatureMenu ( Submenu ) :
def __init__ ( self , mainWindow ) :
""" Init is only called once per program, during startup """
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " root note is the cursor position " ) , hasOkCancelButtons = 2 )
l = [ ( " [ {} ] {} " . format ( num + 1 , modeString . title ( ) ) , lambda r , modeString = modeString : api . insertCursorCommonKeySignature ( modeString ) ) for num , modeString in enumerate ( api . commonKeySignaturesAsList ( ) ) ]
for number , ( prettyname , function ) in enumerate ( l ) :
button = QtWidgets . QPushButton ( prettyname )
button . setShortcut ( QtGui . QKeySequence ( str ( number + 1 ) ) )
self . layout . addWidget ( button )
button . clicked . connect ( function )
button . clicked . connect ( self . done )
def __call__ ( self ) :
self . dynamicLabel . setText ( " Note: " + api . getCursorSimpleLyNote ( ) )
super ( ) . __call__ ( )
class SecondaryDynamicsMenu ( Submenu ) :
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " choose a dynamic " ) , hasOkCancelButtons = 2 )
button = QtWidgets . QPushButton ( translate ( " submenus " , " [r] Ramp " ) )
button . setShortcut ( QtGui . QKeySequence ( " r " ) )
self . layout . addWidget ( button )
button . clicked . connect ( api . insertDynamicRamp )
button . clicked . connect ( self . done )
l = [ ( " [ {} ] {} " . format ( num + 1 , keyword ) , lambda r , keyword = keyword : api . insertDynamicSignature ( keyword ) ) for num , keyword in enumerate ( constantsAndConfigs . dynamics ) ]
for number , ( prettyname , function ) in enumerate ( l ) :
button = QtWidgets . QPushButton ( prettyname )
button . setShortcut ( QtGui . QKeySequence ( str ( number + 1 ) ) )
self . layout . addWidget ( button )
button . clicked . connect ( function )
button . clicked . connect ( self . done )
class SecondaryMetricalInstructionMenu ( Submenu ) :
def __init__ ( self , mainWindow , setInitialInsteadCursorInsert : bool = False ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " choose a metrical instruction " ) , hasOkCancelButtons = 2 )
if setInitialInsteadCursorInsert : #different api function
l = [ ( " [ {} ] {} " . format ( num + 1 , modeString ) , lambda r , modeString = modeString : api . insertCommonMetricalInstrucions ( modeString , setInitialInsteadCursorInsert = True ) ) for num , modeString in enumerate ( api . commonMetricalInstructionsAsList ( ) ) ]
else :
l = [ ( " [ {} ] {} " . format ( num + 1 , modeString ) , lambda r , modeString = modeString : api . insertCommonMetricalInstrucions ( modeString ) ) for num , modeString in enumerate ( api . commonMetricalInstructionsAsList ( ) ) ]
for number , ( prettyname , function ) in enumerate ( l ) :
button = QtWidgets . QPushButton ( prettyname )
button . setShortcut ( QtGui . QKeySequence ( str ( number + 1 ) ) )
self . layout . addWidget ( button )
button . clicked . connect ( function )
button . clicked . connect ( self . done )
class SecondaryTempoChangeMenu ( Submenu ) :
""" A single tempo change where the user can decide which reference unit and how many of them
per minute .
Works as " edit tempo point " when there is already a point at this time position .
This would be the case anyway thanks to backend - behaviour but the gui has the opportunity to
present the current values as a base for editing """
def __init__ ( self , mainWindow , staticExportTempoItem = None ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " choose units per minute, reference note, graph type \n and an optional description like ' Allegro ' " ) , hasOkCancelButtons = True )
self . mainWindow = mainWindow
self . staticExportTempoItem = staticExportTempoItem
tickindex , unitsPerMinute , referenceTicks , graphType , description = self . getCurrentValues ( ) #takes self.staticExportTempoItem into account
self . unitbox = QtWidgets . QSpinBox ( )
self . unitbox . setMinimum ( 1 )
self . unitbox . setMaximum ( 999 )
self . unitbox . setValue ( unitsPerMinute )
self . layout . addWidget ( self . unitbox )
self . referenceList = QtWidgets . QComboBox ( )
self . referenceList . addItems ( constantsAndConfigs . prettyExtendedRhythmsStrings )
self . referenceList . setCurrentIndex ( constantsAndConfigs . prettyExtendedRhythmsValues . index ( referenceTicks ) )
self . layout . addWidget ( self . referenceList )
self . interpolationList = QtWidgets . QComboBox ( )
l = api . getListOfGraphInterpolationTypesAsStrings ( )
self . interpolationList . addItems ( l )
self . interpolationList . setCurrentIndex ( l . index ( graphType ) )
self . layout . addWidget ( self . interpolationList )
self . description = QtWidgets . QLineEdit ( description )
self . layout . addWidget ( self . description )
self . __call__ ( )
def process ( self ) :
""" It says ' insert ' but the backend is a dict. Changes are simply made by overwriting the
whole thing and the backend sends new data to draw to the GUI """
tickindex , unitsPerMinute , referenceTicks , graphType , description = self . getCurrentValues ( )
newReferenceTicks = constantsAndConfigs . prettyExtendedRhythmsValues [ self . referenceList . currentIndex ( ) ]
graphType = api . getListOfGraphInterpolationTypesAsStrings ( ) [ self . interpolationList . currentIndex ( ) ]
description = self . description . text ( )
api . insertTempoItemAtAbsolutePosition ( tickindex , self . unitbox . value ( ) , newReferenceTicks , graphType , description )
self . done ( True )
def getCurrentValues ( self ) :
""" Get the current values from the note-editing backend cursor """
if self . staticExportTempoItem :
return self . staticExportTempoItem [ " position " ] , self . staticExportTempoItem [ " unitsPerMinute " ] , self . staticExportTempoItem [ " referenceTicks " ] , self . staticExportTempoItem [ " graphType " ] , self . staticExportTempoItem [ " lilypondParameters " ] [ " tempo " ]
else :
assert self . mainWindow . scoreView . scoreScene . cursor . cursorExportObject
return self . mainWindow . scoreView . scoreScene . cursor . cursorExportObject [ " tickindex " ] , self . mainWindow . scoreView . scoreScene . cursor . cursorExportObject [ " tempoUnitsPerMinute " ] , self . mainWindow . scoreView . scoreScene . cursor . cursorExportObject [ " tempoReferenceTicks " ] , self . mainWindow . scoreView . scoreScene . cursor . cursorExportObject [ " tempoGraphType " ] , " " #empty string is the description
class SecondaryTemporaryTempoChangeMenu ( Submenu ) :
""" Essentially: What kind of fermata effect do you want? """
lastCustomValue = 0.42
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " [enter] to use value " ) , hasOkCancelButtons = True )
self . spinbox = QtWidgets . QDoubleSpinBox ( )
self . spinbox . setValue ( SecondaryTemporaryTempoChangeMenu . lastCustomValue )
self . spinbox . setDecimals ( 2 )
self . spinbox . setMinimum ( 0.01 )
self . spinbox . setSingleStep ( 0.01 )
self . layout . addWidget ( self . spinbox )
def process ( self ) :
v = round ( self . spinbox . value ( ) , 2 )
SecondaryTemporaryTempoChangeMenu . lastCustomValue = v
api . insertTempoChangeDuringDuration ( v )
self . done ( True )
class BlockPropertiesEdit ( Submenu ) :
def __init__ ( self , mainWindow , staticExportItem ) :
super ( ) . __init__ ( mainWindow , " " , hasOkCancelButtons = True )
self . mainWindow = mainWindow
self . staticExportItem = staticExportItem
self . layout . insertRow ( 0 , QtWidgets . QLabel ( translate ( " submenus " , " edit block # {} " ) . format ( staticExportItem [ " id " ] ) ) )
self . name = QtWidgets . QLineEdit ( self . staticExportItem [ " name " ] )
self . name . selectAll ( )
self . layout . addRow ( translate ( " submenus " , " name " ) , self . name )
#self.minimumInTicks = QtWidgets.QSpinBox()
self . minimumInTicks = CombinedTickWidget ( mainWindow )
self . minimumInTicks . setValue ( self . staticExportItem [ " minimumInTicks " ] )
self . layout . addRow ( translate ( " submenus " , " minimum in ticks " ) , self . minimumInTicks )
self . setMinimumToCurrentButton = QtWidgets . QPushButton ( translate ( " submenus " , " Set min. to current " ) )
self . setMinimumToCurrentButton . clicked . connect ( self . setMinimumToCurrent )
self . layout . addRow ( None , self . setMinimumToCurrentButton )
self . __call__ ( )
def setMinimumToCurrent ( self ) :
self . minimumInTicks . setValue ( self . staticExportItem [ " completeDuration " ] )
def process ( self ) :
newParametersDict = {
" minimumInTicks " : self . minimumInTicks . value ( ) ,
" name " : self . name . text ( ) ,
}
api . changeBlock ( self . staticExportItem [ " id " ] , newParametersDict )
self . done ( True )
class TempoBlockPropertiesEdit ( Submenu ) :
def __init__ ( self , mainWindow , staticExportItem ) :
super ( ) . __init__ ( mainWindow , " " , hasOkCancelButtons = True )
self . mainWindow = mainWindow
self . staticExportItem = staticExportItem
self . layout . insertRow ( 0 , QtWidgets . QLabel ( translate ( " submenus " , " edit block # {} " ) . format ( staticExportItem [ " id " ] ) ) )
self . name = QtWidgets . QLineEdit ( self . staticExportItem [ " name " ] )
self . name . selectAll ( )
self . layout . addRow ( translate ( " submenus " , " name " ) , self . name )
self . duration = CombinedTickWidget ( mainWindow )
self . duration . setValue ( self . staticExportItem [ " duration " ] )
self . layout . addRow ( translate ( " submenus " , " duration in ticks " ) , self . duration )
self . __call__ ( )
def process ( self ) :
newParametersDict = {
" duration " : self . duration . value ( ) ,
" name " : self . name . text ( ) ,
}
api . changeTempoBlock ( self . staticExportItem [ " id " ] , newParametersDict )
self . done ( True )
class TransposeMenu ( Submenu ) :
def __init__ ( self , mainWindow , what ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " Transpose {} " ) . format ( what . title ( ) ) , hasOkCancelButtons = True )
assert what in ( " item " , " score " )
self . what = what
self . layout . insertRow ( 0 , QtWidgets . QLabel ( translate ( " submenus " , " Construct Interval from relative distance " ) ) )
self . fromNote = QtWidgets . QComboBox ( )
self . fromNote . addItems ( pitch . sortedNoteNameList )
self . fromNote . setCurrentIndex ( pitch . sortedNoteNameList . index ( " c ' " ) )
self . layout . addRow ( " from " , self . fromNote )
self . to = QtWidgets . QComboBox ( )
self . to . addItems ( pitch . sortedNoteNameList )
self . to . setCurrentIndex ( pitch . sortedNoteNameList . index ( " c ' " ) )
self . layout . addRow ( " to " , self . to )
self . __call__ ( )
def process ( self ) :
fromPitch = pitch . ly2pitch [ self . fromNote . currentText ( ) ]
toPitch = pitch . ly2pitch [ self . to . currentText ( ) ]
if self . what == " item " :
api . transpose ( fromPitch , toPitch ) #item on cursor position
elif self . what == " score " :
api . transposeScore ( fromPitch , toPitch )
self . done ( True )
class SecondaryProperties ( Submenu ) :
""" Lilypond Settings and Properties.
Directly edits the backend score meta data . There is no api and no callbacks """
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " Lilypond Properties and Metada " ) , hasOkCancelButtons = True )
dictionary = api . getMetadata ( ) #Do not confuse with template/config METADATA. This is Lilypond title, composer etc.
#The dictionary is mutable. We edit in place. #TODO: This might bite us in the future. But maybe it will not...
test = set ( type ( key ) for key in dictionary . keys ( ) )
assert len ( test ) == 1
assert list ( test ) [ 0 ] == str
self . widgets = { key : makeValueWidget ( value ) for key , value in dictionary . items ( ) }
importantKeys = ( " title " , " composer " , " instrument " , " copyright " )
#Draw important metadata widgets first
for k in importantKeys :
self . layout . addRow ( k . title ( ) , self . widgets [ k ] )
self . layout . addRow ( QHLine ( ) )
#Then the rest in alphabetical order
for key , widget in sorted ( self . widgets . items ( ) ) :
if not key in importantKeys :
self . layout . addRow ( key . title ( ) , widget )
self . dynamicLabel . setText ( " <img src= ' :lilypond-metadata-preview.png ' > " )
self . __call__ ( )
def process ( self ) :
api . setMetadata ( { key : getValueFromWidget ( widget ) for key , widget in self . widgets . items ( ) } )
self . done ( True )
#Instance gets killed afterwards. No need to save the new values.
class SecondaryProgramChangeMenu ( Submenu ) :
lastProgramValue = 0
lastMsbValue = 0
lastLsbValue = 0
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " Instrument Change " ) , hasOkCancelButtons = True )
self . program = QtWidgets . QSpinBox ( )
self . program . setValue ( type ( self ) . lastProgramValue )
self . msb = QtWidgets . QSpinBox ( )
self . msb . setValue ( type ( self ) . lastMsbValue )
self . lsb = QtWidgets . QSpinBox ( )
self . lsb . setValue ( type ( self ) . lastLsbValue )
self . shortInstrumentName = QtWidgets . QLineEdit ( )
for label , spinbox in ( ( translate ( " submenus " , " Program " ) , self . program ) , ( translate ( " submenus " , " Bank MSB " ) , self . msb ) , ( translate ( " submenus " , " Bank LSB " ) , self . lsb ) ) :
spinbox . setMinimum ( 0 )
spinbox . setMaximum ( 127 )
spinbox . setSingleStep ( 1 )
self . layout . addRow ( label , spinbox )
self . layout . addRow ( translate ( " submenus " , " Short Name " ) , self . shortInstrumentName )
def process ( self ) :
program = self . program . value ( )
type ( self ) . lastProgramValue = program
msb = self . msb . value ( )
type ( self ) . lastMsbValue = msb
lsb = self . lsb . value ( )
type ( self ) . lastLsbValue = lsb
api . instrumentChange ( program , msb , lsb , self . shortInstrumentName . text ( ) , )
self . done ( True )
class SecondaryChannelChangeMenu ( Submenu ) :
lastCustomValue = 0
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " Channel Change 1-16. [enter] to use value " ) , hasOkCancelButtons = True )
self . spinbox = QtWidgets . QSpinBox ( )
self . spinbox . setValue ( type ( self ) . lastCustomValue )
self . spinbox . setMinimum ( 1 )
self . spinbox . setMaximum ( 16 )
self . spinbox . setSingleStep ( 1 )
self . layout . addRow ( translate ( " submenus " , " Channel " ) , self . spinbox )
self . name = QtWidgets . QLineEdit ( )
self . layout . addRow ( translate ( " submenus " , " Text " ) , self . name )
def process ( self ) :
v = self . spinbox . value ( )
type ( self ) . lastCustomValue = v
api . channelChange ( v - 1 , self . name . text ( ) )
self . done ( True )
class GridRhytmEdit ( Submenu ) :
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , " " , hasOkCancelButtons = True )
self . mainWindow = mainWindow
self . layout . insertRow ( 0 , QtWidgets . QLabel ( translate ( " submenus " , " Edit Grid " ) ) )
self . duration = CombinedTickWidget ( mainWindow )
self . duration . setValue ( constantsAndConfigs . gridRhythm )
self . layout . addRow ( translate ( " submenus " , " duration in ticks " ) , self . duration )
self . opacity = QtWidgets . QSlider ( QtCore . Qt . Horizontal )
self . opacity . setMinimum ( 0 )
self . opacity . setMaximum ( 50 )
self . opacityLabel = QtWidgets . QLabel ( translate ( " submenus " , " opacity: {} % " ) . format ( int ( constantsAndConfigs . gridOpacity * 100 ) ) )
self . layout . addRow ( self . opacityLabel , self . opacity )
self . opacity . valueChanged . connect ( lambda : self . opacityLabel . setText ( translate ( " submenus " , " opacity: {} % " ) . format ( self . opacity . value ( ) ) ) )
self . opacity . setValue ( int ( constantsAndConfigs . gridOpacity * 100 ) )
self . opacity . valueChanged . connect ( lambda : self . mainWindow . scoreView . scoreScene . grid . setOpacity ( self . opacity . value ( ) / 100 ) ) #only react to changes after the initial value was set.
self . __call__ ( )
def process ( self ) :
constantsAndConfigs . gridRhythm = self . duration . value ( )
constantsAndConfigs . gridOpacity = self . opacity . value ( ) / 100
api . session . guiSharedDataToSave [ " grid_opacity " ] = constantsAndConfigs . gridOpacity
api . session . guiSharedDataToSave [ " grid_rhythm " ] = constantsAndConfigs . gridRhythm
self . mainWindow . scoreView . scoreScene . grid . redrawTickGrid ( ) #opacity was already set live, but finally it will be used here again.
self . done ( True )
def abortHandler ( self ) :
self . mainWindow . scoreView . scoreScene . grid . setOpacity ( constantsAndConfigs . gridOpacity ) #reset to initial value and undo the live preview
class SecondaryMultimeasureRestMenu ( Submenu ) :
lastCustomValue = 0
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , translate ( " submenus " , " Rest for how many measures? \n \n (Only works if there is a metrical instruction.) " ) , hasOkCancelButtons = True )
self . spinbox = QtWidgets . QSpinBox ( )
self . spinbox . setValue ( type ( self ) . lastCustomValue )
self . spinbox . setMinimum ( 1 )
self . spinbox . setMaximum ( 9999 )
self . spinbox . setSingleStep ( 1 )
self . layout . addRow ( translate ( " submenus " , " Measures " ) , self . spinbox )
#self.name = QtWidgets.QLineEdit()
#self.layout.addRow(translate("submenus", "Text"), self.name)
def process ( self ) :
v = self . spinbox . value ( )
type ( self ) . lastCustomValue = v
api . insertMultiMeasureRest ( v )
self . done ( True )
#Normal Functions
############
def pedalNoteChooser ( mainWindow ) :
try :
constantsAndConfigs . realNotesStrings [ constantsAndConfigs . realNotesValues . index ( constantsAndConfigs . gridRhythm ) + 1 ]
rhythmString = QtWidgets . QInputDialog . getItem ( mainWindow , translate ( " submenus " , " Insert Pedal Notes " ) , translate ( " submenus " , " Use duration as base " ) , constantsAndConfigs . realNotesStrings , constantsAndConfigs . realNotesValues . index ( constantsAndConfigs . gridRhythm ) + 1 , False )
except IndexError :
rhythmString = QtWidgets . QInputDialog . getItem ( mainWindow , translate ( " submenus " , " Insert Pedal Notes " ) , translate ( " submenus " , " Use duration as base " ) , constantsAndConfigs . realNotesStrings , constantsAndConfigs . realNotesValues . index ( constantsAndConfigs . gridRhythm ) , False )
if rhythmString [ 1 ] : #bool. Canceled?
for baseDuration , v in constantsAndConfigs . commonNotes :
if v == rhythmString [ 0 ] :
api . pedalNotes ( baseDuration )
def forwardText ( mainWindow , title , function ) :
text , status = QtWidgets . QInputDialog . getText ( mainWindow , title , title )
if status :
function ( text )