Page Settings spatium rounding causes downstream issues
This might seem nuts to the few of you that are aware of the feature request I have regarding page settings units, but it was in the course of trying to fix the one outstanding issue I have with that branch that I discovered this abnormal behavior in MuseScore 2.2 and 3.0.
This is a series of odd behaviors that begs the question I was trying to ask in the first place:
How are the Page Settings Units (inch vs. mm) saved and restored in MuseScore?
I saved 2 uncompressed .mscx files with inch vs. mm being the only difference, and the files are identical! Or is this a known issue that is low priority because it doesn't actually affect much of anything?
Steps to reproduce:
1) open the attached file. click the Layout menu, Page Settings...
2) the units should be set to "mm", change them to "inch", click OK
3) click the File menu, Save As..., change the name to "in72.mscz" (or whatever you like...)
4) click the File menu, Open..., and open the original mm72.mscz.
5) check the Page Settings for mm72.mscz again, now the units are set to inch.
6) close and re-open MuseScore
7) open the original mm72.mscz file, check its Page Settings, and the units are now correct, mm
8) open the modified in72.mscz file, check its Page Settings, and the units are now incorrect, mm. they should be inch
diffon new score uncompressed files shows that only the margins and spatium are different.
I would guess that the page setting units aren't saved.
I'm curious why the spatium and margins are different. AFAIK MuseScore stores those numbers in one kind of unit, regardless of the page settings, then it converts when it displays the page settings. If exportxml.cpp has anything to do with it, those units are at 144DPI, the internal 72DPI values multiplied by 2.
So if all you did was change the units in the Page Settings dialog from mm to inch, why did the spatium and the margins change?
fyi - these lines of code in the PageSettings constructor are key:
After the first time you open/close the dialog box, the pageSettings member variable in the Score object preserves the setting for the duration of the session, though this issue illustrates some odd behavior even if that's supposed to be the scenario.
Try this for odd behaviour:
1. new score
2. layout -> page settings
3. switch between mm and in and click 'apply' (not 'ok')
4. repeat 3 a few times
The margins keep changing for me.
Now try the above but with clicking 'ok'.
The margins change a few times, but then stop changing.
Re: margins changing. I replicated your steps and here is what I see:
Default margins for new score are 10mm except for bottom, which is 20mm
Switching to inch, then back to mm causes the left and right margins to change to 8.5mm from 10mm.
Switching back to inch again displays the correct, original 10mm value in inches = 0.39in
But from now on, no matter how many times you switch back and forth inches to mm, it's 8.5mm for left and right margins, and the inches are correct, but out of sync with the mm.
That is very strange and uses very fixed values. I do not have a problem line of code or solution, but I wanted to clarify additional details of the behavior within the dialog box. This margins thing should probably be its own issue...
I just posted another new issue - this is new since 2.0.2 anyway - that is similar to your margins issue. See here:
I'm going to try to debug that one and see where it's going astray. It all sounds like scaling issues, unnecessary unit conversions. But I'm not sure if the margins and tempo text are related yet.
I see. I will file.
I am now certain that the Page Layout units are not stored in the mscz/x file format, and probably never have been. To do so requires adding a member variable to the PageFormat class, and modifying the code in PageFormat::write() and read() to deal with a new page-units tag. It would not affect backwards compatibility in any way, as it would still default to "mm" for files that don't contain the tag.
I don't want to create a PR for this if adding an element to the file format is completely off the table. This seems 100% safe to me, but I'm not all-seeing.
I'll be doing it in my own branch, which has points/pixels as an additional units option. So I can test compatibility there.
At some level, preference for mm versus inches seems more of an overall program preference than something you'd typically want to set score by score. But of course, if that were to be the case, we should actually implement it that way. And sure, at least some people in the world probably deal with both metric and Imperial units and might appreciate a per-score override. Maybe we should look at a way of doing it that way - a global program preference that is saved like any other and maintained across scores, but also the possibility of a score-specific override?
That is one solution that I have been thinking about too, as it's mentioned in old the comments in the code. The global preferences idea appears to have been around a while, but never implemented.
But I also think that global preferences side of it should be aligned with a preference for paper size. There are 3 built in paper sizes that are inch-based, the rest are mm-based, and then there's "custom", which is neither, and would require an explicit preference setting for inch or mm (or pt/px, which is what I'm compiling at this very moment). I don't have a preference setting implemented, but I've implemented the changes to save the units in the file and read them from the file - very small amount of new code, offset by a small amount of deleted code too, which is always nice.
The preferences dialog is tabbed - which tab would all this occupy?
Here is a branch on github that saves and restores the units:
I see no problems opening old scores or new scores, the units default to mm, as they always have.
To see the changes relating to this issue, see the latest commit here:
This code stores the units as an int, because it has 3 different types of units, not just two. In the current code it could be a string or a bool, but I would recommend a string or int to leave open the possibility of adding new types of units.
Interestingly enough, this branch solves another recent issue, here:
Not sure how that happened, but I cleaned up a lot of stuff in page.cpp and pagesettings.cpp, and it appears to have solved that issue. In this code my right margin is defaulting to zero, which does not seem correct. But the left margin is staying constant when units are changed, which is the problem in issue #108226. I'll be looking into the right margin now. It's dependent on the left margin and page size.
So, what is the question?)
I see that latest master correctly saves and loads units to/from page settings.
Here is the question:
It's all about rounding floating point values and the fact that Qt spinbox cannot provide an unrounded value - it rounds the value the same as it displays the value. It might not be solvable in precise terms unless you completely avoid rounding the staff space value, and set the spinbox to use 12-15 decimals.
My pixels as units suggestion also solves the problem, but must be adapted to the DPI_F = 5 change that happened right after this original post, 2 years ago. I submitted a PR for that, but it was rejected at the time. It can certainly be adjusted to reflect the 5x multiplier DPI_F, and to address any other concerns.
Here is my last PR for this:
The main concern in the PR comments is about storing the units (mm vs in vs px) in the MusicXML file. I can definitely remove that and we can always store one type of unit (currently mm, but if we have px/pt that's clearly the best choice, as it's native to the code). I still agree with all my comments supporting the storage of the unit in the file, but I'm also interested in expediency and fixing this structural problem in MuseScore.
Another solution would be to add a "Reset Staff Space" button on the Page Settings dialog. Clicking it would reset the spatium to 25.000 and reset the spinbox display value in whatever units. IMO that's sidestepping the core issue of how units are handled in MuseScore, but it would certainly allow users to recover that clean staff space setting, and even use it with inches as their selected units. The button could disable/enable itself every time the dialog opens and every time the spinbox changes values, including as a side-effect of the user changing units. If the spatium value isn't exactly 25, enable the button. Indicating that the default has been changed makes this much more useful. There are "reset to default" buttons in the General Settings, so it does fit right in, and there's an existing graphic/button to clone from.
I have implemented the Reset Staff Space button in my branch. The button code is here:
This change was also required in applyToScore():
The visual change looks like this:
Note that clicking the button cannot be reversed by clicking Cancel. The original code is not setup for doing that because there is no place to store the unrounded value. This is a quick and dirty implementation of a quick and dirty solution. To make it work with Cancel could be done easily with a new class member var to store the unrounded value. I also didn't put the code in to disable the button, it's always enabled. But if you want to go this way, it's a good start.
In general, reset buttons return things to style defaults, or to a very clear and obvious nominal default value like 0. What would the function of this button be in the case of a score that was initially created from a template that set this to something other than 1.764mm? What if that default value ever changes in a subsequent version of MuseScore? I'm not totally comfortable with the idea of a reset button whose function is "set this to the fairly arbitrary number that just happened to be the default value chosen in this particular version of MuseScore for newly created scores that don't a templates - a value that may or may not have any relevance for this particular score". I would like think there would be a less hacky solution.
I don't disagree. I implemented it as a quick and dirty solution to an immediate problem, and offered it up as a possibility to the more structural change that is otherwise required. The only other alternative I've come up with is my PtPx branch and PR, which has some structural changes, though nothing too extreme or complex, IMO.
There are other alternatives, but they also require some kind of restructuring and/or maybe a feature change decision. I would very much like this topic to be taken seriously and resolved in a clean way that fits into MuseScore. It requires some serious discussion about what is currently a circular bit of logic and number rounding.
Agreed, but this sort of stuff is outside my area of expertise and I have no real-world experience with use cases where it would mater to me, so I don't really have opinions on the internal details.
See here for another user's problem with this:
If you are using Letter or Legal size paper, you are automatically switching to the 1.753mm spatium, which is (substantially) distorted by rounding. Maybe that gives you a better idea of a basic user issue with this.
Also see this issue mentioned earlier in this thread - it is due to this spatium tomfoolery:
I'm changing the issue title and setting the status to active in the hopes of elevating awareness of this issue. I believe I have provided the needed info required to make it active again.
I'm changing the status back to patch (code needs review), because that is the accurate status. I felt the need to churn this over one time to indicate that it's a new patch.
I'm sorry, I churned the wrong issue!
PR is here: https://github.com/musescore/MuseScore/pull/4456
It has PDF docs attached.
This is now part of this EPIC issue: https://musescore.org/en/node/280363
see #280363: [EPIC] Page Settings: fixes and changes
Reading this thread, it seems to me it touches the root cause but seems not solve it. As I wrote in Page Settings: switching to inches distorts staff space, the issue is: Floating point values are, as strings, written into a spinbox and, on a OK or Apply of the form, read and save in the score. It is this
float -> string -> floatconversion which causes the issue.
I think the solution is (at least, that's what I used to do in similar situations in the past), write values in the score only when they are really changed by the user.
I had a quick look and PR4456 and PR4906 but I'm not sure they touch the actual root cause for this particular issue.