#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2019 , Nils Hilbricht , Germany ( https : / / www . hilbricht . net )
This file is part of the Laborejo Software Suite ( https : / / www . laborejo . org ) ,
more specifically its template base application .
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 / > .
"""
from typing import Iterable , Callable , Tuple
from PyQt5 import QtCore , QtGui , QtWidgets
import engine . api as api
import template . engine . pitch as pitch
from template . qtgui . helper import QHLine
from . constantsAndConfigs import constantsAndConfigs
from . designer . tickWidget import Ui_tickWidget
from sys import maxsize
class CombinedTickWidget ( QtWidgets . QFrame ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
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 ( value )
def setMaximum ( self , value ) :
self . upbeatSpinBox . setMaximum ( value )
def setValue ( self , value ) :
self . upbeatSpinBox . setValue ( value )
def value ( self ) :
""" Make this widget behave like a spinbox signal """
return self . upbeatSpinBox . value ( )
def callClickWidgetForUpbeat ( self ) :
dialog = TickWidget ( self , initValue = self . upbeatSpinBox . value ( ) )
self . upbeatSpinBox . setValue ( dialog . ui . ticks . value ( ) )
class TickWidget ( QtWidgets . QDialog ) :
def __init__ ( self , mainWindow , initValue = 0 ) :
super ( ) . __init__ ( mainWindow )
#Set up the user interface from Designer.
self . ui = Ui_tickWidget ( )
self . ui . setupUi ( self )
#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
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 . 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 . ui . durationLabel . setText ( " " )
def addDuration ( self , duration ) :
self . clickedSoFar . append ( duration )
nowTicks = self . ui . ticks . value ( )
self . ui . ticks . setValue ( nowTicks + duration )
def drawLabel ( self ) :
#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 ) )
#There are two types of submenus in this file. The majority is created in menu.py during start up. Like Clef, KeySig etc. These don't need to ask for any dynamic values.
#The other is like SecondaryTempoChangeMenu. In menu.py this is bound with a lambda construct so a new instance gets created each time the action is called by the user. Thats why this function has self.__call__ in its init.
class Submenu ( QtWidgets . QDialog ) :
#TODO: instead of using a QDialog we could use a QWidget and use it as proxy widget on the graphic scene, placing the menu where the input cursor is.
def __init__ ( self , mainWindow , labelString ) :
super ( ) . __init__ ( mainWindow ) #if you don't set the parent to the main window the whole screen will be the root and the dialog pops up in the middle of it.
#self.setModal(True) #we don't need this when called with self.exec() instead of self.show()
self . layout = QtWidgets . QFormLayout ( )
#self.layout = QtWidgets.QVBoxLayout()
self . setLayout ( self . layout )
label = QtWidgets . QLabel ( labelString ) #"Choose a clef" or so.
self . layout . addWidget ( label )
#self.setFocus(); #self.grabKeyboard(); #redundant for a proper modal dialog. Leave here for documentation reasons.
def keyPressEvent ( self , event ) :
""" Escape closes the dialog by default.
We want Enter as " accept value "
All other methods of mixing editing , window focus and signals
results in strange qt behaviour , triggering the api function twice or more .
Especially unitbox . editingFinished is too easy to trigger .
The key - event method turned out to be the most straightforward way . """
try :
getattr ( self , " process " )
k = event . key ( ) #49=1, 50=2 etc.
if k == 0x01000004 or k == 0x01000005 : #normal enter or keypad enter
event . ignore ( )
self . process ( )
else : #Pressed Esc
self . abortHandler ( )
super ( ) . keyPressEvent ( event )
except AttributeError :
super ( ) . keyPressEvent ( event )
def showEvent ( self , event ) :
#TODO: not optimal but better than nothing.
super ( ) . showEvent ( event )
#self.resize(self.layout.geometry().width(), self.layout.geometry().height())
self . resize ( self . childrenRect ( ) . height ( ) , self . childrenRect ( ) . width ( ) )
self . updateGeometry ( )
def abortHandler ( self ) :
pass
def __call__ ( self ) :
""" This instance can be called like a function """
self . exec ( ) #blocks until the dialog gets closed
"""
Most submenus have the line " lambda, r, value=value " . . .
the r is the return value we get automatically from the Qt buttons which need to be handled .
"""
class SecondaryClefMenu ( Submenu ) :
clefs = [ ( " [1] Treble " , lambda : api . insertClef ( " treble " ) ) ,
( " [2] Bass " , lambda : api . insertClef ( " bass " ) ) ,
( " [3] Alto " , lambda : api . insertClef ( " alto " ) ) ,
( " [4] Drum " , lambda : api . insertClef ( " percussion " ) ) ,
( " [5] Treble ^8 " , lambda : api . insertClef ( " treble^8 " ) ) ,
( " [6] Treble _8 " , lambda : api . insertClef ( " treble_8 " ) ) ,
( " [7] Bass _8 " , lambda : api . insertClef ( " bass_8 " ) ) ,
]
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , " choose a clef " )
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 , " split chord in " )
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 SecondaryKeySignatureMenu ( Submenu ) :
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , " root note is the cursor position " )
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 )
class SecondaryDynamicsMenu ( Submenu ) :
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , " choose a dynamic " )
button = QtWidgets . QPushButton ( " [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 ) :
super ( ) . __init__ ( mainWindow , " choose a metrical instruction " )
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 ChooseOne ( Submenu ) :
""" A generic submenu that presents a list of options to the users.
Only supports up to ten entries , for number shortcuts """
def __init__ ( self , mainWindow , title : str , lst : Iterable [ Tuple [ str , Callable ] ] ) :
if len ( lst ) > 9 :
raise ValueError ( f " ChooseOne submenu supports up to nine entries. You have { len ( lst ) } " )
super ( ) . __init__ ( mainWindow , title )
for number , ( prettyname , function ) in enumerate ( lst ) :
button = QtWidgets . QPushButton ( f " [ { number + 1 } ] { prettyname } " )
button . setShortcut ( QtGui . QKeySequence ( str ( number + 1 ) ) )
button . setStyleSheet ( " Text-align:left; padding: 5px; " ) ;
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 , " choose units per minute, reference note, graph type " )
self . mainWindow = mainWindow
self . staticExportTempoItem = staticExportTempoItem
tickindex , unitsPerMinute , referenceTicks , graphType = 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 . __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 = self . getCurrentValues ( )
newReferenceTicks = constantsAndConfigs . prettyExtendedRhythmsValues [ self . referenceList . currentIndex ( ) ]
graphType = api . getListOfGraphInterpolationTypesAsStrings ( ) [ self . interpolationList . currentIndex ( ) ]
api . insertTempoItemAtAbsolutePosition ( tickindex , self . unitbox . value ( ) , newReferenceTicks , graphType )
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 " ] ,
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 " ] ,
class SecondaryTemporaryTempoChangeMenu ( Submenu ) :
""" Essentially: What kind of fermata effect do you want? """
lastCustomValue = 0.42
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , " [enter] to use value " )
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 , " " )
self . mainWindow = mainWindow
self . staticExportItem = staticExportItem
self . layout . insertRow ( 0 , QtWidgets . QLabel ( " edit block # {} " . format ( staticExportItem [ " id " ] ) ) )
self . name = QtWidgets . QLineEdit ( self . staticExportItem [ " name " ] )
self . name . selectAll ( )
self . layout . addRow ( " name " , self . name )
#self.minimumInTicks = QtWidgets.QSpinBox()
self . minimumInTicks = CombinedTickWidget ( )
self . minimumInTicks . setValue ( self . staticExportItem [ " minimumInTicks " ] )
self . layout . addRow ( " minimum in ticks " , self . minimumInTicks )
self . __call__ ( )
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 , " " )
self . mainWindow = mainWindow
self . staticExportItem = staticExportItem
self . layout . insertRow ( 0 , QtWidgets . QLabel ( " edit block # {} " . format ( staticExportItem [ " id " ] ) ) )
self . name = QtWidgets . QLineEdit ( self . staticExportItem [ " name " ] )
self . name . selectAll ( )
self . layout . addRow ( " name " , self . name )
self . duration = CombinedTickWidget ( )
self . duration . setValue ( self . staticExportItem [ " duration " ] )
self . layout . addRow ( " 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 GridRhytmEdit ( Submenu ) :
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( mainWindow , " " )
self . mainWindow = mainWindow
self . layout . insertRow ( 0 , QtWidgets . QLabel ( " Edit Grid " ) )
self . duration = CombinedTickWidget ( )
self . duration . setValue ( constantsAndConfigs . gridRhythm )
self . layout . addRow ( " duration in ticks " , self . duration )
self . opacity = QtWidgets . QSlider ( QtCore . Qt . Horizontal )
self . opacity . setMinimum ( 0 )
self . opacity . setMaximum ( 50 )
self . opacityLabel = QtWidgets . QLabel ( " opacity: {} % " . format ( int ( constantsAndConfigs . gridOpacity * 100 ) ) )
self . layout . addRow ( self . opacityLabel , self . opacity )
self . opacity . valueChanged . connect ( lambda : self . opacityLabel . setText ( " 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 TransposeMenu ( Submenu ) :
def __init__ ( self , mainWindow , what ) :
super ( ) . __init__ ( mainWindow , " Transpose {} " . format ( what . title ( ) ) )
assert what in ( " item " , " score " )
self . what = what
self . layout . insertRow ( 0 , QtWidgets . QLabel ( " 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 ) :
def __init__ ( self , mainWindow ) :
""" Directly edits the backend score meta data. There is no api and no callbacks """
super ( ) . __init__ ( mainWindow , " Meta Data " )
dictionary = api . getMetadata ( )
test = set ( type ( key ) for key in dictionary . keys ( ) )
assert len ( test ) == 1
assert list ( test ) [ 0 ] == str
self . widgets = { key : self . 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 . __call__ ( )
def makeValueWidget ( self , value ) :
types = {
str : QtWidgets . QLineEdit ,
int : QtWidgets . QSpinBox ,
float : QtWidgets . QDoubleSpinBox ,
}
typ = type ( value )
widget = types [ typ ] ( )
if typ == str :
widget . setText ( value )
elif typ == int or typ == float :
widget . setValue ( value )
return widget
def getValueFromWidget ( self , widget ) :
typ = type ( widget )
if typ == QtWidgets . QLineEdit :
return widget . text ( )
elif typ == QtWidgets . QSpinBox or typ == QtWidgets . QDoubleSpinBox :
return widget . value ( )
def process ( self ) :
api . setMetadata ( { key : self . 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 , " Instrument Change " )
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 ( ( " Program " , self . program ) , ( " Bank MSB " , self . msb ) , ( " Bank LSB " , self . lsb ) ) :
spinbox . setMinimum ( 0 )
spinbox . setMaximum ( 127 )
spinbox . setSingleStep ( 1 )
self . layout . addRow ( label , spinbox )
self . layout . addRow ( " Short Name " , self . shortInstrumentName )
self . insert = QtWidgets . QPushButton ( " Insert " )
self . insert . clicked . connect ( self . process )
self . layout . addWidget ( self . insert )
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 , " Channel Change 1-16. [enter] to use value " )
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 ( " Channel " , self . spinbox )
self . name = QtWidgets . QLineEdit ( )
self . layout . addRow ( " Text " , self . name )
def process ( self ) :
v = self . spinbox . value ( )
type ( self ) . lastCustomValue = v
api . channelChange ( v - 1 , self . name . text ( ) )
self . done ( True )
#Normal Functions
############
def pedalNoteChooser ( mainWindow ) :
try :
constantsAndConfigs . realNotesStrings [ constantsAndConfigs . realNotesValues . index ( constantsAndConfigs . gridRhythm ) + 1 ]
rhythmString = QtWidgets . QInputDialog . getItem ( mainWindow , " Insert Pedal Notes " , " Use duration as base " , constantsAndConfigs . realNotesStrings , constantsAndConfigs . realNotesValues . index ( constantsAndConfigs . gridRhythm ) + 1 , False )
except IndexError :
rhythmString = QtWidgets . QInputDialog . getItem ( mainWindow , " Insert Pedal Notes " , " 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 )