Source code for neoscore.western.notehead

from __future__ import annotations

from typing import Optional, cast

from neoscore.core.music_font import MusicFont
from neoscore.core.music_text import MusicText
from neoscore.core.positioned_object import PositionedObject, render_cached_property
from neoscore.core.units import ZERO, Unit
from neoscore.western import notehead_tables
from neoscore.western.duration import Duration, DurationDef
from neoscore.western.duration_display import DurationDisplay
from neoscore.western.notehead_tables import NoteheadTable
from neoscore.western.pitch import Pitch, PitchDef
from neoscore.western.staff_object import StaffObject


[docs]class Notehead(MusicText, StaffObject): """A simple notehead automatically selected and vertically positioned."""
[docs] def __init__( self, pos_x: Unit, parent: PositionedObject, pitch: PitchDef, duration: DurationDef, font: Optional[MusicFont] = None, table: NoteheadTable = notehead_tables.STANDARD, glyph_override: Optional[str] = None, ): """ Args: pos_x: The x-axis position relative to ``parent``. The y-axis position is calculated automatically based on ``pitch`` and contextual information in ``self.staff``. parent: Must be a :obj:`.Staff` or a descendant of one. pitch: The pitch used to vertically place the notehead. See :obj:`.Pitch` and :obj:`.PitchDef` duration: The logical duration of the notehead. This is used to determine the glyph style. font: If provided, this overrides any font found in the ancestor chain. table: The set of noteheads to use according to ``duration``. See :obj:`.notehead_tables`. glyph_override: A SMuFL glyph name. If given, this overrides the glyph normally looked up with ``duration`` from ``table``. """ self.pitch = pitch self.duration = duration self._table = table self._glyph_override = glyph_override MusicText.__init__( self, (pos_x, ZERO), parent, "", font, ) StaffObject.__init__(self, parent) self._update_music_text()
@property def visual_width(self) -> Unit: return self.bounding_rect.width @property def pitch(self) -> Pitch: return self._pitch @pitch.setter def pitch(self, value: PitchDef): rebuild_needed = hasattr(self, "_pitch") self._pitch = Pitch.from_def(value) if rebuild_needed: self._update_music_text() @property def duration(self) -> Duration: return self._duration @duration.setter def duration(self, value: DurationDef): rebuild_needed = hasattr(self, "_duration") value = Duration.from_def(value) if value.display is None: raise ValueError(f"{value} cannot be represented as a single note") self._duration = value if rebuild_needed: self._update_music_text() @property def table(self) -> NoteheadTable: return self._table @table.setter def table(self, value: NoteheadTable): self._table = value self._update_music_text() @property def glyph_override(self) -> Optional[str]: return self._glyph_override @glyph_override.setter def glyph_override(self, value: Optional[str]): self._glyph_override = value self._update_music_text() @render_cached_property def staff_pos(self) -> Unit: """The y-axis position in the staff. This is derived from the ``pitch`` and the staff's active clef at this object's position. """ return self.staff.middle_c_at(self.pos_x_in_staff) + self.staff.unit( self.pitch.staff_pos_from_middle_c ) def _update_music_text(self): duration_display = cast(DurationDisplay, self.duration.display) if self._glyph_override: glyph = self._glyph_override else: glyph = self._table.lookup_duration(duration_display.base_duration) self.text = glyph self.y = self.staff_pos - self.staff.map_to(self.parent).y