So far we’ve discussed how to draw objects with exactly one graphical representation placed fairly literally on a page, but a common feature of musical scores is flowable containers like staff systems. We can imagine flowable containers as long horizontal strips which are broken into different segments as they wrap around the page edges. Objects within these containers may need to be drawn multiple times—and in multiple locations—if they span those breaks.
flow = Flowable(ORIGIN, None, Mm(500), Mm(15)) Text(ORIGIN, flow, "text on first line") Text((Mm(200), ZERO), flow, "text on second line") Text((Mm(300), ZERO), flow, "text spanning line break")
Objects placed within a flowable are automatically rendered in the flowed space. When an object fits entirely in a given flowed line, it’s rendered like usual, just in a different place. Things get more interesting when objects span line breaks. Simple classes like
Text simply break across the line (clipping at the line edges), but other classes can provide variable rendering behavior as an object appears in different places relative to flowable breaks.
By default, flowable lines run as far as they can within the live page area and break at page margins. Flowables can also be made to respect break opportunities by proactively breaking when one is encountered within its
break_threshold. When calculating its layout,
Flowable finds all descendents which subclass
BreakOpportunity; it then checks at every line break whether any opportunity is placed between the page margin and
break_threshold to the left of it. If such opportunities exist, it will break at the last one encountered.
from neoscore.core.break_opportunity import BreakOpportunity class BreakHintText(Text, BreakOpportunity): pass flow = Flowable(ORIGIN, None, Mm(500), Mm(15), break_threshold=Mm(50)) paper = neoscore.document.pages.paper # Outline the flowable for visualization Path.rect(ORIGIN, flow, Mm(500), Mm(15), brush=Brush.no_brush()) # And draw a line over the break threshold Path.straight_line((paper.live_width - Mm(50), ZERO), None, (ZERO, Mm(75)), pen=Pen("#ff0000", pattern=PenPattern.DASH)) BreakHintText((Mm(100), Mm(8)), flow, "opp 1") BreakHintText((Mm(200), Mm(8)), flow, "opp 2") BreakHintText((Mm(300), Mm(8)), flow, "opp 3") BreakHintText((Mm(430), Mm(6)), flow, "opp 4") BreakHintText((Mm(440), Mm(12)), flow, "opp 5")
Flowable.break_threshold is zero by default, meaning break opportunities are always ignored. You can also set it to some value larger than the live page width to make it break at every opportunity.
Flowables can also have dynamically calculated left margins. This is useful in situations like western notation staves, where each staff system line has a left fringe with a clef, key signature, etc., and it is not desirable for that fringe to occupy the flowable coordinate space. In situations like this, the flowable can be made to leave a margin on its side where those elements can be laid out. See