Save the setting of plugins

• Apr 29, 2015 - 19:20

Save the setting of plugins
Now I'm improving the shakuhachi, koto and shinobue notation plugins.
I‘d like to save the setting of plugins.
Prototype code is


import QtQuick 2.2
import MuseScore 1.0
import FileIO 1.0
import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.0
import QtQuick.Layouts 1.1

MuseScore {
  menuPath: "Plugins.writeReadSeettingsDemo"
  version: "2.0"
  description: qsTr("This plugin Write and Read the setting of plugin")
  pluginType: "dialog"

  id: window
  width:200
  height:100
  ExclusiveGroup { id: exclusiveGroupKey }

  RowLayout {
    id: column
    x : 10
    y : 10
    Label {
      font.pointSize: 14
      text: "Select" 
    }
  }


  RowLayout {
    x : 60
    y : 10
  ComboBox {

 //   currentIndex: Number(fileContents[0])     //      doesn't work
    currentIndex: comboBoxInitValue     //      doesn't work

    model: ListModel {
      id: chooseList
      property var key

      ListElement { text: "choose A"; fName: 1 }
      ListElement { text: "choose B"; fName: 2 }
      ListElement { text: "choose C"; fName: 3 }
    }
    width: 100
    onCurrentIndexChanged: {
      console.debug(chooseList.get(currentIndex).text + ", " +chooseList.get(currentIndex).fName)
      chooseList.key = chooseList.get(currentIndex).fName
      console.debug(chooseList.key)
    }
  } // end ComboBox
  }

  RowLayout {
    x : 50
    y : 60
    Button {
      id: closeButton
      text: "Close"
      onClicked: { Qt.quit() }
    }
    Button {
      id: okButton
      text: "Ok"
      onClicked: {
        apply()
        Qt.quit()
      }
    }
  }


  function apply() {
    curScore.startCmd()
    my_file1.write(chooseList.key);
    curScore.endCmd()           
  }


  onRun: {
    var fileContents = my_file1.read();
    console.log("Read from file value="+ Number(fileContents[0]));
    var comboBoxInitValue =Number(fileContents[0])                      //    <<

    if (typeof curScore === 'undefined')   Qt.quit();
  }


  FileIO {
    id: my_file1

    source: tempPath() + "/temp9999.txt"           //  <<<

    onError: console.log(msg)
  }
}

Q1.
The stored value could be read, but couldn't use at the next run.
Line 35 or 36 doesn’t work.
35: // currentIndex: Number(fileContents[0]) // doesn't work
36: currentIndex: comboBoxInitValue // doesn't work

Is there method to set "currentIndex:” from variable?

Q2.
Line 93
93:  source: tempPath() + "/temp9999.txt"
Where is the tempPath()? 
I couldn’t find temp9999.txt file.
If possible, I want to save as a permanent file,
like ./temp9999.txt

Please advise me.

Comments

Initialize comboBoxInitValue as a MuseScore item property:

property var comboBoxInitValue : 1 // somewhere before the first RowLayout

rather than as a var in the onRun: function. Then line 36 will work.

To get the tempPath(), just write it out to the console:

console.log("tempPath(): " + my_file1.tempPath());

In reply to by heuchi

Hmm, not sure I like it:

The information is stored in the system registry on Windows, and in XML preferences files on OS X. On other Unix systems, in the absence of a standard, INI text files are used.

Don't get me wrong, I like setting being saved, but I'd prefer an ini file on Windows too.
Esp. when using the portable version of MuseScore, tampering with the registry is to be avoided.

In reply to by tcbnhrs

Is this more safer than previous one?

MuseScore {
  property var fileExist :true
  ....

  FileIO {
    id: my_file1
    source: tempPath() + "/temp9999s.txt"
    onError: fileExist=false
  }

  onRun: {
    if( fileExist ) var fileContents = my_file1.read();
    if( !fileExist ) var fileContents="18\n0\n13\n0\n0\n0\n0\n0";
    ….
  }
}

Attachment Size
Shakuhachi_Notation_test03.qml 13.09 KB

In reply to by tcbnhrs

Check how it is now done in the batch export plugin, it does save it's settings in the MuserScoreDevelopment.ini (or will do so MuseScore.ini, once 2.0.1 is out), under Linux and Windows (on Mac it should use whatever method is used there, but identical to MuseScore itself)
Check the 2 commits 4837dc5 and bda8d29

In reply to by straannick

Use the JSON representation? JSON.stringify and JSON.parse

Consider the following plugin:

import QtQuick 2.0
import MuseScore 1.0

MuseScore {
      menuPath: "Plugins.pluginName"
      onRun: {
            console.log("hello world");
            var testObj = [
                  { text: 'T1', value: 1 },
                  { text: 'T2', value: 2 },
                  { text: 'T3', value: 3 }
            ];
            console.log(testObj);
            var testStr = JSON.stringify(testObj);
            console.log(testStr);
            var resultObj = JSON.parse(testStr);
            console.log(resultObj);
            console.log(resultObj[1].text + ' ' + resultObj[1].value);
            Qt.quit();
            }
      }


It has the following result:
Debug: hello world
Debug: [[nodetitle:object Object],[object Object],[object Object]]
Debug: [{"text":"T1","value":1},{"text":"T2","value":2},{"text":"T3","value":3}]
Debug: [[nodetitle:object Object],[object Object],[object Object]]
Debug: T2 2

In reply to by jeetee

Thank you, jeetee!
For me no problem to transform something to string. The problem is - how to save it to .ini file using Setting
Let's change your example this way:

import QtQuick 2.0
import MuseScore 1.0
import QtQuick.Dialogs 1.2
import QtQuick.Controls 1.2
import Qt.labs.settings 1.0

MuseScore {
  menuPath: "Plugins.myPlugin"
  pluginType: "dialog"
  id:window
  width: 100; height: 100;

  Settings {
    id: settings
    category: "myPlugin"
    property alias width: window.width
    property alias testObj: //----> testObj = testStr - how ?????
  }
  onRun: {
    console.log("hello world");
    var testObj = [
      { text: 'T1', value: 1 },
      { text: 'T2', value: 2 },
      { text: 'T3', value: 3 }
    ];
    console.log(testObj);
    var testStr = JSON.stringify(testObj);
    console.log(testStr);
    var resultObj = JSON.parse(testStr);
    console.log(resultObj);
    console.log(resultObj[1].text + ' ' + resultObj[1].value);
    Qt.quit();
    }
  }

In reply to by jeetee

About ComboBox. ComboBox model is property, isn't it?
It's intersting that when I set
  Settings {
. . .
  property alias testObj: comboBox.model
. . .
For ComboBox with 4 elements I get in .ini
  testObj=@Variant(\0\0\0\x7f\0\0\0\xfQQmlListModel*\0)

And, of course, after plugin restart I have empty ComboBox.

In reply to by straannick

Directly saving the model won't work, as it won't store the contents of the list, but only the (temporary) reference to it.
I've been having a go at trying out a workaround and have it almost functional; The issue I have is that it runs perfectly fine from the Plugin Creator, but fails to update the combobox when ran from the menu.
It does still load the list from its settings and adds a new item, but somehow the visual component is not updated if ran from the Menu vs when ran from the Plugin Creator.

Attempt to be found at: jeetee/MuseScore_Plugin_Examples/serializedSettings.qml
If we can somehow force the combobox to refresh when being ran from the Menu as well we're good to go.

In reply to by jeetee

Thank you, jeetee!
I my case I'd prefer more universal variant - to build storedList from the ComboBox, so I replace your
  settings.comboBoxList = JSON.stringify(storedList);
with
  var anotherStoredList = JSON.parse("[{ \"text\": \"Default Item\", \"value\": 0 }]")
  for (var i=1; i < myDrop.count; i++)
    anotherStoredList.push ( {text: myDrop.textAt(i), value: i })
  settings.comboBoxList = JSON.stringify (anotherStoredList)

but it should be noted, that I wrote "var anotherStoredList = JSON.parse.." just because I don't know how to create empty "var" of type which JSON.parse will return. Do you know this?
And, of course, it would be great to force the combobox to refresh.

By the way, do you know what next diagnostic in your example means?
  Warning: QVariant::load: unable to load type 39.
  Warning: QVariant::save: unable to save type 'QObject*' (type id: 39).

In reply to by straannick

The result of JSON.parse depends on what string is put in there. The result can be any type of basic JavaScript object/array/string/number depending on what the inputstring is.

In your case, you expect an array with a nested object to be the default, so you could write the following:

var anotherStoredList = [{ text: "Default Item", value: 0 }];
for (var i=1; i < myDrop.count; i++)
  anotherStoredList.push ( {text: myDrop.textAt(i), value: i })
settings.comboBoxList = JSON.stringify (anotherStoredList)

As for the Warning messages you're getting: The Settings object can only store 'basic types'. This is one of the reasons we're looking into the JSON parsers: to reduce non-basic types into a string.
It looks like you're trying to save a QObject* into the Settings, which the Settings object does not know how to do. It is not supported.

In reply to by jeetee

I used JSON here "just for sure". The problem is that in real life, especially with editable ComboBox, "defaul item" at 0 position it's not obligatory. So the better solution is
  var storedList = [{ text: myDrop.textAt(0), value: 0}]
  for (var i=1; i < myDrop.count; i++)
    anotherStoredList.push ( {text: myDrop.textAt(i), value: i })

And, of course, we also need to save/restore currentIndex of ComboBox

About warning - you're getting it too. I suppose in your case it happened at
    myDropList.append(storedList[storedList.length - 1]);
In my case - every time I append into ComboBox new item.

In reply to by straannick

Wierd.
From what 'the truth of the interwebs' show, it is most definitely related to the saving in the settings view. I can't imagine the result of JSON.stringify to become a QObject* rather than a string (typeof(JSON.stringify()) does return string as well).

If you didn't add another property then I'm stumped. The only other thing is perhaps our Qt versions being different. When looking to 'About Qt' from MuseScore, it reports version 5.4.2 for me.

It would also be interesting to see what happens on other platforms (Mac/Linuxes).

In reply to by jeetee

In my case MuseScore Qt version is also 5.4.2.
But I also have two Qt version installed - 5.4 and 5.5 and current PATH is set to 5.4 version:
  C:\Qt\5.4\mingw491_32\bin;C:\Qt\Tools\mingw491_32\bin;...

When I checked your example I didn't add another property because I just copy/pasted text from your project.

In reply to by straannick

If you could be bothered to use https://github.com/jeetee/MuseScore_Plugin_Examples/blob/master/seriali… you should see this for yourself with the following steps:

  1. Open serializedSettings.qml from the Plugin Creator
  2. Run it:
    run_1.png
  3. Press the close button within the dialog and then run it again from the Plugin Creator
    run_2.png
  4. Close the Plugin Creator and enable the Plugin from the Plugin Manager
  5. Run it from the menu Plugins > serializedSettings
    run_3_and_4.png
    The comboBox isn't visually updated, but the settings were loaded, appended and stored again
  6. Press to close button within the dialog and launch it again from the menu. Identical result.
    run_3_and_4.png
  7. Now open the Plugin Creator again, and run the plugin from there
    run_5.png

These steps show that the plugin does load its settings, does add its element and does save the new list. But for some (yet unknown reason) doesn't update the comboBox when ran from the Menu.

EDIT: And it seems to be a known issue related to plugins with the pluginType set to 'dialog'
https://musescore.org/en/node/71081

EDIT2: Workaround implemented by using a standard QtQuick.Dialog item for now. Sample has been updated as well.

Do you still have an unanswered question? Please log in first to post your question.