Source code for musicscore.staff

from typing import Optional, Set

from musicscore.clef import Clef
from musicscore.exceptions import StaffHasNoParentError, AlreadyFinalizedError, AddChordError
from musicscore.finalize import FinalizeMixin
from musicscore.musictree import MusicTree
from musicscore.quantize import QuantizeMixin
from musicscore.voice import Voice
from musicscore.xmlwrapper import XMLWrapper
from musicxml.xmlelement.xmlelement import XMLStaff

__all__ = ['Staff']


[docs]class Staff(MusicTree, QuantizeMixin, FinalizeMixin, XMLWrapper): """ Parent type: :obj:`~musicscore.measure.Measure` Child type: :obj:`~musicscore.voice.Voice` """ _ATTRIBUTES = {'clef', 'default_clef', 'number'} _ATTRIBUTES = _ATTRIBUTES.union(MusicTree._ATTRIBUTES) _ATTRIBUTES = _ATTRIBUTES.union(QuantizeMixin._ATTRIBUTES) XMLClass = XMLStaff def __init__(self, number=None, clef=None, **kwargs): super().__init__() self._xml_object = self.XMLClass(value_=1, **kwargs) self._number = None self._clef = None self.clef = clef self.number = number @property def clef(self) -> Clef: """ :type: :obj:`~musicscore.clef.Clef` :return: Clef associated with the staff. :rtype: :obj:`~musicscore.clef.Clef` """ if not self._clef: if self.get_previous_staff() and self.get_previous_staff().clef: self._clef = self.get_previous_staff().clef.__copy__() self._clef.show = False return self._clef @clef.setter def clef(self, val): if val is not None and not isinstance(val, Clef): raise TypeError if val and not val.number and self.clef: val.number = self.clef.number self._clef = val @property def number(self): """ :type: None or int. If None number is set to 1. :return: positive int or None """ if self._number is not None: return self.xml_object.value_ else: return self._number @number.setter def number(self, val): self._number = val if val is None: self.xml_object.value_ = 1 else: self.xml_object.value_ = val
[docs] def add_child(self, child: Voice) -> Voice: """ - Adds a :obj:`~musicscore.voice.Voice` as child to staff. - If voice number is ``None``, it is determined as length of children + 1. - If voice number is already set an is not equal to length of children + 1. a ``ValueError`` is raised. - If Staff has no parent :obj:`~musicscore.measure.Measure` a :obj:`~musicscore.exceptions.StaffHasNoParentError` is raised. - After adding voice its :obj:`~musicscore.voice.Voice.update_beats()` is called. :param child: :obj:`~musicscore.voice.Voice` , required :return: added :obj:`~musicscore.voice.Voice` """ if self._finalized is True: raise AlreadyFinalizedError(self, 'add_child') self._check_child_to_be_added(child) if not self.up: raise StaffHasNoParentError('A child Voice can only be added to a Staff if staff has a Measure parent.') if child.number is not None and child.number != len(self.get_children()) + 1: raise ValueError(f'Voice number must be None or {len(self.get_children()) + 1}') if child.number is None: child.number = len(self.get_children()) + 1 else: child.number = len(self.get_children()) + 1 child._parent = self self._children.append(child) return child
def add_chord(self, *args, **kwargs): raise AddChordError()
[docs] def add_voice(self, voice_number: Optional[int] = None) -> Voice: """ - Creates and adds a new :obj:`~musicscore.voice.Voice` object as child to staff. - If voice number is greater than length of children + 1 all missing voices are created and added first. :param voice_number: positive int or None. If ``None`` voice number is determined as length of children + 1. :return: new :obj:`~musicscore.voice.Voice` """ if self._finalized is True: raise AlreadyFinalizedError(self, 'add_voice') if voice_number is None: voice_number = len(self.get_children()) + 1 voice_object = self.get_voice(voice_number=voice_number) if voice_object is None: for _ in range(voice_number - len(self.get_children())): voice_object = self.add_child(Voice()) voice_object.update_beats() return voice_object voice_object.update_beats() return voice_object
[docs] def fill_with_rests(self): for voice in self.get_children(): voice.fill_with_rests()
[docs] def get_previous_staff(self) -> Optional['Staff']: """ :return: :obj:`Staff` with the same number in previous :obj:`~musicscore.measure.Measure` if existing else ``None`` """ if self.up and self.up.previous: my_index = self.up.get_children().index(self) try: return self.up.previous.get_children()[my_index] except IndexError: return None return None
[docs] def get_last_pitch_steps_with_accidentals(self) -> Set[str]: """ :return: A set of pitch steps with not natural accidental. This method is used to keep track of accidental signs which needs to be shown hidden. :rtype: Set[str] """ output = set() for v in self.get_children(): if v.get_chords(): last_chord = v.get_chords()[-1] if not last_chord.is_rest: for m in last_chord.midis: if m.accidental.sign != 'natural': step = m.accidental.get_pitch_parameters()[0] if step not in output: output.add(step) return output