Can a plugin access on-time/off-time?

• Jul 3, 2019 - 15:40
Reported version
S5 - Suggestion

Can I write a plugin to impart a desired phrasing upon a run of notes?

For more detail on this request see these forum posts in order:

This is the original post:

This post continues the discussion in the are of implementation:



Doing that now. I managed to force it to the right Qt by renaming the old 5.12 subdir, but the built app couldn't find the Qt import files from the plugins. I unrenamed the directory, uninstalled 5.12, same thing. Rebuilding now after make clean.

Success on the Xcode front; what other neat MS-specific features do I get besides the "Debugger" note context window and the Debug menu? The instructions should note that there are three nonfatal red "issues" about the autogen script ("no classes found", but tool did not return nonzero, so build succeeds). Worked for me without env var adjustments. The "About" box should note that this is a debug build.

Those errors are from MOC (Qt's Meta-Object-Compiler) and basically warnings. Haven't been able to get them removed so far, those files it complains about claim to be/contain Q_OBJECTs, but really don't, MOC is just to stupid to detect that all that code is #ifdefd out.
Debug builds do mention this in the about box "Unstable Prerelease for Version:", at least they do for me.
Edit: actually no, prereleases do that, the way to tell debug builds from others is to check whether the Debug menu is present.

Why? Those (practically) never are publicly visible, no development build is actually a devug build.

Anyway, there you have a potential candidate for your first PR ;-)

We truly need Note.Chord to be accessible via QML, (and segment from chord) or you can't get context (including grace notes) from a clicked-on note. I know that if I understood the well code enough I could do that myself, but Dale could do it faster ... I can search the whole score for the containing chord for now ...

I did a make clean and now both make release and make xcode fail with this (I cannot build at all).
Unknown CMake command "QT5_ADD_RESOURCES".

-- Configuring incomplete, errors occurred!
See also "/Users/bsg/MuseScore/build.release/CMakeFiles/CMakeOutput.log".
xcodebuild: error: 'mscore.xcodeproj' does not exist.
xcodebuild: error: 'mscore.xcodeproj' does not exist.
make: *** [release] Error 66

There are multiple ways to do this, but I would do the following:
git fetch DLLarson chord-add-remove
git cherry-pick DLLarson/chord-add-remove

$ git cherry-pick DLLarson/chord-add-remove
error: could not apply c3151b8... Restore Chord.remove() and Chord.add() methods. + collect_artifacts
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add ' or 'git rm '
hint: and commit the result with 'git commit'

Hi everybody...

The PR's have been inspected and there is one change affecting the scripting. The property selections is now called elements to match the internal name. I've pushed the changes to my test branch:…

I've attached updated test scripts to this post. It's feeling close to finished!


How do I get the whole @#$@#$#$ tree back into a consistent state? I'm trying to restore yesterday's branch, but getting
$ git checkout DLLarson/qml-pr5224-pr5243-playevent-selection
mscore/plugin/api/elements.h: needs merge
error: you need to resolve your current index first

>How do I merge that other branch (chord-add-remove) with what I have? I fear switching to it will ignore the other changes.<

Would you like me to stack it on top of the others?


Dale: Please create a consistent branch with all the changes, including element.parent, which I truly need, thanks. Right now I don't have a buildable tree and I forgot the git magic to restore it to workingness (I knew years ago). I wish there were a channel for discussing this without posting on the forum.

I just added the chord.add/remove PR to the uber feature branch and pushed it up (even though the branch name doesn't reflect it.)


Is there any problem with deleting the whole MuseScore tree, re-cloning, and re-fetch/checkout Dale's branch?
Seems like overkill, but knock yourself out. Did you at least try git cherry-pick --abort ?

This will get the fresh code pretty fast:

git clone --depth 1 -b qml-pr5224-pr5243-playevent-selection


>Here's another request. Shouldn't chords have .parent's, too (segment, so you can find all other chords which sound with it?) (Yes, they should...). no?<

The parent property is exposed for all elements. It isn't restricted to chords. In theory you should be able to walk the object tree.


After my new plugin changes 3 values in playEvents it has located, it logs
Debug: no active command, UndoStack
Debug: no active command, UndoStack
Debug: no active command, UndoStack
when exiting. And sure enough, the changes, which are correctly made, are not undoable. Do you have any idea why this may be happening? The plugin is of the dialog-type, and the changes are made by a button handler.


Sorry to say I'm having issues. I'm running it from the plugin menu and have a debugger attached so I see script output. Here's what I get:

qml: hello adjust appoggiatura
qml: 1 selections
qml: element.type=19
qml: grace notes [object Object]
qml: main note playevs [object Object] 1
qml: on time 333 len 667 off time 1000
qml: grace chord 0: Ms::PluginAPI::Chord(0x15c013c0)
qml: grace note 0 Ms::PluginAPI::Note(0x15c00e80)
qml: on time 0 len 333 off time 333
qml: found summing appog-main pair!
file:///D:/Users/Dale/Documents/MuseScore3Development/Plugins/adjustappoggiatura.qml:100: TypeError: Cannot read property 'playEvents' of null

It gets through the code to the point it sets a note but for some reason the variable used to set note's playEventList is null at the moment it's needed.

However, a couple times it DID work for me but I can't get it to work anywhere near reliably.

This feels a lot like race condition between onRun and initialization of these variables:

property var the_note : null
property var the_grace_note : null

If they are lazy initialized and that happens after onRun--which may be completed before the dialog is up and running--then this could happen. I'm not sure how this declarative QML language works for this. Remember that the Javascript is just a bolt-on interpreter that is called in response to events.

For me this is the only explanation for what I see. If another reason can't be seen for this, is there a way that the onRun code could be run in the dialog button run 'context'?


onRun is called when the plugin is loaded, in every case. If it doesn't find a selection it likes, it exits, and QML never puts up the dialog. If it finds one, it saves the note pointers in those 2 vars (know a better way?) for the Apply handler when the Apply button is pressed. Only then is system data changed.

In reply to by [DELETED] 1831606

Just one thought...

In terms of handling the data in a common context, checkout the attached tin-whistle-tab plugin I've been helping out on. If you look at the "onRun" I check the conditions for a successful use of the plugin and either put up an error dialog (in my case) or do the plugin's function.

In your case you would check for a selected note and grace note and NOT (unless you want to show a visible error) show your dialog if the conditions aren't right.

If all is ok then open your dialog and let the processing continue there.

Of course I realize that there are as many approaches as there are programmers (I call it the Hundred Programmer Problem--If you give a task to 100 programmers, you will get 100 different solutions!) but the key is to keep asynchronous stuff out of the main plugin work.


Attachment Size
tin_whistle_tablature-with-dialog.qml 14.37 KB

I've only been using your files.

I just downloaded your latest and I have the same issue. I also played the score before trying to test your theory that the createPlayLists() should have been called. Here's the console output:

qml: hello adjust appoggiatura
qml: 1 selections
qml: element.type=19
qml: grace chords [object Object]
qml: N grace chords 1
qml: grace chord# 0 Ms::PluginAPI::Chord(0x178e0120)
qml: grace note 0 [0] Ms::PluginAPI::Note(0x178e0300)
qml: on time 0 len 333 off time 333
qml: summa 333
qml: main note playevs 1
qml: on time 333 len 667 off time 1000
qml: found summing appogiatura note!
file:///D:/Users/Dale/Documents/MuseScore3Development/Plugins/adjustappoggiatura_0.qml:110: TypeError: Cannot read property 'playEvents' of null

If a put a console.log("console.log("the_note=" + the_note); on line 109 I get this in the log:

qml: 1 selections
qml: element.type=19
qml: grace chords [object Object]
qml: N grace chords 1
qml: grace chord# 0 Ms::PluginAPI::Chord(0x1a5f04d0)
qml: grace note 0 [0] Ms::PluginAPI::Note(0x1a5ef990)
qml: on time 0 len 333 off time 333
qml: summa 333
qml: main note playevs 1
qml: on time 333 len 667 off time 1000
qml: found summing appogiatura note!
qml: the_note=null
file:///D:/Users/Dale/Documents/MuseScore3Development/Plugins/adjustappoggiatura_0.qml:110: TypeError: Cannot read property 'playEvents' of null

You can see the the_note is null. I entered a number like 450 and press apply. That was the result. Beats me how that happens since above you can see it got through the code that sets the variable.

This time it worked. I got this console output:

qml: element.type=19
qml: grace chords [object Object]
qml: N grace chords 1
qml: grace chord# 0 Ms::PluginAPI::Chord(0x22d53ae0)
qml: grace note 0 [0] Ms::PluginAPI::Note(0x22d53c30)
qml: on time 0 len 333 off time 333
qml: summa 333
qml: main note playevs 1
qml: on time 333 len 667 off time 1000
qml: found summing appogiatura note!
qml: Begin apply pass, new transit= 333
qml: Apply pass grace_chords [object Object]
qml: Did it! 333

However, no undo frame was created. Hmmmmm.


Belay that last report.

If I used the default value it didn't set the undo. Other values it's working fine.

So I understand, to get it working you delayed your actual list scan, computation, and value setting to when the Apply button was pressed?


I've pushed up another update of the uber branch:…

There are no changes to the scripting interface. The changes are all related to ensuring the script doesn't damage internal data.

I've attached a new test script for some of those changes.

Other than that I'm hopeful that this latest set of PR's will be accepted into master.


Attachment Size
plugin-chord.add_.remove-test-v4.qml 3.89 KB

>Do you understand what's going on with my test dialog and initialization?

Nope. I can see in the code that there a distinct differences between the way menu initiated plugins are loaded and executed:…

versus the way the plugin creator does it.…

But that's about it. This will have to be looked at by an overall plugin loader qml expert. It does look like a bug so a defect issue should be entered for it.

>What do I tell git to receive your latest changes?

Since you cloned a shallow history with git clone it's simplest to delete the folder and re-clone the source:

git clone --depth 1 -b qml-pr5224-pr5243-playevent-selection


I found a foolish workaround to the plugin initialization problem, a "press me!" button. In this version, if you're not satisfied with grey "334", you can press "See Current", and violà, as the mis-say, it re-finds the note, computes the summa and puts it in the slot, at dialog-button time. Pretty silly, other people say. Try it. I'm using your branch, all builds and works with my code (haven't tried your code). I used this plugin in actual work to customize dozens of appoggiature in this actual score, which came out pretty well (I bound a key to it): .

Attachment Size
adjustappoggiatura.qml 7.41 KB

When I use your message dialog technique to report errors, if the error is shown and accepted, plugin accelerators no longer work unless I switch to a different application and back to MuseScore. There is something broken in re-selecting the MuseScore window with respect to those accelerators after such a message-box closes. I don't know if it's mac specific. My "onAccepted" calls Qt.quit() like yours. If my "dialog-plugin" dialog closes normally, there is no problem. Do you get this effect?

>here is something broken in re-selecting the MuseScore window with respect to those accelerators after such a message-box closes.<

It appears that the Mac's input 'focus' didn't return to your application. This can be quirky between OS'es. I've attached an updated version that makes the dialog "application.modal" so that control should go back to the application. Give it a try.

I noticed something annoying about short-cut key assignment. When I reload plugin's from the plugin manager my assigned keyboard shortcuts vanish.


In reply to by DLLarson

I added that declaration to my own message-dialog, and observe no difference in behavior. I tried several combinations of quit() or lack thereof after the open() call and in the button handler. I think it has to do with the fact that the plugin is a dialog-type plugin, which, given that it has to accept input, is mandatory. Yeah, when you reload plugins, bye-bye bindings. MS QML is witchcraft.

Dmitrios has identified the no-init bug as known since 2013, has references, a fix in the works, and an easy workaround (use Component.onCompleted instead of onRun until the fix) and that house is now all in order. This surely will fix the the_note problem, too, See the other thread.

Here's an even cooler plugin using the new capabilities , the one I've been waiting for for years. From the source,
/* This plugin replaces the Piano Roll Editor as the most convenient means of
adjusting the on-times and off-times (not "len"s) of notes on a one-by-one
basis. This allows you to phrase without the yellow bar jungle (which is
always there for hard cases). This even works on appoggiature, but does
not work on notes with real "ornaments" (not well-defined how to edit).

Extensions to impose duple- and triple- phrasing patterns are under

Just click on any note, and invoke this (maybe via a shortcut), and
edit away the on and off times. Return and ESC are accepted as
Apply and Cancel.

Attachment Size
articulate.qml 5.7 KB

By the way, destructuring assignment [a, b] = [1, 2] is an ECMAScript 6 feature which is thus not in Qt 5.9. Dale, could you private-mail me an email address so we can take this conversation out of the forum? Thanks.

>and allow the Piano Roll Editor to be thrown off the roof....

It's kind of nice to see the spatial effects on the score as a visualization tool.


I agree it's beautiful and entertaining, but only showing one staff at a time, it is less valuable as a visualization tool. As a performance-editing tool, it is far better than it was in version 2, where the usual outcome was crashing the program, and is a huge boon, but has nothing to offer over these new tools.

Status PR created fixed

Fixed in branch master, commit 216188f7ed

_Fix #291708: Expose Score.selection object to access to GUI selected elements.

This commit exposes the Score.selection.elements list
enabling QML scripts to manipulate on user selected score elements.
The expectation is that additional selection information will be
provided on this object in the future._

Status PR created fixed

Fixed in branch master, commit b05c823912

_Fix #291708: Expose PlayEvent's via QML Plugin Note interface.

Adds capablity to examine and modify PlayEvent's and a Note's
playEvents list. Adds access to Chord.playEventType. Also
supports UNDO for these operations._

Probably no one's reading this except myself, Dale, and Matt, but this is what a 5-minute Bach motet movement sounds like when every single note is deliberately phrased, by use of the new tools facilitated by these improvements, to match the text, line shape, slurs/phrasing marked by the composer, and other architectural considerations (i.e., "choral director dharma"): . This opens a new chapter in MuseScore realism.

Not quite. I just learned that "nightly" isn't really 'nightly', it happens as soon as a merge succeeds! Now for the bad news! The mac build times out, and the previous build (0734 this morning, Europe time) doesn't have the OnRun fix, which I now need. Hopefully Dmitrios or Anatoly will fix the build problem. I so do not want to have to back out the onRun fix code.

Fix version