Make a bundle reopenable, and number slices
A bundle was a one-way trip. The slice images are output and the cuts that produced them lived only in the producer's own project file, so a bundle someone handed you meant cutting the score again from scratch. The manifest now carries the geometry, in a `source` block: per page the cut polylines, skew, levels and content rectangle, all in normalised coordinates so they survive any render resolution, and per slice the page and slot it came from. Discards are stated by omission — a slot no slice claims was discarded — since shipping a discarded slice's image would defeat discarding it. `noteman-slicer open song.zip` unpacks the archived PDF, rebuilds the project from that geometry, restores markers, engravings and the title block, and opens the editor. Jump destinations go back from an array index to the (page, slot) the editor works in. The images in the zip are discarded: the PDF is what the pipeline renders from. Re-exporting a reopened bundle reproduces its manifest exactly. It refuses to overwrite a PDF or project file already sitting there, because the obvious place to unpack is where someone's unfinished cuts live. Separately, every slice can now carry the measure it starts at, not just a re-engraved one — a scanned system is numbered in the score the same way, and noteman wants to answer "take it from bar 33" about either. It moves off the replacement onto the page, alongside markers and discards, and out of the bundle's engraving object onto the slice.
This commit is contained in:
+16
-11
@@ -96,9 +96,9 @@ class EngraveWindow(QDialog):
|
||||
self.setWindowTitle(f"Re-engrave — page {page + 1}, slice {slot + 1}")
|
||||
self.setModal(False)
|
||||
|
||||
state = project.pages[page]
|
||||
self.replacement = state.replacements[slot] or self._seed()
|
||||
state.replacements[slot] = self.replacement
|
||||
self.state = project.pages[page]
|
||||
self.replacement = self.state.replacements[slot] or self._seed()
|
||||
self.state.replacements[slot] = self.replacement
|
||||
|
||||
rows = QSplitter(Qt.Vertical)
|
||||
rows.addWidget(self._image_panel("Scanned", _pixmap(original)))
|
||||
@@ -182,9 +182,11 @@ class EngraveWindow(QDialog):
|
||||
row.addWidget(self.print_time, 1)
|
||||
top.addRow("Time", row)
|
||||
|
||||
# Per slice, unlike key and time: which measure a system starts at is
|
||||
# the one thing that changes with every slice and cannot be inherited.
|
||||
self.bar = QLineEdit("" if self.replacement.bar is None else str(self.replacement.bar))
|
||||
# A property of the slice, not of the replacement — the same field the
|
||||
# main panel shows for a scanned slice — so it survives discarding the
|
||||
# engraving. Unlike key and time it cannot be inherited from the song.
|
||||
current = self.state.bars[self.slot]
|
||||
self.bar = QLineEdit("" if current is None else str(current))
|
||||
self.bar.setValidator(QIntValidator(1, 9999, self.bar))
|
||||
self.bar.setFixedWidth(70)
|
||||
self.bar.setPlaceholderText("none")
|
||||
@@ -256,7 +258,7 @@ class EngraveWindow(QDialog):
|
||||
self._refresh_source()
|
||||
|
||||
def _bar_changed(self, text: str) -> None:
|
||||
self.replacement.bar = int(text) if text.strip().isdigit() else None
|
||||
self.state.bars[self.slot] = int(text) if text.strip().isdigit() else None
|
||||
self._refresh_source()
|
||||
|
||||
def _settings_changed(self) -> None:
|
||||
@@ -273,15 +275,18 @@ class EngraveWindow(QDialog):
|
||||
self.project.pages[self.page_index].replacements[self.slot] = None
|
||||
self.accept()
|
||||
|
||||
def _refresh_source(self) -> None:
|
||||
self.generated.setPlainText(
|
||||
lilypond.generate(self.replacement, self.project.key, self.project.time)
|
||||
def _source(self) -> str:
|
||||
return lilypond.generate(
|
||||
self.replacement, self.project.key, self.project.time, self.state.bars[self.slot]
|
||||
)
|
||||
|
||||
def _refresh_source(self) -> None:
|
||||
self.generated.setPlainText(self._source())
|
||||
|
||||
# -- rendering --------------------------------------------------------
|
||||
|
||||
def render(self) -> None:
|
||||
source = lilypond.generate(self.replacement, self.project.key, self.project.time)
|
||||
source = self._source()
|
||||
self.status.setStyleSheet("color: #808080;")
|
||||
self.status.setText("rendering…")
|
||||
self.repaint()
|
||||
|
||||
Reference in New Issue
Block a user