Source code for neoscore.western.metronome_mark

from __future__ import annotations

from typing import Optional

from neoscore.core.font import Font
from neoscore.core.has_music_font import HasMusicFont
from neoscore.core.music_char import MusicChar
from neoscore.core.music_font import MusicFont
from neoscore.core.music_text import MusicStringDef, MusicText
from neoscore.core.point import ORIGIN, PointDef
from neoscore.core.positioned_object import PositionedObject
from neoscore.core.raw_music_char import RawMusicChar
from neoscore.core.text import Text
from neoscore.core.units import ZERO


[docs]class MetronomeMark(PositionedObject, HasMusicFont): """A combined :obj:`MusicText` and :obj:`Text` for use in metronome markings"""
[docs] def __init__( self, pos: PointDef, parent: Optional[PositionedObject], music_str: MusicStringDef, text_str: str, music_font: Optional[MusicFont] = None, text_font: Optional[Font] = None, spaces_between_music_chars: bool = True, ): """ Args: pos: Position relative to ``parent`` parent: If no ``music_font`` is given, this or one of its ancestors must implement :obj:`.HasMusicFont`. music_str: The :obj:`MusicText` string portion of the mark. Glyphs should typically come from `the relevant SMuFL range <https://w3c.github.io/smufl/latest/tables/metronome-marks.html>`_. text_str: The plain-text string portion of the mark. This will typically be of the form "= 123". music_font: If provided, this overrides any music font found in the ancestor chain. text_font: The font for the plain text portion of the mark. spaces_between_music_chars: Whether to insert spaces between each specified music character. This is needed to correctly space rhythm dots if used. """ PositionedObject.__init__(self, pos, parent) self._music_font = music_font or HasMusicFont.find_music_font(parent) self._music_text_obj = MusicText(ORIGIN, self, music_str, self._music_font) if spaces_between_music_chars and len(self._music_text_obj.music_chars) > 1: self._music_text_obj.music_chars = self._insert_spaces( self._music_text_obj.music_chars ) music_text_rect = self._music_text_obj.bounding_rect music_text_end_x = music_text_rect.x + music_text_rect.width # Because text objects can't have leading or trailing spaces, to get a space # between these elements we do a terrible hack to find the width of a space and # offset accordingly. See https://github.com/DigiScore/neoscore/issues/34 text_obj = Text((music_text_end_x, ZERO), self, " " + text_str, text_font) width_including_space = text_obj.bounding_rect.width text_obj.text = text_str text_obj.x += width_including_space - text_obj.bounding_rect.width self._plain_text_obj = text_obj
@property def music_font(self) -> MusicFont: return self._music_font @property def music_text_obj(self) -> MusicText: return self._music_text_obj @property def plain_text_obj(self) -> Text: return self._plain_text_obj def _insert_spaces(self, chars: List[MusicChar]) -> List[MusicChar]: result = [] blank = RawMusicChar(self.music_font, " ") for i, char in enumerate(chars): result.append(char) if i < len(chars) - 1: result.append(blank) return result