How set Save_config_pending_items to {} in a macro?

Basic Information:

Printer Model: Creality CR6 SE and MAX
MCU / Printerboard: Linux Mint PC
klippy.log: n/a

Fill out above information and in all cases attach your klippy.log file. Pasting your printer.cfg is not needed
Be sure to check our Knowledge Base and in particular this and this post

Describe your issue:

… The SAVE_CONFIG process has a couple of behaviours that are problematic for novice users. In particular, it uses save_config_pending_items to “stack” multiple configuration changes, and then execute them all in a batch, when the user enters a single SAVE_CONFIG command.

That behaviour is not intuitive, nor is it particularly “friendly” to lock people into making changes they did not intend to make, and to do it without warning.

I would like to help users of my DWIN_SET UI to avoid such pitfalls, but
I do not see any way to clear that stack, or to at least inspect the contents and remove anything that should NOT be saved.

Has anyone here found a way to do that, without having to customize configfile.py? Jinja2 will let me inspect the items dictionary, but I have no idea whether/how I could reset save_config_pending to False and reset save_config_pending_items to an empty dictionary.

Is it just not doable? Or can someone advise me how it is done?

This script does not work, but illustrates one example of what I am trying to learn how to do:

> [gcode_macro CLEAR_SAVE_CONFIG_PENDING_ITEMS]
> description: IN WIP. Syntactically incorrect in this form.
> gcode:
>     {% if printer['configfile'].save_config_pending_items != {} %}  
>         {% set printer['configfile'].save_config_pending_items = {} %}
>     {% endif %}

Can you describe a workflow where a user (novice or otherwise) would have multiple configuration changes stacked up?

Any time I have been given the option to do a SAVE_CONFIG it’s after one operation (PROBE_CALIBRATE, PID_CALIBRATE, etc.) that is usually a prerequisite for other operations further on and it doesn’t seem reasonable or appropriate to hold off on doing them until I have a bunch of them.

The first workflow that triggered this research was when I discovered that if I changed the brightness of my display, Klipper saved that change in save_config_pending_items. Then I calibrated the Z probe, did SAVE_CONFIG and it failed, because the brightness preset was in an included cfg file.

I have worked around that problem by consolidating all presets into printer.cfg, but I do not know why SAVE_CONFIG is managing the brightness value so I don’t know what else it may try to update.

If I run ABL, it offers to SAVE_CONFIG. Suppose I don’t want to save it, so I move on to Z Offset. As a user, I am not going to realize that the Z Offset SAVE_CONFIG has also now saved that ABL mesh to the default profile.

Same with PID. I might PID for the current print, but not for my usual print temperature, so I don’t use SAVE_CONFIG. Then I update my default bed mesh and SAVE_CONFIG. I don’t realize that the PID has now also been saved.

Since it is my display UI that sets me (and other users) up for these troubles, I am trying to at least signal that SAVE_CONFIG has items pending. I could also parse save_config_pending_items to enumerate what is pending, but the only way to clear that stack, if I don’t want to commit all of it, seems to be to literally RESTART.

1 Like

To be honest; I would say that of the cases you’ve cited, only the one that I would consider an issue is where you change the brightness and then one other parameter. Setting the Z offset, PID(s), ABL and other operations all have pretty good messages for the user indicating that a SAVE_CONFIG is required to make sure the changes are saved before going on.

However, from what you describe, the brightness case sounds like something that should be addressed if it doesn’t prompt the user to do a SAVE_CONFIG after changes have been made. Is this for KlipperScreen or for an attached display?

The brightness is for the stock TFT, which I am integrating through t5uid1.py.

I do not agree with your assessment of the other cases, though. Although the functions do make clear that a SAVE_CONFIG is necessary to make the changes persistent, they do not make clear that if you elect not to SAVE_CONFIG at the time, then Klipper will happily hold onto those settings and will apply them anyway, the next time you SAVE_CONFIG something else.

1 Like

In my view, it is evident that any alterations made will remain in effect until they are either committed through SAVE_CONFIG or abandoned by shutting down/restarting Klipper.
This is a standard practice for most programs. A Word document will remain on your screen. If you don’t save your work on Word before closing it, it will be lost. However, if you save it beforehand, you can use it again later.
I see this as a common-sense approach.

Edit:

Since this is a third party contribution and not part of Klipper’s main-line it is completely at the discretion of the relevant author to have it implemented as it is.

Edit 2:
grafik

fluidd for example, makes it pretty clear that there are pending changes to be saved. If you hit this button, you’ll even get a summary of all changes that are committed

1 Like

So is there is no known way to do that for which I requested help, or you don’t want to help me if you don’t agree with what I am trying to do?

I am trying to make a friendly user interface for Marlin users, to help them make the jump to Klipper. It is not common sense to me that users should have to restart their machines to decline an invitation to save the results of a process. I don’t reboot my PC to close a Word file.

Changes to this system has been discussed numerous times, e.g. also the possibility to save without restart. All these have been declined so far by the lead developer, since he has concerns that it will lead to undefined states.
If this is true for manipulating the stack of pending changes as well, I cannot tell. Would be up to the dev to decide. I personally do not know any accessible / exposed way to manipulate this today.

A simple restart of the Klipper host service clears the stack (Same as Word ;-)).

Nope, me neither.

I guess that leaves me looking at alternatives for alerting the user to the presence of items in the save_config_pending_items queue, without any means of clearing them out of the list, except by pressing RESTART.

I have my own save_variables system for controlling a couple of other parameters, so I could move brightness out of printer.cfg if I want to. I have no idea why Klipper tries to SAVE_CONFIG a custom parameter anyway. No one has been able to tell me why it does that, either, especially if it is going to fail anyway with it being an included variable.

I draw the line at modifying Klipper modules, though.
Watching the discussion on plugins with interest.

There’s probably an argument to be made for an option not only to not do a SAVE_CONFIG, but also to use the new value for this session only. The effect would be (for example) if you measure the bed mesh and then choose “this session only,” it will keep the newly measured mesh, but you will lose it (and revert to the previously configured mesh) if you RESTART or SAVE_CONFIG at a later time.

Still, I suspect that this is likely to come less often in normal operation than during initial setup/configuration/tuning. Returning to the bed mesh example, if you’re using a macro to probe only the used bed area before every print, you probably would not want this to be saved to the config file if you do SAVE_CONFIG. But by the same token, if you’re doing that bed probe on every print, it may not matter whether it’s saved to the config because it gets overridden by the new probe sequence anyway.

So I guess my suggestion is that new users should be encouraged to focus on setting up/configuring/tuning one parameter at a time and then do a SAVE_CONFIG before moving on to the next parameter.

1 Like

Thank you, yes I have grouped the UI controls with that idea of one parameter at a time. I provided a SAVE_CONFIG button on each screen whose function calls for a SAVE_CONFIG command to commit the change. I did not realize, when I did that, that unsaved changes would be “stacked” and implemented later, on a different screen in the context of a different function.

I long ago discovered that I do not control the user, I can only influence the user experience. In this case, I am trying not to set my users up for failure.

I asked how to clear the stack, so that I could reset the pending items to a null set if a user exits a screen without a SAVE_CONFIG, when a SAVE_CONFIG was required, to commit the change.

The “if” statement in my code snippet does test the state of save_config_pending_items correctly, so I can design the UI to alert the user when the stack is not empty. Displaying what changes are pending becomes more of a challenge, with limited screen space and numerous possible scenarios.

Anticipating the stacking of changes, and clearing the stack if a user leaves a screen without selecting SAVE_CONFIG seems to me the more “user friendly” design. KISS vs RTFM.

I am not asking for a change to Klipper, just asking how to work within the current klippy/extras framework, to harden my UI against this undocumented and unexpected behaviour.

Looking at configfile.py, it looks like the configfile parser searches for the autosave header:

#*# <---------------------- SAVE_CONFIG ---------------------->
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
#*#

And considers any parameters that appear after it to be “autosave data,” which is subject to being overwritten by SAVE_CONFIG. So if you’re putting custom parameters or importing other .cfg files after the autosave header, that may explain why Klipper is trying to autosave those values in the first place.

As for the original question, it looks like save_config_pending_items is just a status-reporting object. Changing the contents of save_config_pending_items would not actually affect the behavior of SAVE_CONFIG because SAVE_CONFIG doesn’t look at save_config_pending_items, it looks at self.autosave.fileconfig.sections().

To do what you want, I think your module would need to use the remove_section method, which looks like it would remove the specified configfile section from self.autosave.fileconfig.sections() and from save_config_pending_items.

That is helpful. Thank you!