Source code for neoscore.western.key_signature

from typing import Optional, Union

from neoscore.core.layout_controllers import NewLine
from neoscore.core.music_text import MusicText
from neoscore.core.point import Point
from neoscore.core.positioned_object import PositionedObject, render_cached_property
from neoscore.core.units import ZERO, Unit
from neoscore.western import clef_type
from neoscore.western.accidental_type import AccidentalType
from neoscore.western.key_signature_type import KeySignatureType
from neoscore.western.staff import Staff
from neoscore.western.staff_object import StaffObject


[docs]class KeySignature(PositionedObject, StaffObject): """A logical and graphical key signature. The signature will be rendered initially at the given ``pos_x``, and at the beginning of subsequent lines until a new ``KeySignature`` is encountered. This does not currently support natural (cancelling key signatures). """ # Type sentinel used to hackily check type # without importing the type, risking cyclic imports. _neoscore_key_signature_type_marker = True
[docs] def __init__( self, pos_x: Unit, staff: Staff, key_signature_type: Union[KeySignatureType, str], ): """ Args: pos_x (Unit): The x position relative to the parent staff. staff (Staff): The parent staff key_signature_type (KeySignatureType or str): A description of the key signature. Any KeySignatureType may be used, or a str of one's name. """ PositionedObject.__init__(self, Point(pos_x, ZERO), staff) StaffObject.__init__(self, staff) self._key_signature_type = ( key_signature_type if isinstance(key_signature_type, KeySignatureType) else KeySignatureType[key_signature_type.upper()] )
@property def key_signature_type(self) -> KeySignatureType: """KeySignatureType: A logical description of the key signature.""" return self._key_signature_type @property def breakable_length(self) -> Unit: """Key signatures extend until another is found in the staff.""" return self.staff.distance_to_next_of_type(self) @render_cached_property def visual_width(self) -> Unit: """The visual width of this key signature This assumes that key signatures have the same width in any clef, and that the accidentals used in key signatures are 1 staff unit wide. """ max_x = 0 for letter, accidental_type in self.key_signature_type.value.items(): if accidental_type is None: continue if accidental_type == AccidentalType.SHARP: pos_tuple = clef_type.TREBLE.key_signature_sharp_layout[letter] else: pos_tuple = clef_type.TREBLE.key_signature_flat_layout[letter] max_x = max(max_x, pos_tuple[0]) # Add max position + 1 for accidental width return self.staff.unit(max_x + 1) def _render_occurrence( self, pos: Point, flowable_line: Optional[NewLine], for_line_start: bool ): inside_flowable = bool(flowable_line) # when inside flowable this pos is absolute, otherwise relative base_x = pos.x base_y = pos.y fringe_layout = self.staff.fringe_layout_at(flowable_line) if for_line_start: base_x += fringe_layout.key_signature clef = self.staff.active_clef_at(fringe_layout.pos_x_in_staff) for letter, accidental_type in self.key_signature_type.value.items(): if accidental_type is None: continue if accidental_type == AccidentalType.SHARP: pos_tuple = clef.clef_type.key_signature_sharp_layout[letter] else: pos_tuple = clef.clef_type.key_signature_flat_layout[letter] acc_pos = Point( base_x + self.staff.unit(pos_tuple[0]), base_y + self.staff.unit(pos_tuple[1]), ) parent = None if inside_flowable else self accidental = MusicText( acc_pos, parent, accidental_type.value, self.staff.music_font ) accidental.render() accidental.remove()
[docs] def render_complete( self, pos: Point, flowable_line: Optional[NewLine] = None, flowable_x: Optional[Unit] = None, ): for_line_start = False if flowable_line: if flowable_line.flowable_x == flowable_x: for_line_start = True elif self.x == ZERO: for_line_start = True self._render_occurrence(pos, flowable_line, for_line_start)
[docs] def render_before_break(self, pos: Point, flowable_line: NewLine, flowable_x: Unit): fringe_layout = self.staff.fringe_layout_at(flowable_line) for_line_start = fringe_layout.pos_x_in_staff == self.pos_x_in_staff self._render_occurrence(pos, flowable_line, for_line_start)
[docs] def render_spanning_continuation( self, pos: Point, flowable_line: NewLine, object_x: Unit ): self._render_occurrence(pos, flowable_line, True)
[docs] def render_after_break(self, pos: Point, flowable_line: NewLine, object_x: Unit): self._render_occurrence(pos, flowable_line, True)