Add text line to range
I've made a plugin that adjusts note anchored lines to serve as brackets for marking ligatures in early music as encountered in this Facebook post (https://www.facebook.com/groups/musescore/posts/7546201332072786?commen…). I've got the tweaks happening pretty well (attached), but I'd like to be able to create the line itself in the plugin on a range of two or more notes.
I tried:
var bracket = newElement(Element.TEXTLINE); curScore.selection.elements.add(bracket);
but got a TypeError: Property 'add' of object [object Object] is not a function
What am I missing?
Attachment | Size |
---|---|
ligatureBracket.qml | 1.24 KB |
Comments
A line needs to be applied over a range, not added to a single object.
You could attempt the following:
1. Select the range using the selection API
2. Call
cmd('add-noteline');
It could be that the
cmd()
has to be wrapped within startCmd/endCmd to then correctly further process it (although I'm not sure how you can access that line object then afterwards to adjust properties of it).In reply to A line needs to be applied… by jeetee
Thanks, I did have a range selected and still the got above error. The cmd() worked without wrapping it, but you're right that then there's no way to refer to the object because the parent of the TextLine item is the system, not the notes.
I tried the following:
var cursor = curScore.newCursor();
var bracket = newElement(Element.TEXTLINE);
cursor.rewind(1);
curScore.selection.select(cursor.element.notes[0]);
cursor.next();
curScore.selection.select(cursor.element.notes[0], true);
curScore.selection.add(bracket);
but I get
TypeError: Property 'add' of object Ms::PluginAPI::Selection(0x11822300) is not a function
. Do I need to make an array of notes and then call add() on the array? The API says that only the cursor, chord, and note classes have the add function.In reply to Thanks. The cmd() worked… by jonarnold
Since, as you're aware, there are only
add
methods on single objects, there is no way to add lines in the plugin API besides using thecmd
interface for it.Now I believe I did find a way to reach the line afterwards and it is to make a new range selection after inserting the line. Then you can again loop over the elements in the selection and this time the TextLine element will be included in it; allowing you to further access/modify it.
In reply to Since, as you're aware,… by jeetee
Ah thanks! I was able to do it without changing the selection actually. I do get a
Warning: Trying to construct an instance of an invalid type, type id: 329084992
warning when I do the cmd, but it still works. Not sure if I should worry about it?In reply to Ah thanks! I was able to do… by jonarnold
If you're now using the cmd upon a range selection, then that might be the origin. A note anchored line is really only supposed to be attached to two selected notes and nothing else, so make sure your selection is exactly that to be on the safe side.
You'll then need to range select that to be able to access the TextLine from what I can see (the line isn't highlighted when you add it manually, so I assume it won't be in the selection directly after adding it).
In reply to If you're now using the cmd… by jeetee
Thanks. The warning happens even when only two notes are selected. I selected two notes manually, ran the command from the Plugin Creator, and it triggered the error.
In reply to Thanks. The warning happens… by jonarnold
I can confirm that error in 3.6.2 (although it's a different numeric value for me:
959208912
).The error comes from the underlying Qt libraries and have to do with that the constructed element is not convertible into a QVariant type of variable (which indeed is the case, but also not something to worry about for you).
That errors comes from the fact that your selection contains not only notes but also stems, ...
So the primary idea would to isolate the notes from the selection and add the bracket on the note directly.
var sel=curScore.selection.elements;
No error.... But a crash (this is a typical behaviour of MS). So we've got to find another solution.
The idea of jeetee is working fine. After adding the line through the
cmd
the line is added to the selection.You just have to retrieve it from the selection in order to modify it:
// The way it works actually
curScore.startCmd();
cmd("add-noteline");
curScore.endCmd();
In reply to That errors comes from the… by parkingb
Thanks, I developed a similar solution, and the setting of the beginning and ending hooks worked fine, but oddly the same line has different Y positions when I find it by looping through the selection than when it's the only selection. So, my old code doesn't move the line at all if it's not selected by itself.
In reply to Thanks, I developed a… by jonarnold
What about reselecting the line thru the code (i.e. leaving the original selection once the line is created) ?
In reply to What about reselecting the… by parkingb
How would I select it without iterating through the range selection?
In reply to How would I select it… by jonarnold
Honestly I've not been able to select it.
When you select the line manually, its type is "TextLineSegment". Not "TextLine".
But is not a segment. As "TextLineSegment" is not a valid Segment type.
My next attempt has been to iterate through all the elements of the first note measure. The line is NOTthere.
Conclusion: I don't know to programmatically find and select a line...
[EDIT] "The line is NOT there".
In reply to Honestly I've not been able… by parkingb
It's an unfortunate fact that MuseScore internally uses the word "segment" to mean to entirely unrelated things.
One is "vertical slice of time". Each note or rest in a score belongs to a segment, and the segments to measures. That's presumably the usage you are thinking of.
But also, a line consists of "segments" in the standard geometric sense of the word - something with a defined start and end point. Most lines have only a single segment, but any line continued from one system to the next - the portion of the line on a given system is a segment. So, each line contains an array of segments.
I finally found some time to look into this and can conclude the following:
1.) There is no need to reselect something the range from within the plugin as the added line using
cmd("add-noteline")
is automatically included in it.2.) The line is found as a TEXTLINE element, even if it crosses systems and thus has two underlying SLineSegments internally.
3.) Changing the position of the TEXTLINE via the plugin is not possible (and possibly a bug in the API).
You can assign
offset
, but it is written to theTextLine
tag instead of the childSegment
tag. That in itself wouldn't be an issue if it was taken into account when reading the score back in / layouting the textline. But only the offset of the underlying SLineSegment is applied.Because of not forwarding this property to the SLineSegment assigning
userOff2
entirely fails. That property doesn't exist on the TextLine itself and there's no access to the SLineSegment inside to apply it there.Find the test plugin version attached. When testing out, best to save the example score as mscx so you can open it with a text editor and filter on <
Spanner type="TextLine"
> to see the effect the plugin has.In reply to I finally found some time to… by jeetee
Further follow-up; you can indeed set offset and userOff2 for a TextLineSegment and that is what is selected if you select the element manually in the score. So you could do a "Select all similar elements" and then loop through all those TextLineSegments to set their offset values as you want.
However I can't find a way to go from a TextLine to the TextLineSegments within the Plugin API.