diff --git a/engine/block.py b/engine/block.py index ff1ad8b..7190425 100644 --- a/engine/block.py +++ b/engine/block.py @@ -344,6 +344,16 @@ class Block(object): def isAppending(self): return self.localCursorIndex == len(self.data) #len counts from 1 and the cursorIndex from 0. So if they are the same we are one cursor position right of last item + def zeroLogicalDuration(self)->bool: + """Return true if this has no actual items with duration in it. + A block with only text items and barlines is empty. + Minimum tick duration does not affect isEmpty""" + for item in self.data: + if item.logicalDuration() > 0: + return False + return True + + def lilypond(self, carryLilypondRanges): """Called by track.lilypond(), returns a string. carryLilypondRanges is handed from item to item for ranges diff --git a/engine/main.py b/engine/main.py index 4db919c..6c66bf9 100644 --- a/engine/main.py +++ b/engine/main.py @@ -917,6 +917,8 @@ class Data(template.engine.sequencer.Score): return False for block in startBlock.linkedContentBlocks: #all linked blocks in all tracks, including currentBlock. All blocks are unique. The content is not. + if not block.parentTrack: + continue #hotfix 2022-08-05. It was "None" . #TODO maybe from deleting a link previously that was in multiple tracks? blockIndex = block.parentTrack.blocks.index(block) try: followUpBlock = block.parentTrack.blocks[blockIndex +1] @@ -1006,7 +1008,7 @@ class Data(template.engine.sequencer.Score): newLeft.name = block.name newLeft.minimumInTicks = block.minimumInTicks / 2 assert newLeftId in Block.allBlocks - assert newLeft.parentTrack is block.parentTrack + #assert newLeft.parentTrack is block.parentTrack #that is just not true. We have linked blocks in other tracks as well. assert newLeft in protoBlockLeft.linkedContentBlocks newRight = protoBlockRight.contentLink() @@ -1014,7 +1016,7 @@ class Data(template.engine.sequencer.Score): newRight.name = block.name newRight.minimumInTicks = newLeft.minimumInTicks assert newRightId in Block.allBlocks - assert newRight.parentTrack is block.parentTrack + #assert newRight.parentTrack is block.parentTrack assert newRight in protoBlockRight.linkedContentBlocks positionToInsert = blockOrder.index(id(block)) @@ -1088,9 +1090,9 @@ class Data(template.engine.sequencer.Score): Each object controls what it exports. Overrides are possible at every level. """ tempoStaff = self.tempoTrack.lilypond() if "metronome" in self.metaData and self.metaData["metronome"] else "" - data = {track:track.lilypond() for track in self.tracks} #processed in the lilypond module + data = {track:track.lilypond() for track in self.tracks if not track.zeroLogicalDuration() } #processed in the lilypond module #the template file name is in metadata, but it can be empty or not existient - #From Template has direct access to the score metadata and WILL destructively modify it's own parameters, like the lilypond template entry. + #From Template has direct access to the score metadata and WILL destructively modify it's own parameters, like the "lilypond template" entry itself. return fromTemplate(session = self.parentSession, data = data, meta = self.metaData, tempoStaff = tempoStaff) diff --git a/engine/track.py b/engine/track.py index 5df38b4..ee7be09 100644 --- a/engine/track.py +++ b/engine/track.py @@ -846,6 +846,15 @@ class Track(object): #we don't need to unregister anything from cbox. + def zeroLogicalDuration(self)->bool: + """Return true if this has no actual items with duration in it. + A track with only text items and barlines is empty. + Minimum tick duration does not affect isEmpty""" + for block in self.blocks: + if not block.zeroLogicalDuration(): + return False + return True + #Save / Load / Export def lilypond(self): """Called by score.lilypond(), returns a string.