QML coding: can't initialize dialog of dialog-type plugin
I have written a dialog-type plugin which works perfectly in the plugin creator, but not when run from the plugin menu. What seems to be happening in the latter case is that it ignores the data that my onRun() has placed in the mainLayout dialog, and displays it with default values. I can prove that the onRun is running, but there seems to be no way it can convey data to the dialog or the button handlers, although it does so perfectly when running in the Plugin creator. I have written a plugin which putatively looks at stuff and offers to you to change it, but seemingly can't. What am I missing?
The enclosed minimal test case shows "RIGHT" when run under the Creator, and "WRONG" when run from the menu. (In Qt 5.9.4).
Attachment | Size |
---|---|
testdlg.qml | 916 bytes |
Comments
Confirmed in MuseScore 3.2.3 on Windows, Qt 5.12.2
Other dialog type plugins don't try to do that, like TempoChanges. Others that use dialogs are not being declared as being dialog type, like batch_convert and are using that mechanism to set up things in
onRun()
.BTW, that
won't work, there is no such string in that context. Try e.g.
instead. And
won't work at all, as there is no such string at all anywhere in MuseScore
In reply to confirmed in MuseScore 3.2.3… by Jojo-Schmitz
Thank you for looking into this for me.
Is there rhyme or reason or, heaven forbid, documentation, on how to do this (qsTr) properly? What if InsertMeasuresDialogBase goes away or changes name some day, as the string I was using already did? I just copied it from some other plugin (which must thus be broken, too).
Reading what you said, is it thus that there is just no way that a plugin can set stuff up from the score and then ask for input? This is a "fairly severe limitation" if so. I just want to say "the current value is 200; what would you like it to be?" Not crazy, no? I know that Tempo Changes dialog puts up values (e.g., last linear/curved setting) from "Settings" presumably stored in some persistent place outside the plugin. Is there just no way, or do I not know enough? Should there be a way? Ought something be re-engineered or added in the MS plugin framework?
MS QML plugin writing needs a book.
In reply to Is there rhyme or reason or,… by [DELETED] 1831606
Perhaps there should be a magic method called "onDialogInit" which is called by the framework before the dialog of a dialog-type plugin is actually put up? Is there already some kind of "onInit" method hook available, as in good old Windows C++ dialogs?
In reply to Perhaps there should be a… by [DELETED] 1831606
Could it fairly be described as a bug that the dialog-initializations of onRun are ignored? It was also discovered fortuitously that "property" plugin-wide variables set by onRun are also overwritten. onRun for a dialog plugin apparently can do nothing except check stuff and complain if not OK (and this mechanism is buggy too, subject of forthcoming report).
In reply to Is there rhyme or reason or,… by [DELETED] 1831606
If you're referencing other translations, you're alway at risk to point to nowhere eventually. Same as with symbolicg links in Linux/UNIX/maxOS, they can point to something non-existing.
For "Cancel", "OK" and the likes pointing to the corresponding Qt resource might be the saver bet and lay the translation into the hands and responsibility of the Qt developers.
I believe "QPlatformTheme" might be the context to use and indepentant of MuseScore's translation.
For that other string you need to translate it for that plugin, Check how other plugins are doing this.
In reply to If you're referencing other… by Jojo-Schmitz
What is the purpose of the qsTr(anslate), anyway? What does that operand mean, and how should it be chosen? Anyway, that is a very small problem because the fixed strings appear correctly in spite of it. That "I want to write a plugin that shows something and offers to change it" seems beyond the state of the art is a much bigger problem. My current work-around is "cute", you have to press a button, "show current value", but inane; no device should have a "please press me" button.
In reply to What is the purpose of the… by [DELETED] 1831606
qsTr("string")
is for a string to be translated allong with this plugin, extract withlupdate
(intotranslation/locale_*.ts
files), translate (manually editing those ts files or using Qt Linguist), release usinglrelease
(or Qt Linguist) (generatestranslations/locale_*.qm
files)`qsTranslate("context", "string") merely refers to a string from another context and pulls its translation (if available) from there
In reply to qsTr("string") is for a… by Jojo-Schmitz
I see, language translation. So I have to find a string somewhere which is close enough to what I want and name the place where it's used. What happens if I have to use a new string? I guess I have to learn how to say it in Thai, Polish, and Malayalam. I could probably use a per-mille symbol instead of a string for my input-field label...but it is indeed a high bar for adding controls or diagnostic messages.
In reply to I see, language translation… by [DELETED] 1831606
Does this mean that no plugin using a "new string" can be written unless a release of the core product containing that string is behind it? That sort of defeats the plugin idea.
In reply to Does this mean that no… by [DELETED] 1831606
Of course it can. That string just won't show translated. If for a string from a qsTr() or qsTranslate() there is no translation available or found it falls back to the string in their argument.
In reply to I see, language translation… by [DELETED] 1831606
"close enough" is not close enough, needs to be 100% identical.
And you don't need to do translations yourself, but just ask for help on those and accept PRs to your plugin.
Like I do only the German translation to my plugins, jeetee does the Dutch ones for his, and we help one another, so I do the German one for his and he the Dutch ones for mine.
It gets easier if you can refer to existing strings, in which case no translation is needed, as you just take the work done by others. I'm using that exessively in the notenames plugin for example.
Using qsTr("String") just serves as the infrastructure for translations, the i18n part, the translation then is the l11n part.
In reply to "close enough" is not close… by Jojo-Schmitz
What I meant by "close enough" is "close enough in meaning", e.g., "see" vs "view". Obviously lexical "closeness" is silly! However, "cow" in context 1 might not be the same as "cow" in context 2 (nod to S.I. Hayakawa). Even lexical identity might not be enough! I've seen localization files in plugin zipfiles -- how does that work?
In reply to What I meant by "close… by [DELETED] 1831606
Those are created using lupdate/lrelease
In reply to Those are created using… by Jojo-Schmitz
Is there documentation for those tools and using them with plugins?
In reply to Is there ocumentation for… by [DELETED] 1831606
I'm sure there is, but if so it is pretty well hidden and I don't remember whether I ever found and used it...
In reply to I'm sure there is, but if so… by Jojo-Schmitz
Then can you give me a few clues (bigger than a breadbox? Could a man or a woman use it equally? (old "what's my line" questions) about its use? What role do the private i18n files for plugins play in their deployment?
In reply to Then can you give me a few… by [DELETED] 1831606
If yoiu want your plugin being available in different laguages and cannot source the strungs to translated from Qt or MuseScore, you got to have these 'private' translation files. The notenames plugin doesn't need them (except of maybe 1 or 2 strings), the batch_export plugin does need them for all strings (well, the latest version only for most of the strings), the TempoChanges needs it for some strings but not all. Therse plugin also come with batch files to generated these files. For Mac those would need to get turned into shell scripts, but that isn't rocket science, each of the 2 batch files is basically just one comment plus its arguments.
It turns out that this is a bug in MuseScore back from 2013 which causes
run()
signal being emitted in a wrong instance of a plugin so some UI gets updated but not the one that is visible after launching a plugin from a menu. This issue has also been reported in the tracker, see:#72416: Plug-in code working differently when called directly and when run under Plugin Creator
#97316: Investigate differences between plugins ran from the Creator and the Menu
This is easy to correct so I'll push a fix for it a bit later. As an immediate workaround, you may consider putting your initialization code into
Component.onCompleted
instead ofonRun
as it was done, for instance, here.Component.onCompleted
is executed in every plugin instance so the correct instance will get its UI updated by that code as well.In reply to It turns out that this is a… by dmitrio95
Thank you for your attention; please post about the push and hopefully Dale will integrate; This is great news; thanks again for your attention!
In reply to What a beautiful world we… by [DELETED] 1831606
Works like a charm! Thank you so much!
In reply to Works like a charm! Thank… by [DELETED] 1831606
I was out of electrical civilization over the weekend, hence the late reply/acknowledgement.
As you by now know, I've experienced similar issues back in 2016 (wow, time does fly, even when having less fun as one would like..). My issue was noticed especially with dynamically trying to load combobox/dropdown contents for a plugin of the type dialog during work on the UltraStar Exporter plugin.
The solution there was to not only move most dialog initialization into
onCompleted
instead ofonRun
but I even had to resort to creating a QML dialog object myself rather than use the MuseScore dialog type one.Now that it's on Dmitri's list we're bound to slowly, but steadily, gain an increasingly stable and useful framework in place.
In reply to I was out of electrical… by jeetee
The OnRun() event is a MuseScore plugin creation that is fired by the C++ code after providing the QML code to a Component. Other than that it was no relationship with things going on within the QML model.
So doing everything from OnRun() or nothing in OnRun() (using Component.onCompleted) seems safest to ensure you're working in the same code and time line.
At least that's what I see in the C++ code. If QML engine processing does things asynchronously you aren't guaranteed OnRun occurs before QML loading or object instantiation, or... whatever else. OnRun needs some kind of interlock with the QML Component stuff.
-Dale
In reply to The OnRun() event is a… by DLLarson
That's bizarre. I wonder, then, what is the right time and place to make a check that prevents the dialog from coming up at all (right now, in error cases, the dialog comes up, but is immediately shadowed by an error box, which, when dismissed, closes all cleanly nevertheless).
In reply to That's bizarre. I wonder,… by [DELETED] 1831606
Event driven programming can be challenging. It's a matter of understating the event model of the framework. That's why things like Component.onCompleted exist. When it's called you know it has completed its setup in this case.
Perhaps there's a Component.onCompleted at the MuseScore object level?
Pretty much all GUI's are event driven so it would be bizarre to not do it that way. The issue here is specifically OnRun.
-Dale
In reply to Event driven programming can… by DLLarson
Just some stuff to chew on...
I've attached a new version of BSG's
testdlg.qml
for testing.Here's the order of event calls when running the attached
testdlg-v2.qml
from the menu...I don't know why the Components are called twice before
MuseScore.onRun
is called.Here's the order of event calls when running testdlg-v2.qml from Plugin Creator...
In the Creator console I see these:
But the
Component.onCompleted
console.log
calls show up on the debugger console (when I debug the release application.)...I don't know why that is.
Weird results regardless the approach.
-Dale
In reply to Just some stuff to chew on… by DLLarson
From the QML docs...
It's notable that the "The order of running the onCompleted handlers is undefined."
-Dale
In reply to From the QML docs... … by DLLarson
All fair enough -- onCompleted is great if you know about it. What's bizarre is that onRun was added by MS without thought about its interaction with the extant framework and its initialization regimen. The result of the confusion was the bug.
In reply to From the QML docs... … by DLLarson
I'm aware of most of that.. being part of the reason for constructing a separate Dialog rather than using the MuseScore plugin type one.
It also allows control over when it should open..
In reply to I'm aware of most of that… by jeetee
I was unable to figure out how to instantiate a "private" dialog; the necessary import files didn't seem to be found. Do you have an example of some code that does this?
In reply to I was unable to figure out… by [DELETED] 1831606
See https://github.com/jeetee/MuseScore_Parsons_Code_Exporter/blob/master/P…
That plugin is still on MS2, but the dialog part is unchanged for MS3.
A more involved plugin that uses it and has been made compatible with MS3 can be found here: https://github.com/JosephEoff/MuseScore_UltraStar_Exporter_MS3/blob/mas…
In reply to See https://github.com… by jeetee
Thanks. Welcome back to electrical civilization!
In reply to Thanks. Welcome back to… by [DELETED] 1831606
Wow. That code should be the start of a manual/tutorial on this black art (i.e, MSQML in general).
In reply to Wow. That code should be… by [DELETED] 1831606
The actual problem is that you can't look at this as Javascript programming per se. It's QML: Quick Markup Language or Qt Modeling Language depending where you look programming.
Although it uses Javascript to perform complicated actions and computations, it's still mostly QML.
We have a miss-match of skills.
Given that the UI's for MuseScore plugin's are pretty simple, it seems boilerplate code is needed for the three different models: Dock, Dialog, and no-UI. Then a non-QML but sorta Javascript programmer can not feel lost when a UI is involved.
But still, to truly make it sing, a person would need to understand QML in more detail.
-Dale
In reply to The actual problem is that… by DLLarson
Sure, that is precisely the explanatory challenge. That is exactly why documenting it at all is difficult. Everybody knows Javascript, but nobody except Mr. Qt knows QML.
In reply to Sure, that is precisely the… by [DELETED] 1831606
There's also a third level, of course, which is neither Javascript nor QML, but the specifics that MuseScore expects of plugins, the APIs and objects it exposes, and the way common tasks are expected to be expressed. It is not at all simple; I suppose only skilled professional programmers like ourselves can deal with such high piles of stuff capably.