Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #274540: save user choice of instrument genre #3830

Conversation

jthistle
Copy link
Contributor

This addresses a feature request at: https://musescore.org/en/node/274540

The request was to keep the user's selection of instrument genre so that they don't have to change it every time they add/change an instrument. This PR adds this functionality by saving the user's last choice of instrument in QSettings, which actually means that the last selected genre will be saved even after the application has closed.

@jthistle jthistle force-pushed the 274540-save-instrument-group-selection branch from 748ceb7 to 6aed008 Compare July 25, 2018 09:39
@@ -362,6 +362,8 @@ QString InstrumentTemplateListItem::text(int col) const
InstrumentsWidget::InstrumentsWidget(QWidget* parent)
: QWidget(parent)
{
storeChangeInGenre = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed?
Couldn't it be set directly to true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is needed because when the InstrumentsWidget is being created, the selected instrument genre is changed by some other scripts a couple of times. I want to be able to save changes to the genre, but only ones made by the user. So, when loading is complete and this variable has been set to true, only then will any genre changes made be stored, because they are user-made changes, not automatic ones.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable isn't needed though if you see my reply below I guess

if ( ! settings.value("selectedInstrumentGenre").isNull() ){
int selectedGenre = settings.value("selectedInstrumentGenre").value<int>();
instrumentGenreFilter->setCurrentIndex(selectedGenre);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these last lines are not needed.
just use

instrumentGenreFilter->setCurrentIndex(settings.value("selectedInstrumentGenre", /*default value, probably 0*/ 0).toInt());

same for in SelectInstrument.cpp

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is whatever index 'common' has, since that's what it gets set to in some script (I can't remember which one). This creates a problem, because the index of common could change, but the default should still be common. So, to avoid having to look up the index of t default, it's easier and simpler to just check whether the selectedInstrumentGenre is set and then leave the filter as the default value if not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right about that

if (storeChangeInGenre){
settings.setValue("selectedInstrumentGenre", index);
settings.sync();
}
Copy link
Contributor

@ecstrema ecstrema Jul 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldn't these settings be saved in the destructor?
They would only be saved once, and there would be no need for the storeChangeInGenre private variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point actually. I can't test it right now, but I seem to remember that if you access the value of a setting after it's been changed but not synced, it will have the last synced value. The variable would be unneeded, I guess because:

  1. The widget is initialized
  2. The genre settings value is changed a couple of times automatically but not synced
  3. The genre filter is set to the genre settings value (but the last saved one, which would be user chosen)
  4. This change in genre filter is saved to the genre settings variable (meaning that any automatic genre changes are basically ignored)
  5. User possibly changes genre
  6. Widget closes, last chosen genre is saved

Sounds good to me. Basically, you're right :)

@jthistle
Copy link
Contributor Author

Thanks for your review @Marr11317. Unfortunately I'll be unable to implement any changes for the next 3 weeks, but your feedback is very useful.

@jthistle jthistle force-pushed the 274540-save-instrument-group-selection branch 2 times, most recently from 6af1486 to af96235 Compare August 14, 2018 09:19
@jthistle
Copy link
Contributor Author

Feedback addressed, a final review would be appreciated @Marr11317 (or anyone else)

{
QSettings settings;
settings.sync();
}
Copy link
Contributor

@ecstrema ecstrema Aug 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you simply save the index in the destructor?
you would have this kind of destructor:

    InstrumentsWidget::~InstrumentsWidget()
      {
      QSettings settings;
      settings.beginGroup(objectName());
      settings.setValue("selectedInstrumentGenre", instrumentGenreFilter->index());
      settings.endGroup();
      }

and such a constructor

    InstrumentsWidget::InstrumentsWidget()
      {
      ...
      QSettings settings;
      settings.beginGroup(objectName());
      if ( ! settings.value("selectedInstrumentGenre").isNull() ){
            int selectedGenre = settings.value("selectedInstrumentGenre").toInt();
            instrumentGenreFilter->setCurrentIndex(selectedGenre);
            }
      settings.endGroup();
      ...
      }

That way, no need for saving it every time the index changes. It will simply be saved on destruction, and initialised on construction.

Plus, here Qt's doc about QSettings::sync()
The settings are already synced in the destructor so there shouldn't be any need for line 1073 here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. You're right, the settings value could be written in the destructor. I'm a bit new to C++ so I don't know the ins and outs of it very well yet, so your feedback really helps :)

@@ -50,6 +50,7 @@ class SelectInstrument : public QDialog, private Ui::SelectInstrument {
public:
SelectInstrument(const Instrument*, QWidget* parent = 0);
const InstrumentTemplate* instrTemplate() const;
~SelectInstrument();
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as upper

@ecstrema
Copy link
Contributor

ecstrema commented Aug 14, 2018

Here's a few things you could also do around:

  • remove the InstrumentsWidget::writeSettings() function, since it seems like it has no definition, and is never called.
  • replace the few Qt's foreach by _c++ 11's(the link is for an example) for_ in instrwidget.cpp and instrtemplate.cpp.

@ecstrema
Copy link
Contributor

ecstrema commented Aug 14, 2018

Actually, I think the index should not be saved as an int, but as a QString, since the indexes may vary if new instrument lists are added (such as with Extensions and PREF_APP_PATHS_INSTRUMENTLIST2).
I imagine something like

///constructor:
//...
QSettings settings;
settings.beginGroup(objectName());
if ( ! settings.value("selectedInstrumentGenre").isNull() ){
      QString selectedGenre = settings.value("selectedInstrumentGenre").toString();
      instrumentGenreFilter->setCurrentText(selectedGenre);
      }
settings.endGroup();
//...

///and the destructor:
//...
QSettings s;
s.beginGroup(objectName());
s.setValue("selectedInstrumentGenre", instrumentGenreFilter->currentText());
s.endGroup();
//...

@jthistle
Copy link
Contributor Author

jthistle commented Aug 14, 2018

@Marr11317

Actually, I think the index should not be saved as an int, but as a QString

I don't think it should because you could have the reverse of the problem you're talking about: you're worried that instrument genre indices may change, but what about the instrument genre names? Surely those could change as well, and if they do then there would be a similar problem. Because of this, I'm going to stick with saving the index.

@jthistle jthistle force-pushed the 274540-save-instrument-group-selection branch 2 times, most recently from 55fb1e9 to 6da831c Compare August 14, 2018 15:50
@jthistle
Copy link
Contributor Author

Any final review needed @Marr11317 ?

@ecstrema
Copy link
Contributor

you're worried that instrument genre indices may change, but what about the instrument genre names? Surely those could change as well, and if they do then there would be a similar problem

But then, if the string changes, and so the saved string is not found, the index will be set to 0, which means general instruments. But if new instruments lists are added, and you try to put the index to let's say 3, It could be something completely random such as "Indian folk instruments of the nineteenth century"

@ecstrema
Copy link
Contributor

settings.beginGroup(objectName()) is widely used for two reasons:

  1. Since the objectName() is almost all of the time set to the class' name, and you can't have two class with the same name (under the same namespace), it avoids saving two values at the same place.
  2. An example will be easier in that case: suppose in an other dialog you use that same InstrumentsWidget class. Now, this dialog is specific to some kind off particular music (let's say earlyMusic). People that use this dialog may not want it to have the same default value as in the new score wizard, but since the 2 values are saved at the same place, they will. So what you do is this:
InstrumentsWidget* iw = new InstrumentsWidget();
iw->setObjectName("Some new specific name");

And now the values won't be saved at the same place because objectName() doesn't return the same thing anymore.
Of course, they will still be loaded at the same place so you will have to create a readSettings() function which will be reading your settings under the objectName(), and you will have to add this line in InstrumentsWidget's constructor:

connect(this, &QObject::objectNameChanged, this, &InstrumentsWidget::readSettings);

So could you just change these "instrumentGenre" to objectName()?
PS: it looks like the index in instrwidget.cpp and selinstrument.cpp are now saved under the same adress.

@jthistle
Copy link
Contributor Author

@Marr11317

The whole point of this feature request is so that the genre is the same. It doesn't make sense to have it different in different dialogues. The genre, when changed in the 'add instrument' windows, should also change in the 'change instrument' window (for changing the instrument of a stave). The reasoning being that when working on a project with a particular genre of instruments you don't want to have to keep changing the genre, neither when you're adding an instrument nor when you're changing one. So, the value must be saved in the same place, something that can't be done across objects using the objectName() as the group name.

@ecstrema
Copy link
Contributor

looks good to me.

@jthistle
Copy link
Contributor Author

@Marr11317 after some investigation, I've found out that things aren't as simple as they might be :/

The instruments widget doesn't ever destruct during runtime - it constructs once, when first opened, then destructs once when the program closes. The select instrument window, on the other hand, does construct every time when opened, and destructs every time it's closed. I'm going to have to go back to writing and syncing in the on_instrumentGenreFilter_currentIndexChanged method, as inelegant as it may be. This may require me having to use a variable again, but hey, what can you do sometimes 🤷‍♂️

@jthistle jthistle force-pushed the 274540-save-instrument-group-selection branch from 6da831c to bcd67b5 Compare August 16, 2018 08:30
@jthistle
Copy link
Contributor Author

jthistle commented Aug 16, 2018

@Marr11317 (one final review maybe?)

Right. This should be the final commit. Things have had to be changed a bit - as I said before, the Instrument Dialog wasn't being destroyed on close. Now, it is destroyed on close, but it's not simple, because sometimes the Instrument Widget (a child of the Dialog) needs to persist for a while after closure to allow instrument changes to be made. So the, Dialog needs to be closed at different points in the code depending on if a change has been made or not (and so it needs to happen in the editInstrList method). And, because the Dialog is a child of the main window, cleanup needs to happen in the main MuseScore class, which is why I've added a method to it in the musescore.cpp file. Phew. Saving does now happen in destructors though, which is what you wanted I think :)

@jthistle jthistle force-pushed the 274540-save-instrument-group-selection branch 2 times, most recently from 767720e to 7d18eb3 Compare August 16, 2018 08:44
{
if (instrList){
// ugly, but instrList->done(1) doesn't work, so instrList must be directly destroyed
instrList->~InstrumentsDialog();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about delete instrList;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, yes that would be better 🤦‍♂️

@jthistle jthistle force-pushed the 274540-save-instrument-group-selection branch from 7d18eb3 to 3e1903f Compare August 16, 2018 10:15
@jthistle
Copy link
Contributor Author

There, all done @Marr11317 . Ready for merge now @anatoly-os ?

@Jojo-Schmitz
Copy link
Contributor

Rebase needed, conflict should not be too difficult to fix

@jthistle jthistle changed the title Save user choice of instrument genre Fix #274540: save user choice of instrument genre Jan 5, 2019
@jthistle jthistle changed the title Fix #274540: save user choice of instrument genre fix #274540: save user choice of instrument genre Jan 5, 2019
@jthistle jthistle closed this Jan 5, 2019
@jthistle jthistle force-pushed the 274540-save-instrument-group-selection branch from 3e1903f to cb982cd Compare January 5, 2019 11:02
@jthistle jthistle reopened this Jan 5, 2019
@jthistle
Copy link
Contributor Author

jthistle commented Jan 5, 2019

Accidently force-pushed branch without committing 🤦‍♂️
Reopening.

@jthistle
Copy link
Contributor Author

jthistle commented Jan 5, 2019

This PR is outdated. The code is also badly written. I will rewrite it, and open a new PR, and as such I am closing this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants