Best way to preserve tool settings?

I’ve created a fairly UI intensive tool for Maya, and I really would like to be able to preserve the settings between sessions. Previously I’ve added functionality to tools to save all the settings out to an ini file, that could be read back in later. This works, but it’s not practical in this case.

What approach do you use to preserve user settings on tools?

(I’m using PyMel with Maya2008, fyi)

Thanks!

you could always use optionVars?
TBH, if you’ve already some modules to handle writing and reading back variables from an .ini files, I doubt you’ll find optionVars more practical.

You can also write a simple wrapper class for your UI and then pickle it to disk. pickle — Python object serialization — Python 3.12.6 documentation

I tend to dislike pickling for that sort of application, it has its purposes, but there are a host of better solutions.

I wrote a few functions to get the [Windown] app data directory from the registry, and then wrote a class to represent generic “settings,” which is capable of reading from and storing to an XML file. Now, any time I need to load or save settings, I extend the class (10-15 minutes work) and I have a good, robust [windows-based] system.

I’d be interested in seeing people’s opinions on using app data vs. just storing settings in the script’s directory or a subdirectory thereof.

I haven’t personally found a need to go beyond optionVars, but most of the tools I write are pretty much self contained within Maya…

Regardless of what you’re writing out, I would heartily recommend not writing it in the application’s directory. If your tool ever runs on a PC where the user does not have full admin rights, or they have User Account Control (UAC) enabled, Windows will put the bozak on it. This is most often the case at overseas outsourcing studios.

Be a good Windows citizen and write your app’s data to the user’s appdata folder. That’s what the folder is for. You generally don’t have to dig into registry to find it, either:

>>> os.getenv( ‘appdata’ )
‘C:\Users\adam.pletcher\AppData\Roaming’

To the original topic… If I know the settings don’t need to be super readable, and only Python needs to access them, I use cPickle. There’s nothing simpler in Python. If the above isn’t true, I’ll use INI files (via ConfigParser module) or XML (using cElementTree), depending on the situation.

An additional note… We store all our user settings in the Roaming appdata folder shown above (under “Volition\whatever” subdirs), even though our IT guys don’t actually have roaming profiles enabled. This is typical for most off-the-shelf apps as well.

I use QSettings from Qt package. It has support for all sorts of storage of the shelf by just editing a flag and handles data conversion automagically for you.

Wow there are some… strange ideas here!

First thing, where you put the data- Adam is right, your settings must go into the appdata folder. You can also use the ‘appdirs’ python module. You should be putting logs there as well. Do not use the registry, do not use the application folder, just do what MS tells you to do, and what users expect, and what takes 0 extra work, and use the appdata folder (through os.environ, or appdirs module).

Second thing. Something many people in games miss is one reason why dynamic languages like python have been so successful in web development- the effortless serialization of data to and from objects. I have absolutely no idea why people would complicate things unnecessarily with all sorts of custom behavior, XML, etc. I have no idea why anyone is parsing or using special modules anything- just use dictionaries and serialize to json (which is in the stdlib) or yaml or whatever (I prefer ascii formats so I can read what’s in there, I have no idea why you’d use binary for this, but whatever, that’s easy to change). If dealing with your settings is doing any more than calling dump and load, you’re over complicating things. Nor do I understand the choice for INI files over other data formats.

We have a python class with GetValue, SetValue, and GetOrSet methods, that will save out the preferences after each Set and will load things from disk automatically, as this is a lot more natural to not have to worry about those aspects on the caller (things just get magically persisted to the filename you initialize it with).

Now this part is more personal but I’d strongly discourage people from using ‘magical’ persistence of UI’s. Be explicit with what you save out and load. UI’s are usually very stateful and dependent upon events and applying some state from preferences to a UI magically is a recipe for disaster and maintenance difficulties. If you want to persist the state of a control, just write the code to persist that state’s control. We’re talking about 2 lines of code and code that is very easy to get right- just be explicit. Remember that frameworks like Qt are very much more geared for languages like C where they do not have a fraction of the ‘batteries’ python comes with. QSettings are IMO complete overkill for python.

I know a lot of the ‘difficulty’ for settings, which should be incredibly simple, are because as programmers we tend to be absolutely horrible with our treatment of mutable state, and of course with UI code as well, and then we treat preferences as global so wow just pile on the mistakes. If you design your code correctly, the treatment of settings is trivial- we have wonderful persistence of pretty much every UI we write here and it took very little work to get it working properly.

While I agree with everything you say, I would not say QSettings is overkill, it has its place. QSettings supports everything you listed like app data dir, and it walks hand to hand with the Qt data types which you’ll mostly encounter wether you use the gui module or any other module it provides.

If Qt provides a robust cross platform solution for a specific problem then I don’t see a reason to write my own especially when I mostly sit with Qt, unless the provided solution to my problem is insufficent in terms of requirements. Besides you can’t json dump / load Qt objects, pickling works though.

But I digress, the OP didn’t mention anything about Qt or Qt based ui.

Rob, so in this case i take it you wouldn’t suggest pickling the whole UI in order to maintain state? (this sounds…ugly to me but that might just be me and my anti-pickling prejudice).

would your solution then be to have a dict of settings (say, {selected_tab : 3, number_of_fucks: 0}) that are explicitly selected things in the UI that should be persistent, and then serialize that dict to a file?

that was the solution I’ve been using, except that I took the reprehensibly un-lazy route of writing something to serialize to xml instead of using any of the build in serializers.

just looking for clarification, because there’s more learning for me to glean from this thread :3

To focus on the anti-pickle thing a second… Why does it matter what the format is? Does it have to be very human-readable? Does it need to be read or written by languages other than Python? Is there something that’s physically impossible to serialize with pickle?

If no to above, why cook up your own XML read/write code? It’s just more new software that can have bugs and require general attention, compared to something with tons of mileage out of it out of the standard library.

Regarding what settings should persist, I would also not recommend saving everything in a UI. Be selective, and learn what minimum number of settings could benefit from persistence and user-configurability, and where it’s not worth it.

Yasin: Of course, and if this is your code, do what works. But if you’re working with other programmers, you are requiring them to work with and trust a framework and system they may be unfamiliar with, to accomplish what should be a trivial pure python task.

Adam: Pickling to binary is fine, and format doesn’t really matter. I prefer ascii since I can see what’s in my files, and it is editable, and I can do things like change settings to try to force edge cases and see what happens. Anyway, if you’re using the pickle interface of dump/load, it is trivial to change. I use pickle when speed or size matters, or you’re saying custom python types, which don’t matter for settings. So I usually use JSON since it is part of the stdlib and is easily editable. We had a similar discussion in the pose tool thread but as settings are not even persistent data (in the sense that it goes into P4), and they are so much simpler than an animation pose format, it matters less IMO.

Tyler: Essentially, yes. But I have no doubt you’ll find better ways of doing things, as you’re smart and haven’t yet 1) been abused by XML where simpler solutions suffice, 2) been burned by some automagical serialization system that ends up being way too limiting or too much trouble (often these systems use XML, coincidentally or not!), and 3) developed a knack for understanding what you should write and what you don’t need to (I blogged about this recently wrt the feed aggregator, we all make this mistake for new things).

Adam: back when i first started trying to pickle things, I kept on coming across errors when I would try to read the pickled string (My guess is that I was not saving the files correctly, or I was making some sort of really ubernewb mistake. I got frustrated with it early and I’m probably still operating on that prejudice because I haven’t [knowingly] worked with pickles since. I didn’t know any other serialization stuff out of the box, so I derped and wrote my own.

Rob: I’m unfamiliar with JSON, i’ll be doing some research into it later. I ended up using xml because i could extend the writer to serialize just about anything to a tree structure (basically interpreting object x as a tree structure and writing that to XML). coercing objects to dicts and then serializing them is essentially the same process, but makes more sense in hindsight.

tl;dr: ignore any advice I’ve given so far, listen to the others :slight_smile: