Serialize/Deserialize GUI
It is useful in some cases to save the state of the GUI and restore it later. In most cases, widget states are defined by the widget values.
In magic-class, simple serialize and deserialize functions are available. These
methods can be used to save and load the state of the magicgui's Container widgets
or any magic-class widgets. All the value widgets or value-like widgets are recursively
converted into dictionaries.
What is a value-like widget?
ValueWidget, such asSpinBox,Slider,CheckBox,ComboBox, etc.- Widget class that has a
valueproperty with both getter and setter being defined. This includesFileEditand custom magic-classes that have a propervalueproperty.
Basic Usage
serialize and deserialize are available in the magicclass.serialize module.
serialize converts the widget state to a dictionary, and deserialize does the
opposite.
Here's an example of serializing a magicgui's FunctionGui.
from magicgui import magicgui
from magicclass.serialize import serialize, deserialize
@magicgui
def func(x: int = 1, y: str = "X"):
pass
serialize(func)
{'x': 1, 'y': 'X'}
And it can be deserialized back to the FunctionGui using deserialize.
deserialize(func, {"x": 2, "y": "Y"})
print(func)
<FunctionGui func(x: int = 2, y: str = 'Y')>
Same functions can be used to serialize and deserialize magic-class widgets.
from magicclass import magicclass, field
@magicclass
class Parameters:
x = field(1)
y = field("X")
def print(self):
"""Print the parameters"""
print("x =", self.x.value, ", y =", self.y.value)
params = Parameters()
serialize(params)
{'x': 1, 'y': 'X'}
Note
You may have noticed that a magic-class widget became very similar to a
pydantic model.
Custom Serialization
If a custom Container subclass or a magic-class widget need a special way for
serialization and deserialization, you can define __magicclass_serialize__ and
__magicclass_deserialize__ methods to do this.
In the following example, widget values are saved as a tuple instead of separate dictionary items.
from magicclass import magicclass, field
@magicclass
class A:
x = field(1)
y = field("X")
def __magicclass_serialize__(self):
return {"custom_value": (self.x.value, self.y.value)}
def __magicclass_deserialize__(self, data):
self.x.value, self.y.value = data["custom_value"]
Note
You can use serialize in __magicclass_serialize__ and deserialize in
__magicclass_deserialize__, because these functions detects and avoids recursion.
Skip Serialization for Some Values
Serialized data should be simple and usually JSON serializable. Some widgets may hold
very complex data such as numpy.ndarray or pandas.DataFrame. In these cases, you
may want to skip serialization for these values.
The skip_if argument of serialize is designed for this purpose. It should be a
callable that take a value and return True if the value should be skipped.
# A widget that load an image from a path
from pathlib import Path
import numpy as np
from magicgui.widgets import Image
from magicclass import magicclass, vfield
from magicclass.serialize import serialize
@magicclass
class A:
img = vfield(Image)
path = vfield(Path)
def load(self):
"""Load image from path"""
from skimage.io import imread
self.img = imread(self.path)
ui = A()
ui.path = "path/to/image.png"
ui.load()
serialize(ui, skip_if=lambda x: isinstance(x, np.ndarray))
{'path': WindowsPath('path/to/image.png')}