Source code for neoscore.western.clef

from typing import Optional

from neoscore.core.brush import BrushDef
from neoscore.core.layout_controllers import NewLine
from neoscore.core.music_font import MusicFont
from neoscore.core.music_text import MusicText
from neoscore.core.pen import PenDef
from neoscore.core.point import Point
from neoscore.core.positioned_object import render_cached_property
from neoscore.core.units import ZERO, Unit
from neoscore.western.clef_type import ClefType, ClefTypeDef
from neoscore.western.staff import Staff
from neoscore.western.staff_object import StaffObject


[docs]class Clef(MusicText, StaffObject): """A graphical and logical staff clef. These are drawn at the initially specified position, and at the beginning of new lines in the parent :obj:`.Staff` until a new ``Clef`` is encountered or the end of the ``Staff``. ``Staff`` uses these to map pitches to vertical positions. """
[docs] def __init__( self, pos_x: Unit, staff: Staff, clef_type: ClefTypeDef, font: Optional[MusicFont] = None, brush: Optional[BrushDef] = None, pen: Optional[PenDef] = None, ): """ Args: pos_x: The x position in the staff staff: The parent staff clef_type: The type of clef. String names of common clefs may be given as a convenience; see :obj:`.ClefTypeDef`. font: The font used. Defaults to the staff's font. brush: The brush to fill in text shapes with. pen: The pen to trace text outlines with. This defaults to no pen. """ StaffObject.__init__(self, staff) # Init with placeholder y position and text; clef_type setter will update MusicText.__init__(self, (pos_x, ZERO), staff, "", font, brush, pen) self.clef_type = clef_type
@property def clef_type(self) -> ClefType: """The type of clef, both logical and graphical.""" return self._clef_type @clef_type.setter def clef_type(self, value: ClefTypeDef): self._clef_type = ClefType.from_def(value) if callable(self._clef_type.staff_pos): staff_pos = self._clef_type.staff_pos(self.staff.line_count) else: staff_pos = self._clef_type.staff_pos self.y = self.staff.unit(staff_pos) self.text = self._clef_type.glyph_name if callable(self.clef_type.middle_c_staff_pos): middle_c_staff_pos = self.clef_type.middle_c_staff_pos( self.staff.line_count ) else: middle_c_staff_pos = self.clef_type.middle_c_staff_pos self._middle_c_staff_position = self.staff.unit(middle_c_staff_pos) @render_cached_property def breakable_length(self) -> Unit: """Find the length in the staff during which this clef is active. This is defined as the distance relative to the staff until the next clef is encountered. If no further clefs are found, this is the remaining length of the staff. """ # Iterate through the (typically cached) staff clef list # The list is sorted by x position relative to the staff, # which means the first loop pass in which self_staff_x # has been assigned must be the next clef in the staff. self_staff_x = None for staff_x, clef in self.staff.clefs: if self_staff_x is not None: return staff_x - self_staff_x if clef is self: self_staff_x = staff_x # No later clefs exist; return the remaining staff length return self.staff.breakable_length - self_staff_x @property def middle_c_staff_position(self) -> Unit: """The vertical position of middle C for this clef 0 means exactly at the top staff line. Positive values extend *downward* below the top staff line while negative values extend *upward* above the top staff line. This value is primarily useful in calculations of pitch staff positions which take a clef into account """ return self._middle_c_staff_position
[docs] def render_complete( self, pos: Point, flowable_line: Optional[NewLine] = None, flowable_x: Optional[Unit] = None, ): fringe_layout = self.staff.fringe_layout_at(flowable_line) if fringe_layout.pos_x_in_staff == self.pos_x_in_staff: pos = Point(pos.x + fringe_layout.clef, pos.y) super().render_complete(pos, flowable_line)
[docs] def render_before_break(self, pos: Point, flowable_line: NewLine, flowable_x: Unit): fringe_layout = self.staff.fringe_layout_at(flowable_line) if fringe_layout.pos_x_in_staff == self.pos_x_in_staff: pos = Point(pos.x + fringe_layout.clef, pos.y) super().render_complete(pos, flowable_line)
[docs] def render_spanning_continuation( self, pos: Point, flowable_line: NewLine, object_x: Unit ): fringe_layout = self.staff.fringe_layout_at(flowable_line) super().render_complete(Point(pos.x + fringe_layout.clef, pos.y), flowable_line)
[docs] def render_after_break(self, pos: Point, flowable_line: NewLine, object_x: Unit): fringe_layout = self.staff.fringe_layout_at(flowable_line) super().render_complete(Point(pos.x + fringe_layout.clef, pos.y), flowable_line)