cursor.next hangs when last measure not full
I believe the following is a bug, tho correct me if I am wrong:
When using cursor.next()
in a plugin, if the last measure of the score is not fully filled out with notes (to match the measure's timesig) the cursor 'hangs' at the last measure in the score. Meaning that repeated calls to cursor.next()
have no effect, the cursor simply stays at the same tick.
This is a problem when walking a staff inside a plugin as the ususal method to detect the end of processing doesn't work in this edge case.
Example score and plugin code attached.
First: select measures 10-11 in the test score and run the plugin - all is fine.
Next: Select measures 10-12 and run the plugin and you should see that the watchdog counter trips in the staff-walking loop.
If you inspect the console debug output you can see that the cursor is just not advancing past that final rest in the last measure; thus causing an infinite loop condition.
Note that if the last measure is fully populated with notes all is well, no infinite loop occurs.
The staff walking loop, with relevant debug output, is in the function makeTAB()
. The user's selection range start/end ticks is determined in QtObject oSelection.storeUserRange()
, which does the usual accounting for the 'wrap past last tick = 0' case.
I am using: MuseScore-3.6.2.548021370-x86_64.AppImage on Linux.
Attachment | Size |
---|---|
Issue_LastMeasureCursorAdvance.mscz | 16.76 KB |
MtnDulcimer-SMN2DiatonicTAB_rel-1.3.qml | 44.02 KB |
Comments
UPDATE: After running some experiments I have discovered that when adding the last note, or rest, in the score the cursor will not automatically advance after adding that last note or rest. Due to how I organized the work-flow in my code I am restoring the cursor position after reading the info off one staff and writing new content onto a different staff so that my staff-walking loop doesn't have to make assumptions about what was written out to the other staff I am updating. So when I hit the last note/rest in the score my note/rest writing functions where simply assuming the cursor advanced after my staff write operations. I was able to fix the problem by testing to see if the cursor had advanced or not after writing out each note or rest, and if not I then don't try to move the back to what I think was it's prior position.
(I assume this Musescore non cursor advance behaviour on the last ChordRest is because there is no longer any room left for the cursor to advance to. My assumption is based on the further assumption that when Musescore is adding a note and then looking to advance the cursor after doing so it is only going to advance the cursor into space that would be valid for writing a next note onto. So, for example, it isn’t going to advance the cursor onto a barline. So, then, when we are at the end of a score and we’ve just written out a note that has used up all the beats in that measure - well, there is no longer any note-writing space left in the score; so Musescore just leaves the cursor sitting on that final note.)
Now, as to whether you'd consider this behaviour to be a bug or, in old IBM terms, "works as designed" is probably a matter of taste. For me the inconsistency feels buggy. But probably the only clean solution is to not advance the cursor automatically on any ChordRest write operation. But it's too late to revisit that design choice given all the plugin code already out there depending on it.
Could this relate to #319079: Shift-selecting lyrics doesn't work with last word?
In reply to Could this relate to #319079… by Jojo-Schmitz
I could see that it might be; tho my guess is that this is unrelated. I just tried the latest nightly build Linux appimage and that lyrics selection issue is still present so not able to test the theory as yet. My guess is the plugin API is simply using the underlying C++ user interface routines for note entry and thus it stops cursor advancement at the last beat since, in the context of a human at the keyboard, you really do want that behaviour.
For anyone coming across this issue posting who is experiencing the same edge-case - after a hike this morning it occurred to me that probably the cleanest way to return the cursor to it's prior location, the location just before you began doing some note writing, is to store the cursor tick location before you change staffs and start to write out your notes or rests. When done, you can go back to your input staff, and then return the cursor to it's prior read-point using
cursor.rewindToTick(int)
. Then in your staff-walking loop you can safely use cursor.next(), which under these conditions will return a NULL segment once it falls off the end of the score.