def createEditor( self, parent, option, index ): if index.column() == DESCRIPCION: combo = QComboBox( parent ) combo.setEditable( True ) value = index.data().toString() self.filtrados.append( value ) self.proxymodel.setFilterRegExp( self.filter() ) combo.setModel( self.proxymodel ) combo.setModelColumn( 1 ) combo.setCompleter( self.completer ) return combo elif index.column() == BANCO: combo = QComboBox( parent ) #combo.setEditable(True) combo.setModel( self.bancosmodel ) combo.setModelColumn( 1 ) #combo.setCompleter(self.completer) return combo elif index.column() == MONTO: doublespinbox = QDoubleSpinBox( parent ) doublespinbox.setMinimum( -1000000 ) doublespinbox.setMaximum( 1000000 ) doublespinbox.setDecimals( 4 ) doublespinbox.setAlignment( Qt.AlignHCenter ) return doublespinbox elif index.column() == REFERENCIA: textbox = QStyledItemDelegate.createEditor( self, parent, option, index ) textbox.setAlignment( Qt.AlignHCenter ) return textbox
class ScoreProperties(object): """This is only the base class, it should be mixed in with a widget or a different way.""" def createWidgets(self): """Creates all widgets.""" self.createKeySignatureWidget() self.createTimeSignatureWidget() self.createPickupWidget() self.createMetronomeWidget() self.createTempoWidget() def layoutWidgets(self, layout): """Adds all widgets to a vertical layout.""" self.layoutKeySignatureWidget(layout) self.layoutTimeSignatureWidget(layout) self.layoutPickupWidget(layout) self.layoutMetronomeWidget(layout) self.layoutTempoWidget(layout) def translateWidgets(self): self.translateKeySignatureWidget() self.translateTimeSignatureWidget() self.translatePickupWidget() self.tranlateMetronomeWidget() self.translateTempoWidget() def ly(self, node, builder): """Adds appropriate LilyPond command nodes to the parent node. Settings from the builder are used where that makes sense. All widgets must be present. """ self.lyKeySignature(node, builder) self.lyTimeSignature(node, builder) self.lyPickup(node, builder) self.lyTempo(node, builder) def globalSection(self, builder): """Returns a sequential expression between { } containing the output of ly().""" seq = ly.dom.Seq() self.ly(seq, builder) return seq # Key signature def createKeySignatureWidget(self): self.keySignatureLabel = QLabel() self.keyNote = QComboBox() self.keyNote.setModel(listmodel.ListModel(keyNames['nederlands'], self.keyNote)) self.keyMode = QComboBox() self.keyMode.setModel(listmodel.ListModel(modes, self.keyMode, display=listmodel.translate_index(1))) self.keySignatureLabel.setBuddy(self.keyNote) def translateKeySignatureWidget(self): self.keySignatureLabel.setText(_("Key signature:")) self.keyMode.model().update() def layoutKeySignatureWidget(self, layout): """Adds our widgets to a layout, assuming it is a QVBoxLayout.""" box = QHBoxLayout() box.addWidget(self.keySignatureLabel) box.addWidget(self.keyNote) box.addWidget(self.keyMode) layout.addLayout(box) def setPitchLanguage(self, language='nederlands'): self.keyNote.model()._data = keyNames[language or 'nederlands'] self.keyNote.model().update() def lyKeySignature(self, node, builder): """Adds the key signature to the ly.dom node parent.""" note, alter = keys[self.keyNote.currentIndex()] alter = fractions.Fraction(alter, 2) mode = modes[self.keyMode.currentIndex()][0] ly.dom.KeySignature(note, alter, mode, node).after = 1 # Time signature def createTimeSignatureWidget(self): self.timeSignatureLabel = QLabel() self.timeSignature = QComboBox(editable=True) icons = { '(4/4)': symbols.icon('time_c44'), '(2/2)': symbols.icon('time_c22'), } self.timeSignature.setModel(listmodel.ListModel(timeSignaturePresets, self.timeSignature, icon=icons.get)) self.timeSignature.setCompleter(None) self.timeSignatureLabel.setBuddy(self.timeSignature) def translateTimeSignatureWidget(self): self.timeSignatureLabel.setText(_("Time signature:")) def layoutTimeSignatureWidget(self, layout): """Adds our widgets to a layout, assuming it is a QVBoxLayout.""" box = QHBoxLayout() box.addWidget(self.timeSignatureLabel) box.addWidget(self.timeSignature) layout.addLayout(box) def lyTimeSignature(self, node, builder): """Adds the time signature to the ly.dom node parent.""" sig = self.timeSignature.currentText().strip() if '+' in sig: pass # TODO: implement support for \compoundMeter else: if sig == '(2/2)': ly.dom.TimeSignature(2, 2, node).after = 1 elif sig == '(4/4)': ly.dom.TimeSignature(4, 4, node).after = 1 else: match = re.search(r'(\d+).*?(\d+)', sig) if match: if builder.lyVersion >= (2, 11, 44): ly.dom.Line(r"\numericTimeSignature", node) else: ly.dom.Line(r"\override Staff.TimeSignature #'style = #'()", node) num, beat = map(int, match.group(1, 2)) ly.dom.TimeSignature(num, beat, node).after = 1 # Pickup bar def createPickupWidget(self): self.pickupLabel = QLabel() self.pickup = QComboBox() pickups = [''] pickups.extend(durations) self.pickup.setModel(listmodel.ListModel(pickups, self.pickup, display = lambda item: item or _("None"), icon = lambda item: symbols.icon('note_{0}'.format(item.replace('.', 'd'))) if item else None)) self.pickup.view().setIconSize(QSize(22, 22)) self.pickupLabel.setBuddy(self.pickup) def translatePickupWidget(self): self.pickupLabel.setText(_("Pickup measure:")) self.pickup.model().update() def layoutPickupWidget(self, layout): box = QHBoxLayout() box.addWidget(self.pickupLabel) box.addWidget(self.pickup) layout.addLayout(box) def lyPickup(self, node, builder): if self.pickup.currentIndex() > 0: dur, dots = partialDurations[self.pickup.currentIndex() - 1] ly.dom.Partial(dur, dots, parent=node) # Metronome value def createMetronomeWidget(self): self.metronomeLabel = QLabel() self.metronomeNote = QComboBox() self.metronomeNote.setModel(listmodel.ListModel(durations, display=None, icon = lambda item: symbols.icon('note_{0}'.format(item.replace('.', 'd'))))) self.metronomeNote.setCurrentIndex(durations.index('4')) self.metronomeNote.view().setIconSize(QSize(22, 22)) self.metronomeEqualSign = QLabel('=') self.metronomeEqualSign.setFixedWidth(self.metronomeEqualSign.minimumSizeHint().width()) self.metronomeValue = QComboBox(editable=True) self.metronomeValue.setModel(listmodel.ListModel(metronomeValues, self.metronomeValue, display=format)) self.metronomeValue.setCompleter(None) self.metronomeValue.setValidator(QIntValidator(0, 999, self.metronomeValue)) self.metronomeValue.setCurrentIndex(metronomeValues.index(100)) self.metronomeTempo = widgets.tempobutton.TempoButton() self.metronomeTempo.tempo.connect(self.setMetronomeValue) self.metronomeLabel.setBuddy(self.metronomeNote) def layoutMetronomeWidget(self, layout): box = QHBoxLayout(spacing=0) box.addWidget(self.metronomeLabel) box.addWidget(self.metronomeNote) box.addWidget(self.metronomeEqualSign) box.addWidget(self.metronomeValue) box.addWidget(self.metronomeTempo) layout.addLayout(box) def tranlateMetronomeWidget(self): self.metronomeLabel.setText(_("Metronome mark:")) def setMetronomeValue(self, bpm): """ Tap the tempo tap button """ l = [abs(t - bpm) for t in metronomeValues] m = min(l) if m < 6: self.metronomeValue.setCurrentIndex(l.index(m)) # Tempo indication def createTempoWidget(self): self.tempoLabel = QLabel() self.tempo = widgets.lineedit.LineEdit() completionmodel.complete(self.tempo, "scorewiz/completion/scoreproperties/tempo") self.tempo.completer().setCaseSensitivity(Qt.CaseInsensitive) self.tempoLabel.setBuddy(self.tempo) def layoutTempoWidget(self, layout): box = QHBoxLayout() box.addWidget(self.tempoLabel) box.addWidget(self.tempo) layout.addLayout(box) def translateTempoWidget(self): self.tempoLabel.setText(_("Tempo indication:")) def lyTempo(self, node, builder): """Returns an appropriate tempo indication.""" text = self.tempo.text().strip() if builder.showMetronomeMark: dur = durations[self.metronomeNote.currentIndex()] val = self.metronomeValue.currentText() elif text: dur = None val = None else: return tempo = ly.dom.Tempo(dur, val, node) if text: ly.dom.QuotedString(text, tempo) def lyMidiTempo(self, node): """Sets the configured tempo in the tempoWholesPerMinute variable.""" node['tempoWholesPerMinute'] = ly.dom.Scheme(self.schemeMidiTempo()) def schemeMidiTempo(self): """Returns a string with the tempo like '(ly:make-moment 100 4)' from the settings.""" base, mul = midiDurations[self.metronomeNote.currentIndex()] val = int(self.metronomeValue.currentText()) * mul return "(ly:make-moment {0} {1})".format(val, base) def lySimpleMidiTempo(self, node): """Return a simple \tempo x=y node for the currently set tempo.""" dur = durations[self.metronomeNote.currentIndex()] val = self.metronomeValue.currentText() return ly.dom.Tempo(dur, val, node)
class Choir(VocalPart): @staticmethod def title(_=_base.translate): return _("Choir") def createWidgets(self, layout): self.label = QLabel(wordWrap=True) self.voicingLabel = QLabel() self.voicing = QComboBox(editable=True) self.voicingLabel.setBuddy(self.voicing) self.voicing.setCompleter(None) self.voicing.setValidator(QRegExpValidator( QRegExp("[SATB]+(-[SATB]+)*", Qt.CaseInsensitive), self.voicing)) self.voicing.addItems(( 'SA-TB', 'S-A-T-B', 'SA', 'S-A', 'SS-A', 'S-S-A', 'TB', 'T-B', 'TT-B', 'T-T-B', 'SS-A-T-B', 'S-A-TT-B', 'SS-A-TT-B', 'S-S-A-T-T-B', 'S-S-A-A-T-T-B-B', )) self.lyricsLabel = QLabel() self.lyrics = QComboBox() self.lyricsLabel.setBuddy(self.lyrics) self.lyrics.setModel(listmodel.ListModel(lyricStyles, self.lyrics, display=listmodel.translate_index(0), tooltip=listmodel.translate_index(1))) self.lyrics.setCurrentIndex(0) self.pianoReduction = QCheckBox() self.rehearsalMidi = QCheckBox() layout.addWidget(self.label) box = QHBoxLayout() layout.addLayout(box) box.addWidget(self.voicingLabel) box.addWidget(self.voicing) self.createStanzaWidget(layout) box = QHBoxLayout() layout.addLayout(box) box.addWidget(self.lyricsLabel) box.addWidget(self.lyrics) self.createAmbitusWidget(layout) layout.addWidget(self.pianoReduction) layout.addWidget(self.rehearsalMidi) def translateWidgets(self): self.translateStanzaWidget() self.translateAmbitusWidget() self.lyrics.model().update() self.label.setText('<p>{0} <i>({1})</i></p>'.format( _("Please select the voices for the choir. " "Use the letters S, A, T, or B. A hyphen denotes a new staff."), _("Hint: For a double choir you can use two choir parts."))) self.voicingLabel.setText(_("Voicing:")) self.lyricsLabel.setText(_("Lyrics:")) self.pianoReduction.setText(_("Piano reduction")) self.pianoReduction.setToolTip(_( "Adds an automatically generated piano reduction.")) self.rehearsalMidi.setText(_("Rehearsal MIDI files")) self.rehearsalMidi.setToolTip(_( "Creates a rehearsal MIDI file for every voice, " "even if no MIDI output is generated for the main score.")) def build(self, data, builder): # normalize voicing staves = self.voicing.currentText().upper() # remove unwanted characters staves = re.sub(r'[^SATB-]+', '', staves) # remove double hyphens, and from begin and end staves = re.sub('-+', '-', staves).strip('-') if not staves: return splitStaves = staves.split('-') numStaves = len(splitStaves) staffCIDs = collections.defaultdict(int) # number same-name staff Context-IDs voiceCounter = collections.defaultdict(int) # dict to number same voice types maxNumVoices = max(map(len, splitStaves)) # largest number of voices numStanzas = self.stanzas.value() lyrics = collections.defaultdict(list) # lyrics grouped by stanza number pianoReduction = collections.defaultdict(list) rehearsalMidis = [] p = ly.dom.ChoirStaff() choir = ly.dom.Sim(p) data.nodes.append(p) # print main instrumentName if there are more choirs, and we # have more than one staff. if numStaves > 1 and data.num: builder.setInstrumentNames(p, builder.instrumentName(lambda _: _("Choir"), data.num), builder.instrumentName(lambda _: _("abbreviation for Choir", "Ch."), data.num)) # get the preferred way of adding lyrics lyrAllSame, lyrEachSame, lyrEachDiff, lyrSpread = ( self.lyrics.currentIndex() == i for i in range(4)) lyrEach = lyrEachSame or lyrEachDiff # stanzas to print (0 = don't print stanza number): if numStanzas == 1: allStanzas = [0] else: allStanzas = list(range(1, numStanzas + 1)) # Which stanzas to print where: if lyrSpread and numStanzas > 1 and numStaves > 2: spaces = numStaves - 1 count, rest = divmod(max(numStanzas, spaces), spaces) stanzaSource = itertools.cycle(allStanzas) stanzaGroups = (itertools.islice(stanzaSource, num) for num in itertools.chain( itertools.repeat(count + 1, rest), itertools.repeat(count, numStaves - rest))) else: stanzaGroups = itertools.repeat(allStanzas, numStaves) # a function to set staff affinity (in LilyPond 2.13.4 and above): if builder.lyVersion >= (2, 13, 4): def setStaffAffinity(context, affinity): ly.dom.Line("\\override VerticalAxisGroup " "#'staff-affinity = #" + affinity, context.getWith()) else: def setStaffAffinity(lyricsContext, affinity): pass # a function to make a column markup: if builder.lyVersion >= (2, 11, 57): columnCommand = 'center-column' else: columnCommand = 'center-align' def makeColumnMarkup(names): node = ly.dom.Markup() column = ly.dom.MarkupEnclosed(columnCommand, node) for name in names: ly.dom.QuotedString(name, column) return node stavesLeft = numStaves for staff, stanzas in zip(splitStaves, stanzaGroups): # are we in the last staff? stavesLeft -= 1 # the number of voices in this staff numVoices = len(staff) # sort the letters in order SATB staff = ''.join(i * staff.count(i) for i in 'SATB') # Create the staff for the voices s = ly.dom.Staff(parent=choir) builder.setMidiInstrument(s, self.midiInstrument) # Build a list of the voices in this staff. # Each entry is a tuple(name, num). # name is one of 'S', 'A', 'T', or 'B' # num is an integer: 0 when a voice occurs only once, or >= 1 when # there are more voices of the same type (e.g. Soprano I and II) voices = [] for voice in staff: if staves.count(voice) > 1: voiceCounter[voice] += 1 voices.append((voice, voiceCounter[voice])) # Add the instrument names to the staff: if numVoices == 1: voice, num = voices[0] longName = builder.instrumentName(voice2Voice[voice].title, num) shortName = builder.instrumentName(voice2Voice[voice].short, num) builder.setInstrumentNames(s, longName, shortName) else: # stack instrument names (long and short) in a markup column. # long names longNames = makeColumnMarkup( builder.instrumentName(voice2Voice[voice].title, num) for voice, num in voices) shortNames = makeColumnMarkup( builder.instrumentName(voice2Voice[voice].short, num) for voice, num in voices) builder.setInstrumentNames(s, longNames, shortNames) # Make the { } or << >> holder for this staff's children. # If *all* staves have only one voice, addlyrics is used. # In that case, don't remove the braces. staffMusic = (ly.dom.Seq if lyrEach and maxNumVoices == 1 else ly.dom.Seqr if numVoices == 1 else ly.dom.Simr)(s) # Set the clef for this staff: if 'B' in staff: ly.dom.Clef('bass', staffMusic) elif 'T' in staff: ly.dom.Clef('treble_8', staffMusic) # Determine voice order (\voiceOne, \voiceTwo etc.) if numVoices == 1: order = (0,) elif numVoices == 2: order = 1, 2 elif staff in ('SSA', 'TTB'): order = 1, 3, 2 elif staff in ('SAA', 'TBB'): order = 1, 2, 4 elif staff in ('SSAA', 'TTBB'): order = 1, 3, 2, 4 else: order = range(1, numVoices + 1) # What name would the staff get if we need to refer to it? # If a name (like 's' or 'sa') is already in use in this part, # just add a number ('ss2' or 'sa2', etc.) staffCIDs[staff] += 1 cid = ly.dom.Reference(staff.lower() + str(staffCIDs[staff] if staffCIDs[staff] > 1 else "")) # Create voices and their lyrics: for (voice, num), voiceNum in zip(voices, order): name = voice2id[voice] if num: name += ly.util.int2text(num) a = data.assignMusic(name, voice2Voice[voice].octave) lyrName = name + 'Verse' if lyrEachDiff else 'verse' # Use \addlyrics if all staves have exactly one voice. if lyrEach and maxNumVoices == 1: for verse in stanzas: lyrics[verse].append((ly.dom.AddLyrics(s), lyrName)) ly.dom.Identifier(a.name, staffMusic) else: voiceName = voice2id[voice] + str(num or '') v = ly.dom.Voice(voiceName, parent=staffMusic) voiceMusic = ly.dom.Seqr(v) if voiceNum: ly.dom.Text('\\voice' + ly.util.int2text(voiceNum), voiceMusic) ly.dom.Identifier(a.name, voiceMusic) if stanzas and (lyrEach or (voiceNum <= 1 and (stavesLeft or numStaves == 1))): # Create the lyrics. If they should be above the staff, # give the staff a suitable name, and use alignAbove- # Context to align the Lyrics above the staff. above = voiceNum & 1 if lyrEach else False if above and s.cid is None: s.cid = cid for verse in stanzas: l = ly.dom.Lyrics(parent=choir) if above: l.getWith()['alignAboveContext'] = cid setStaffAffinity(l, "DOWN") elif not lyrEach and stavesLeft: setStaffAffinity(l, "CENTER") lyrics[verse].append((ly.dom.LyricsTo(voiceName, l), lyrName)) # Add ambitus: if self.ambitus.isChecked(): ambitusContext = (s if numVoices == 1 else v).getWith() ly.dom.Line('\\consists "Ambitus_engraver"', ambitusContext) if voiceNum > 1: ly.dom.Line("\\override Ambitus #'X-offset = #{0}".format( (voiceNum - 1) * 2.0), ambitusContext) pianoReduction[voice].append(a.name) rehearsalMidis.append((voice, num, a.name, lyrName)) # Assign the lyrics, so their definitions come after the note defs. # (These refs are used again below in the midi rehearsal routine.) refs = {} for verse in allStanzas: for node, name in lyrics[verse]: if (name, verse) not in refs: refs[(name, verse)] = self.assignLyrics(data, name, verse).name ly.dom.Identifier(refs[(name, verse)], node) # Create the piano reduction if desired if self.pianoReduction.isChecked(): a = data.assign('pianoReduction') data.nodes.append(ly.dom.Identifier(a.name)) piano = ly.dom.PianoStaff(parent=a) sim = ly.dom.Sim(piano) rightStaff = ly.dom.Staff(parent=sim) leftStaff = ly.dom.Staff(parent=sim) right = ly.dom.Seq(rightStaff) left = ly.dom.Seq(leftStaff) # Determine the ordering of voices in the staves upper = pianoReduction['S'] + pianoReduction['A'] lower = pianoReduction['T'] + pianoReduction['B'] preferUpper = 1 if not upper: # Male choir upper = pianoReduction['T'] lower = pianoReduction['B'] ly.dom.Clef("treble_8", right) ly.dom.Clef("bass", left) preferUpper = 0 elif not lower: # Female choir upper = pianoReduction['S'] lower = pianoReduction['A'] else: ly.dom.Clef("bass", left) # Otherwise accidentals can be confusing ly.dom.Line("#(set-accidental-style 'piano)", right) ly.dom.Line("#(set-accidental-style 'piano)", left) # Move voices if unevenly spread if abs(len(upper) - len(lower)) > 1: voices = upper + lower half = (len(voices) + preferUpper) // 2 upper = voices[:half] lower = voices[half:] for staff, voices in (ly.dom.Simr(right), upper), (ly.dom.Simr(left), lower): if voices: for v in voices[:-1]: ly.dom.Identifier(v, staff) ly.dom.VoiceSeparator(staff).after = 1 ly.dom.Identifier(voices[-1], staff) # Make the piano part somewhat smaller ly.dom.Line("fontSize = #-1", piano.getWith()) ly.dom.Line("\\override StaffSymbol #'staff-space = #(magstep -1)", piano.getWith()) # Nice to add Mark engravers ly.dom.Line('\\consists "Mark_engraver"', rightStaff.getWith()) ly.dom.Line('\\consists "Metronome_mark_engraver"', rightStaff.getWith()) # Keep piano reduction out of the MIDI output if builder.midi: ly.dom.Line('\\remove "Staff_performer"', rightStaff.getWith()) ly.dom.Line('\\remove "Staff_performer"', leftStaff.getWith()) # Create MIDI files if desired if self.rehearsalMidi.isChecked(): a = data.assign('rehearsalMidi') rehearsalMidi = a.name func = ly.dom.SchemeList(a) func.pre = '#\n(' # hack ly.dom.Text('define-music-function', func) ly.dom.Line('(parser location name midiInstrument lyrics) ' '(string? string? ly:music?)', func) choir = ly.dom.Sim(ly.dom.Command('unfoldRepeats', ly.dom.SchemeLily(func))) data.afterblocks.append(ly.dom.Comment(_("Rehearsal MIDI files:"))) for voice, num, ref, lyrName in rehearsalMidis: # Append voice to the rehearsalMidi function name = voice2id[voice] + str(num or '') seq = ly.dom.Seq(ly.dom.Voice(name, parent=ly.dom.Staff(name, parent=choir))) if builder.lyVersion < (2, 18, 0): ly.dom.Text('<>\\f', seq) # add one dynamic ly.dom.Identifier(ref, seq) # add the reference to the voice book = ly.dom.Book() # Append score to the aftermath (stuff put below the main score) suffix = "choir{0}-{1}".format(data.num, name) if data.num else name if builder.lyVersion < (2, 12, 0): data.afterblocks.append( ly.dom.Line('#(define output-suffix "{0}")'.format(suffix))) else: ly.dom.Line('\\bookOutputSuffix "{0}"'.format(suffix), book) data.afterblocks.append(book) data.afterblocks.append(ly.dom.BlankLine()) score = ly.dom.Score(book) # TODO: make configurable midiInstrument = voice2Midi[voice] cmd = ly.dom.Command(rehearsalMidi, score) ly.dom.QuotedString(name, cmd) ly.dom.QuotedString(midiInstrument, cmd) ly.dom.Identifier(refs[(lyrName, allStanzas[0])], cmd) ly.dom.Midi(score) ly.dom.Text("\\context Staff = $name", choir) seq = ly.dom.Seq(choir) ly.dom.Line("\\set Score.midiMinimumVolume = #0.5", seq) ly.dom.Line("\\set Score.midiMaximumVolume = #0.5", seq) ly.dom.Line("\\set Score.tempoWholesPerMinute = #" + data.scoreProperties.schemeMidiTempo(), seq) ly.dom.Line("\\set Staff.midiMinimumVolume = #0.8", seq) ly.dom.Line("\\set Staff.midiMaximumVolume = #1.0", seq) ly.dom.Line("\\set Staff.midiInstrument = $midiInstrument", seq) lyr = ly.dom.Lyrics(parent=choir) lyr.getWith()['alignBelowContext'] = ly.dom.Text('$name') ly.dom.Text("\\lyricsto $name $lyrics", lyr)
class ChoirStaff(StaffBase): def __init__(self, dialog): StaffBase.__init__(self, dialog) self.staffCount = QSpinBox() self.staffCount.setRange(2, 8) l = QLabel(i18n("Staves per system:")) l.setBuddy(self.staffCount) self.layout().addWidget(l, 0, 1, Qt.AlignRight) self.layout().addWidget(self.staffCount, 0, 2) l = QLabel(i18n("Systems per page:")) l.setBuddy(self.systems) self.layout().addWidget(l, 1, 1, Qt.AlignRight) self.layout().addWidget(self.systems, 1, 2) self.clefs = QComboBox() self.clefs.setEditable(True) self.clefs.setCompleter(None) l = QLabel(i18n("Clefs:")) l.setBuddy(self.clefs) self.layout().addWidget(l, 2, 1, Qt.AlignRight) self.layout().addWidget(self.clefs, 2, 2) # tool tips self.clefs.setToolTip(i18n( "Enter as many letters (S, A, T or B) as there are staves.\n" "See \"What's This\" for more information.")) self.clefs.setWhatsThis(i18n( "To configure clefs, first set the number of staves per system. " "Then enter as many letters (S, A, T or B) as there are staves.\n\n" "S or A: treble clef,\n" "T: treble clef with an \"8\" below,\n" "B: bass clef\n\n" "So when you want to create music paper for a four-part mixed " "choir score, first set the number of staves per system to 4. " "Then enter \"SATB\" (without the quotes) here.")) self.staffCount.valueChanged.connect(self.slotStaffCountChanged) self.slotStaffCountChanged(2) def name(self): return i18n("Choir Staff") def default(self): self.staffCount.setValue(2) def slotStaffCountChanged(self, value): self.clefs.clear() self.clefs.addItem(i18n("None")) self.clefs.addItems(( ('SB', 'SS', 'ST', 'TT', 'TB', 'BB'), # 2 ('SSB', 'SSS', 'TTB', 'TTT', 'TTB'), # 3 ('SATB', 'STTB', 'TTBB'), # 4 ('SSATB', 'SATTB'), # 5 ('SSATTB', 'SSATBB'), #6 ('SSATTBB', ), # 7 ('SATBSATB', 'SSAATTBB') #8 )[value - 2]) self.clefs.setCurrentIndex(0) systemCount = int(12 / value) if systemCount == 1 and self.dialog.staffSize.value() <= 18: systemCount = 2 self.systems.setValue(systemCount) def music(self, layout): clefs = self.clefs.currentText().upper() length = self.staffCount.value() if clefs and set(clefs) <= set("SATB"): # pad to staff count if necessary clefs = (clefs + clefs[-1] * length)[:length] else: clefs = [None] * length layout.add('Staff', "\\override Clef #'transparent = ##t") if lilyPondVersion() < (2, 13, 4): layout.add("Staff", "\\override VerticalAxisGroup #'minimum-Y-extent = #'(-6 . 4)") music = ['\\new ChoirStaff <<'] for clef in clefs: music.append('\\new Staff {{{0} \\music }}'.format({ 'S': ' \\clef treble', 'A': ' \\clef treble', 'T': ' \\clef "treble_8"', 'B': ' \\clef bass', None: '', }[clef])) music.append('>>') return music
class ScoreProperties(object): """This is only the base class, it should be mixed in with a widget or a different way.""" def createWidgets(self): """Creates all widgets.""" self.createKeySignatureWidget() self.createTimeSignatureWidget() self.createPickupWidget() self.createMetronomeWidget() self.createTempoWidget() def layoutWidgets(self, layout): """Adds all widgets to a vertical layout.""" self.layoutKeySignatureWidget(layout) self.layoutTimeSignatureWidget(layout) self.layoutPickupWidget(layout) self.layoutMetronomeWidget(layout) self.layoutTempoWidget(layout) def translateWidgets(self): self.translateKeySignatureWidget() self.translateTimeSignatureWidget() self.translatePickupWidget() self.tranlateMetronomeWidget() self.translateTempoWidget() def ly(self, node, builder): """Adds appropriate LilyPond command nodes to the parent node. Settings from the builder are used where that makes sense. All widgets must be present. """ self.lyKeySignature(node, builder) self.lyTimeSignature(node, builder) self.lyPickup(node, builder) self.lyTempo(node, builder) def globalSection(self, builder): """Returns a sequential expression between { } containing the output of ly().""" seq = ly.dom.Seq() self.ly(seq, builder) return seq # Key signature def createKeySignatureWidget(self): self.keySignatureLabel = QLabel() self.keyNote = QComboBox() self.keyNote.setModel( listmodel.ListModel(keyNames['nederlands'], self.keyNote)) self.keyMode = QComboBox() self.keyMode.setModel( listmodel.ListModel(modes, self.keyMode, display=listmodel.translate_index(1))) self.keySignatureLabel.setBuddy(self.keyNote) def translateKeySignatureWidget(self): self.keySignatureLabel.setText(_("Key signature:")) self.keyMode.model().update() def layoutKeySignatureWidget(self, layout): """Adds our widgets to a layout, assuming it is a QVBoxLayout.""" box = QHBoxLayout() box.addWidget(self.keySignatureLabel) box.addWidget(self.keyNote) box.addWidget(self.keyMode) layout.addLayout(box) def setPitchLanguage(self, language='nederlands'): self.keyNote.model()._data = keyNames[language or 'nederlands'] self.keyNote.model().update() def lyKeySignature(self, node, builder): """Adds the key signature to the ly.dom node parent.""" note, alter = keys[self.keyNote.currentIndex()] alter = fractions.Fraction(alter, 2) mode = modes[self.keyMode.currentIndex()][0] ly.dom.KeySignature(note, alter, mode, node).after = 1 # Time signature def createTimeSignatureWidget(self): self.timeSignatureLabel = QLabel() self.timeSignature = QComboBox(editable=True) icons = { '(4/4)': symbols.icon('time_c44'), '(2/2)': symbols.icon('time_c22'), } self.timeSignature.setModel( listmodel.ListModel(timeSignaturePresets, self.timeSignature, icon=icons.get)) self.timeSignature.setCompleter(None) self.timeSignatureLabel.setBuddy(self.timeSignature) def translateTimeSignatureWidget(self): self.timeSignatureLabel.setText(_("Time signature:")) def layoutTimeSignatureWidget(self, layout): """Adds our widgets to a layout, assuming it is a QVBoxLayout.""" box = QHBoxLayout() box.addWidget(self.timeSignatureLabel) box.addWidget(self.timeSignature) layout.addLayout(box) def lyTimeSignature(self, node, builder): """Adds the time signature to the ly.dom node parent.""" sig = self.timeSignature.currentText().strip() if '+' in sig: pass # TODO: implement support for \compoundMeter else: if sig == '(2/2)': ly.dom.TimeSignature(2, 2, node).after = 1 elif sig == '(4/4)': ly.dom.TimeSignature(4, 4, node).after = 1 else: match = re.search(r'(\d+).*?(\d+)', sig) if match: if builder.lyVersion >= (2, 11, 44): ly.dom.Line(r"\numericTimeSignature", node) else: ly.dom.Line( r"\override Staff.TimeSignature #'style = #'()", node) num, beat = map(int, match.group(1, 2)) ly.dom.TimeSignature(num, beat, node).after = 1 # Pickup bar def createPickupWidget(self): self.pickupLabel = QLabel() self.pickup = QComboBox() pickups = [''] pickups.extend(durations) self.pickup.setModel( listmodel.ListModel(pickups, self.pickup, display=lambda item: item or _("None"), icon=lambda item: symbols.icon( 'note_{0}'.format(item.replace('.', 'd'))) if item else None)) self.pickup.view().setIconSize(QSize(22, 22)) self.pickupLabel.setBuddy(self.pickup) def translatePickupWidget(self): self.pickupLabel.setText(_("Pickup measure:")) self.pickup.model().update() def layoutPickupWidget(self, layout): box = QHBoxLayout() box.addWidget(self.pickupLabel) box.addWidget(self.pickup) layout.addLayout(box) def lyPickup(self, node, builder): if self.pickup.currentIndex() > 0: dur, dots = partialDurations[self.pickup.currentIndex() - 1] ly.dom.Partial(dur, dots, parent=node) # Metronome value def createMetronomeWidget(self): self.metronomeLabel = QLabel() self.metronomeNote = QComboBox() self.metronomeNote.setModel( listmodel.ListModel( durations, display=None, icon=lambda item: symbols.icon('note_{0}'.format( item.replace('.', 'd'))))) self.metronomeNote.setCurrentIndex(durations.index('4')) self.metronomeNote.view().setIconSize(QSize(22, 22)) self.metronomeEqualSign = QLabel('=') self.metronomeEqualSign.setFixedWidth( self.metronomeEqualSign.minimumSizeHint().width()) self.metronomeValue = QComboBox(editable=True) self.metronomeValue.setModel( listmodel.ListModel(metronomeValues, self.metronomeValue, display=format)) self.metronomeValue.setCompleter(None) self.metronomeValue.setValidator( QIntValidator(0, 999, self.metronomeValue)) self.metronomeValue.setCurrentIndex(metronomeValues.index(100)) self.metronomeTempo = widgets.tempobutton.TempoButton() self.metronomeTempo.tempo.connect(self.setMetronomeValue) self.metronomeLabel.setBuddy(self.metronomeNote) self.metronomeRound = QCheckBox() def layoutMetronomeWidget(self, layout): grid = QGridLayout() grid.addWidget(self.metronomeLabel, 0, 0) box = QHBoxLayout(spacing=0) box.addWidget(self.metronomeNote) box.addWidget(self.metronomeEqualSign) box.addWidget(self.metronomeValue) box.addWidget(self.metronomeTempo) grid.addLayout(box, 0, 1) grid.addWidget(self.metronomeRound, 1, 1) layout.addLayout(grid) def tranlateMetronomeWidget(self): self.metronomeLabel.setText(_("Metronome mark:")) self.metronomeRound.setText(_("Round tap tempo value")) self.metronomeRound.setToolTip( _("Round the entered tap tempo to a common value.")) def setMetronomeValue(self, bpm): """ Tap the tempo tap button """ if self.metronomeRound.isChecked(): l = [abs(t - bpm) for t in metronomeValues] m = min(l) if m < 6: self.metronomeValue.setCurrentIndex(l.index(m)) else: self.metronomeValue.setEditText(str(bpm)) # Tempo indication def createTempoWidget(self): self.tempoLabel = QLabel() self.tempo = widgets.lineedit.LineEdit() completionmodel.complete(self.tempo, "scorewiz/completion/scoreproperties/tempo") self.tempo.completer().setCaseSensitivity(Qt.CaseInsensitive) self.tempoLabel.setBuddy(self.tempo) def layoutTempoWidget(self, layout): box = QHBoxLayout() box.addWidget(self.tempoLabel) box.addWidget(self.tempo) layout.addLayout(box) def translateTempoWidget(self): self.tempoLabel.setText(_("Tempo indication:")) def lyTempo(self, node, builder): """Returns an appropriate tempo indication.""" text = self.tempo.text().strip() if builder.showMetronomeMark: dur = durations[self.metronomeNote.currentIndex()] val = self.metronomeValue.currentText() or '60' elif text: dur = None val = None else: return tempo = ly.dom.Tempo(dur, val, node) if text: ly.dom.QuotedString(text, tempo) def lyMidiTempo(self, node): """Sets the configured tempo in the tempoWholesPerMinute variable.""" node['tempoWholesPerMinute'] = ly.dom.Scheme(self.schemeMidiTempo()) def schemeMidiTempo(self): """Returns a string with the tempo like '(ly:make-moment 100 4)' from the settings.""" base, mul = midiDurations[self.metronomeNote.currentIndex()] val = int(self.metronomeValue.currentText() or '60') * mul return "(ly:make-moment {0} {1})".format(val, base) def lySimpleMidiTempo(self, node): r"""Return a simple \tempo x=y node for the currently set tempo.""" dur = durations[self.metronomeNote.currentIndex()] val = self.metronomeValue.currentText() or '60' return ly.dom.Tempo(dur, val, node)