# Data set examples

## Basic example

Source code :

    # -*- coding: utf-8 -*-

    import guidata
    import guidata.dataset as gds

    # Note: the following line is not required if a QApplication has already been created
    _app = guidata.qapplication()

    class Processing(
        gds.DataSet,
        title="Processing Parameters",
        comment="This comment concerns this class of parameters, and may be overriden "
        "when calling the constructor by passing a comment argument. That is the same for "
        "the title.",
    ):
        """Example"""

        a = gds.FloatItem("Parameter #1", default=2.3)
        b = gds.IntItem("Parameter #2", min=0, max=10, default=5)
        type = gds.ChoiceItem("Processing algorithm", ("type 1", "type 2", "type 3"))

    param = Processing()  # Default title and comment are used if not provided here
    param.edit()
    print(param)  # Showing param contents
    param.b = 4  # Modifying item value
    param.view()

    # Alternative way for creating a DataSet instance:
    param = Processing.create(a=7.323, b=4)
    print(param)

## Other examples

A lot of examples are available in the `guidata` test module

    from guidata import tests
    tests.run()

The two lines above execute the guidata test launcher :

### All `guidata` items demo

    import atexit
    import datetime
    import shutil
    import tempfile
    from enum import Enum

    import numpy as np

    import guidata.dataset as gds
    from guidata.config import _
    from guidata.env import execenv
    from guidata.qthelpers import qt_app_context

    # Creating temporary files and registering cleanup functions
    TEMPDIR = tempfile.mkdtemp(prefix="test_")
    atexit.register(shutil.rmtree, TEMPDIR)
    FILE_ETA = tempfile.NamedTemporaryFile(suffix=".eta", dir=TEMPDIR)
    atexit.register(FILE_ETA.close)
    FILE_CSV = tempfile.NamedTemporaryFile(suffix=".csv", dir=TEMPDIR)
    atexit.register(FILE_CSV.close)

    class ImageTypes(Enum):
        """Image types."""

        #: Image filled with zeros
        ZEROS = _("Zeros")
        #: Empty image (filled with data from memory state)
        EMPTY = _("Empty")
        #: Image filled with random data (uniform law)
        UNIFORMRANDOM = _("Random (uniform law)")
        #: Image filled with random data (normal law)
        NORMALRANDOM = _("Random (normal law)")
        #: 2D Gaussian image
        GAUSS = _("Gaussian")
        #: Bilinear form image
        RAMP = _("2D ramp")

    class FilteringMethod(gds.LabeledEnum):
        """LabeledEnum example with invariant keys and UI labels."""

        GAUSSIAN = "gaussian", _("Gaussian filter")
        MEDIAN = "median", _("Median filter")
        SOBEL = "sobel", _("Sobel edge detection")
        LAPLACIAN = "laplacian", _("Laplacian filter")
        BILATERAL = "bilateral", _("Bilateral filter")
        MORPHOLOGICAL = "morphological"  # Uses key as label

    class ThresholdMode(gds.LabeledEnum):
        """Another LabeledEnum example."""

        OTSU = "otsu_method", _("Otsu's method")
        ADAPTIVE = "adaptive_mean", _("Adaptive mean")
        GLOBAL = "global_threshold", _("Global threshold")
        TRIANGLE = "triangle_algorithm", _("Triangle algorithm")

    class Parameters(gds.DataSet):
        """
        DataSet test
        The following text is the DataSet 'comment': <br>Plain text or
        <b>rich text<sup>2</sup></b> are both supported,
        as well as special characters (α, β, γ, δ, ...)
        """

        dir = gds.DirectoryItem("Directory", default=TEMPDIR)
        preview = gds.TextItem("File names preview", default="-")
        option = gds.ChoiceItem("Option", (("1", "first choice"), ("2", "second choice")))

        fname = gds.FileOpenItem("Open file", ("csv", "eta"), FILE_CSV.name)
        fnames = gds.FilesOpenItem("Open files", "csv", [FILE_CSV.name])
        fname_s = gds.FileSaveItem("Save file", "eta", FILE_ETA.name)
        string = gds.StringItem("String", default="default string !?")
        text = gds.TextItem("Text", default="default\nmultiline\ntext")
        float_slider = gds.FloatItem(
            "Float (with slider)", default=0.5, min=0, max=1, step=0.01, slider=True
        )
        integer = gds.IntItem("Integer", default=5, min=3, max=16, slider=True).set_pos(
            col=1
        )
        dtime = gds.DateTimeItem("Date/time", default=datetime.datetime(2010, 10, 10))
        date = gds.DateItem("Date", default=datetime.date(2010, 10, 10)).set_pos(col=1)
        bool1 = gds.BoolItem("Boolean option without label")
        bool2 = gds.BoolItem("Boolean option with label", "Label")
        _bg = gds.BeginGroup("A sub group")
        color = gds.ColorItem("Color", default="red")
        choice1 = gds.ChoiceItem(
            "Single choice 1",
            [("16", "first choice"), ("32", "second choice"), ("64", "third choice")],
        )
        choice2 = gds.ChoiceItem("Image type", ImageTypes, default=ImageTypes.GAUSS)

        # LabeledEnum examples - Pattern 1 with invariant keys and UI labels
        filter_method = gds.ChoiceItem(
            "Filtering method", FilteringMethod, default=FilteringMethod.GAUSSIAN
        )
        threshold_mode = gds.ChoiceItem(
            "Threshold mode", ThresholdMode, default=ThresholdMode.OTSU
        )

        mchoice2 = gds.ImageChoiceItem(
            "Single choice 2",
            [
                ("rect", "first choice", "gif.png"),
                ("ell", "second choice", "txt.png"),
                ("qcq", "third choice", "file.png"),
            ],
        )
        _eg = gds.EndGroup("A sub group")
        floatarray = gds.FloatArrayItem(
            "Float array", default=np.ones((50, 5), float), format=" %.2e "
        ).set_pos(col=1)
        mchoice3 = gds.MultipleChoiceItem(
            "MC type 1", [str(i) for i in range(12)]
        ).horizontal(4)
        mchoice1 = (
            gds.MultipleChoiceItem(
                "MC type 2", ["first choice", "second choice", "third choice"]
            )
            .vertical(1)
            .set_pos(col=1)
        )
        dictionary = gds.DictItem(
            "Dictionary",
            default={
                "lkl": 2,
                "tototo": 3,
                "zzzz": "lklk",
                "bool": True,
                "float": 1.234,
                "list": [1, 2.5, 3, "str", False, 5, {"lkl": 2, "l": [1, 2, 3]}],
            },
            help="This is a dictionary",
        )

    def test_all_items():
        """Test all DataItem objects"""
        with qt_app_context():
            prm = Parameters()

            prm.floatarray[:, 0] = np.linspace(-5, 5, 50)

            # Demonstrate LabeledEnum functionality
            execenv.print("=== LabeledEnum Pattern 1 Demo ===")
            execenv.print(f"Filter method (enum member): {prm.filter_method!r}")
            execenv.print(f"Filter value (invariant key): {prm.filter_method.value!r}")
            execenv.print(f"Filter label (UI display): {prm.filter_method.label!r}")
            execenv.print(f"Filter str(): {str(prm.filter_method)!r}")

            # Show different ways to set LabeledEnum values
            prm.filter_method = "sobel"  # Set by invariant key
            execenv.print(f"Set by key 'sobel': {prm.filter_method!r}")

            prm.filter_method = "Median filter"  # Set by UI label
            execenv.print(f"Set by label 'Median filter': {prm.filter_method!r}")

            prm.filter_method = 0  # Set by index
            execenv.print(f"Set by index 0: {prm.filter_method!r}")

            execenv.print("=== End LabeledEnum Demo ===")

            execenv.print(prm)
            if prm.edit():
                execenv.print(prm)
            prm.view()
            execenv.print("OK")

    if __name__ == "__main__":
        test_all_items()

### All (GUI-related) `guidata` features demo

    import atexit
    import shutil
    import tempfile

    import numpy as np

    import guidata.dataset as gds
    from guidata.config import ValidationMode, get_validation_mode, set_validation_mode
    from guidata.dataset.qtitemwidgets import DataSetWidget
    from guidata.dataset.qtwidgets import DataSetEditLayout, DataSetShowLayout
    from guidata.env import execenv
    from guidata.qthelpers import qt_app_context

    # Creating temporary files and registering cleanup functions
    TEMPDIR = tempfile.mkdtemp(prefix="test_")
    atexit.register(shutil.rmtree, TEMPDIR)
    FILE_ETA = tempfile.NamedTemporaryFile(suffix=".eta", dir=TEMPDIR)
    atexit.register(FILE_ETA.close)
    FILE_CSV = tempfile.NamedTemporaryFile(suffix=".csv", dir=TEMPDIR)
    atexit.register(FILE_CSV.close)

    class SubDataSet(gds.DataSet):
        dir = gds.DirectoryItem("Directory", TEMPDIR)
        fname = gds.FileOpenItem("Single file (open)", ("csv", "eta"), FILE_CSV.name)
        fnames = gds.FilesOpenItem("Multiple files", "csv", [FILE_CSV.name])
        fname_s = gds.FileSaveItem("Single file (save)", "eta", FILE_ETA.name)

    class SubDataSetWidget(DataSetWidget):
        klass = SubDataSet

    class SubDataSetItem(gds.ObjectItem):
        klass = SubDataSet

    DataSetEditLayout.register(SubDataSetItem, SubDataSetWidget)
    DataSetShowLayout.register(SubDataSetItem, SubDataSetWidget)

    class Parameters(gds.DataSet):
        """
        DataSet test
        The following text is the DataSet 'comment': <br>Plain text or
        <b>rich text<sup>2</sup></b> are both supported,
        as well as special characters (α, β, γ, δ, ...)
        """

        dict_ = gds.DictItem(
            "dict_item",
            {
                "strings_col": ["a", "b", "c"],
                "_col": [1, 2.0, 3],
                "float_col": 1.0,
            },
        )
        string = gds.StringItem("String", default="")
        string_regexp = gds.StringItem("String", regexp=r"^[a-z]+[0-9]$", default="abcd9")
        password = gds.StringItem("Password", default="", password=True)
        text = gds.TextItem("Text", default="")
        _bg = gds.BeginGroup("A sub group")
        float_slider = gds.FloatItem(
            "Float (with slider)", default=0.5, min=0, max=1, step=0.01, slider=True
        )
        fl1 = gds.FloatItem(
            "Current", default=10.0, min=1, max=30, unit="mA", help="Threshold current"
        )
        fl2 = gds.FloatItem("Float (col=1)", default=1.0, min=1, max=1).set_pos(col=1)
        fl3 = gds.FloatItem("Not checked float").set_prop("data", check_value=False)
        bool1 = gds.BoolItem("Boolean option without label")
        bool2 = gds.BoolItem("Boolean option with label", "Label").set_pos(col=1, colspan=2)
        color = gds.ColorItem("Color", default="red")
        choice1 = gds.ChoiceItem(
            "Single choice (radio)",
            [(16, "first choice"), (32, "second choice"), (64, "third choice")],
            radio=True,
        ).set_pos(col=1, colspan=2)
        choice2 = gds.ChoiceItem(
            "Single choice (combo)",
            [(16, "first choice"), (32, "second choice"), (64, "third choice")],
        ).set_pos(col=1, colspan=2)
        _eg = gds.EndGroup("A sub group")
        floatarray = gds.FloatArrayItem(
            "Float array", default=np.ones((50, 5), float), format=" %.2e "
        ).set_pos(col=1)
        floatarray2 = gds.FloatArrayItem(
            "Empty array", default=np.array([], float)
        ).set_pos(col=2)
        g0 = gds.BeginTabGroup("group")
        mchoice1 = gds.MultipleChoiceItem(
            "MC type 1", ["first choice", "second choice", "third choice"]
        ).vertical(2)
        mchoice2 = (
            gds.ImageChoiceItem(
                "MC type 2",
                [
                    ("rect", "first choice", "gif.png"),
                    ("ell", "second choice", "txt.png"),
                    ("qcq", "third choice", "html.png"),
                ],
            )
            .set_pos(col=1)
            .set_prop("display", icon="file.png")
            .set_prop("display", size=(32, 32))
        )
        mchoice3 = gds.MultipleChoiceItem(
            "MC type 3", [str(i) for i in range(10)]
        ).horizontal(2)
        eg0 = gds.EndTabGroup("group")
        integer_slider = gds.IntItem(
            "Integer (with slider)", default=5, min=-50, max=50, slider=True
        )
        integer = gds.IntItem("Integer", default=5, min=3, max=60).set_pos(col=1)

    def test_all_features():
        """Test all guidata item/group features"""
        old_mode = get_validation_mode()
        set_validation_mode(ValidationMode.STRICT)
        with execenv.context(accept_dialogs=True):
            with qt_app_context():
                prm1 = Parameters()
                prm1.floatarray[:, 0] = np.linspace(-5, 5, 50)
                execenv.print(prm1)

                if prm1.edit():
                    prm1.edit()
                    execenv.print(prm1)
                prm1.view()

                prm2 = Parameters.create(integer=59, string="Using `create`")
                assert prm2.integer == 59
                print(prm2)

                try:
                    # Try to set an unknown attribute using the `create` method:
                    Parameters.create(unknown_attribute=42)
                except AttributeError:
                    pass
                else:
                    raise AssertionError("AttributeError not raised")

                execenv.print("OK")
        set_validation_mode(old_mode)

    if __name__ == "__main__":
        test_all_features()

### Embedding guidata objects in GUI layouts

    import numpy as np
    from qtpy.QtWidgets import QMainWindow, QSplitter

    import guidata.dataset as gds
    from guidata.configtools import get_icon
    from guidata.dataset.qtwidgets import DataSetEditGroupBox, DataSetShowGroupBox
    from guidata.env import execenv
    from guidata.qthelpers import (
        add_actions,
        create_action,
        get_std_icon,
        qt_app_context,
        win32_fix_title_bar_background,
    )
    from guidata.tests.dataset.test_activable_dataset import Parameters
    from guidata.widgets import about

    class AnotherDataSet(gds.DataSet):
        """
        Example 2
        <b>Simple dataset example</b>
        """

        param0 = gds.ChoiceItem("Choice", ["deazdazk", "aeazee", "87575757"])
        param1 = gds.FloatItem("Foobar 1", default=0, min=0)
        a_group = gds.BeginGroup("A group")
        param2 = gds.FloatItem("Foobar 2", default=0.93)
        param3 = gds.FloatItem("Foobar 3", default=123)
        _a_group = gds.EndGroup("A group")

    class ExampleMultiGroupDataSet(gds.DataSet):
        """Example DS with multiple groups"""

        choices = gds.MultipleChoiceItem("Choices", ["a", "b", "c", "d", "e"])
        dictionary = gds.DictItem("Dictionary", {"a": 1, "b": 2, "c": 3})
        param0 = gds.ChoiceItem("Choice", ["deazdazk", "aeazee", "87575757"])
        param1 = gds.FloatItem("Foobar 1", default=0, min=0)
        t_group = gds.BeginTabGroup("T group")
        a_group = gds.BeginGroup("A group")
        param2 = gds.FloatItem("Foobar 2", default=0.93)
        dir1 = gds.DirectoryItem("Directory 1")
        file1 = gds.FileOpenItem("File 1")
        _a_group = gds.EndGroup("A group")
        b_group = gds.BeginGroup("B group")
        param3 = gds.FloatItem("Foobar 3", default=123)
        param4 = gds.BoolItem("Boolean")
        _b_group = gds.EndGroup("B group")
        c_group = gds.BeginGroup("C group")
        param5 = gds.FloatItem("Foobar 4", default=250)
        param6 = gds.DateItem("Date").set_prop("display", format="dd.MM.yyyy")
        param7 = gds.ColorItem("Color")
        _c_group = gds.EndGroup("C group")
        _t_group = gds.EndTabGroup("T group")

    class OtherDataSet(gds.DataSet):
        """Another example dataset"""

        group1 = gds.BeginGroup("Group 1")
        title = gds.StringItem("Title", default="Title")
        icon = gds.ChoiceItem(
            "Icon",
            (
                ("python.png", "Python"),
                ("guidata.svg", "guidata"),
                ("settings.png", "Settings"),
            ),
        )
        opacity = gds.FloatItem("Opacity", default=1.0, min=0.1, max=1)
        transform = gds.FloatArrayItem("Transform", default=np.array([1, 2, 3, 4, 5, 6]))
        _group1 = gds.EndGroup("Group 1")
        group2 = gds.BeginGroup("Group 2")
        computed = gds.StringItem("Computed").set_computed(
            lambda ds: f"T: {ds.title}, I: {ds.icon}, O: {ds.opacity}"
        )
        _group2 = gds.EndGroup("Group 2")

    class MainWindow(QMainWindow):
        """Main window"""

        def __init__(self):
            QMainWindow.__init__(self)
            win32_fix_title_bar_background(self)
            self.setWindowIcon(get_icon("python.png"))
            self.setWindowTitle("Application example")

            # Instantiate dataset-related widgets:
            self.groupbox1 = DataSetShowGroupBox(
                "Activable dataset", Parameters, comment=""
            )
            self.groupbox2 = DataSetShowGroupBox(
                "Standard dataset", AnotherDataSet, comment=""
            )
            self.groupbox3 = DataSetEditGroupBox(
                "Standard dataset", OtherDataSet, comment=""
            )
            self.groupbox4 = DataSetEditGroupBox(
                "Standard dataset", ExampleMultiGroupDataSet, comment=""
            )
            self.groupbox3.SIG_APPLY_BUTTON_CLICKED.connect(self.update_window)
            self.update_groupboxes()

            splitter = QSplitter(self)
            splitter.addWidget(self.groupbox1)
            splitter.addWidget(self.groupbox2)
            splitter.addWidget(self.groupbox3)
            splitter.addWidget(self.groupbox4)
            self.setCentralWidget(splitter)
            self.setContentsMargins(10, 5, 10, 5)

            # File menu
            file_menu = self.menuBar().addMenu("File")
            quit_action = create_action(
                self,
                "Quit",
                shortcut="Ctrl+Q",
                icon=get_std_icon("DialogCloseButton"),
                tip="Quit application",
                triggered=self.close,
            )
            add_actions(file_menu, (quit_action,))

            # Edit menu
            edit_menu = self.menuBar().addMenu("Edit")
            editparam1_action = create_action(
                self, "Edit dataset 1", triggered=self.edit_dataset1
            )
            editparam2_action = create_action(
                self, "Edit dataset 2", triggered=self.edit_dataset2
            )
            editparam4_action = create_action(
                self, "Edit dataset 4", triggered=self.edit_dataset4
            )
            ro_param4_action = create_action(
                self, "Dataset 4: read-only", toggled=self.ro_dataset4
            )
            add_actions(
                edit_menu,
                (editparam1_action, editparam2_action, editparam4_action, ro_param4_action),
            )

            # ? menu
            help_menu = self.menuBar().addMenu("?")
            about_action = create_action(
                self,
                "About guidata",
                icon=get_std_icon("MessageBoxInformation"),
                triggered=about.show_about_dialog,
            )
            add_actions(help_menu, (about_action,))

        def update_window(self):
            """Update window"""
            dataset = self.groupbox3.dataset
            self.setWindowTitle(dataset.title)
            self.setWindowIcon(get_icon(dataset.icon))
            self.setWindowOpacity(dataset.opacity)

        def update_groupboxes(self):
            """Update groupboxes"""
            self.groupbox1.dataset.set_activable(False)  # This is an activable dataset
            self.groupbox1.get()
            self.groupbox2.get()
            self.groupbox4.get()

        def ro_dataset4(self, readonly: bool):
            self.groupbox4.dataset.set_readonly(readonly)
            self.groupbox4.get()

        def edit_dataset1(self):
            """Edit dataset 1"""
            self.groupbox1.dataset.set_activable(True)  # This is an activable dataset
            if self.groupbox1.dataset.edit(self):
                self.update_groupboxes()

        def edit_dataset2(self):
            """Edit dataset 2"""
            if self.groupbox2.dataset.edit(self):
                self.update_groupboxes()

        def edit_dataset4(self):
            """Edit dataset 4"""
            if self.groupbox4.dataset.edit(self):
                self.update_groupboxes()

    def test_editgroupbox():
        """Test editgroupbox"""
        with qt_app_context(exec_loop=True):
            window = MainWindow()
            window.show()
            execenv.print("OK")

    if __name__ == "__main__":
        test_editgroupbox()

### Data item groups and group selection

    import guidata.dataset as gds
    from guidata.env import execenv
    from guidata.qthelpers import qt_app_context

    prop1 = gds.ValueProp(False)
    prop2 = gds.ValueProp(False)

    class Parameters(gds.DataSet):
        """
        Group selection test
        <b>Group selection example:</b>
        """

        g1 = gds.BeginGroup("group 1")
        enable1 = gds.BoolItem(
            "Enable parameter set #1",
            help="If disabled, the following parameters will be ignored",
            default=False,
        ).set_prop("display", store=prop1)
        prm11 = gds.FloatItem("Prm 1.1", default=0, min=0).set_prop("display", active=prop1)
        prm12 = gds.FloatItem("Prm 1.2", default=0.93).set_prop("display", active=prop1)
        _g1 = gds.EndGroup("group 1")
        g2 = gds.BeginGroup("group 2")
        enable2 = gds.BoolItem(
            "Enable parameter set #2",
            help="If disabled, the following parameters will be ignored",
            default=True,
        ).set_prop("display", store=prop2)
        prm21 = gds.FloatItem("Prm 2.1", default=0, min=0).set_prop("display", active=prop2)
        prm22 = gds.FloatItem("Prm 2.2", default=0.93).set_prop("display", active=prop2)
        _g2 = gds.EndGroup("group 2")

    def test_bool_selector():
        """Test bool selector"""
        with qt_app_context():
            prm = Parameters()
            prm.edit()
            execenv.print(prm)
            execenv.print("OK")

    if __name__ == "__main__":
        test_bool_selector()

### Activable data sets

    class Parameters(gds.ActivableDataSet):
        """
        Example
        <b>Activable dataset example</b>
        """

        def __init__(self, title=None, comment=None, icon=""):
            gds.ActivableDataSet.__init__(self, title, comment, icon)

        enable = gds.BoolItem(
            "Enable parameter set",
            help="If disabled, the following parameters will be ignored",
            default=False,
        )
        param0 = gds.ChoiceItem("Param 0", ["choice #1", "choice #2", "choice #3"])
        param1 = gds.FloatItem("Param 1", default=0, min=0)
        param2 = gds.FloatItem("Param 2", default=0.93)
        color = gds.ColorItem("Color", default="red")

    Parameters.active_setup()

    def test_activable_dataset():
        """Test activable dataset"""
        with qt_app_context():
            prm = Parameters()
            prm.set_activable(True)
            prm.edit()
            prm.set_activable(False)
            prm.edit()
            prm.set_readonly()
            prm.edit()
            prm.set_readonly(False)
            prm.edit()
            execenv.print("OK")

    if __name__ == "__main__":
        test_activable_dataset()

### Data set groups

    from guidata.dataset import DataSetGroup
    from guidata.env import execenv
    from guidata.qthelpers import qt_app_context
    from guidata.tests.dataset.test_all_features import Parameters

    def test_dataset_group():
        """Test DataSetGroup"""
        with qt_app_context():
            e1 = Parameters("DataSet #1")
            e2 = Parameters("DataSet #2")

            g = DataSetGroup([e1, e2], title="Parameters group")
            g.edit()
            execenv.print(e1)
            g.edit()
            execenv.print("OK")

            g = DataSetGroup([e1, e2], title="Parameters group in table mode")
            g.edit(mode="table")
            execenv.print(e1)
            g.edit()
            execenv.print("OK")

            g.edit()
            execenv.print(e1)
            g.edit()
            execenv.print("OK")

    if __name__ == "__main__":
        test_dataset_group()

### Utilities

#### Update/restore a dataset from/to a dictionary

    import numpy as np

    from guidata.dataset.conv import restore_dataset, update_dataset
    from guidata.tests.dataset.test_all_items import Parameters

    def test_update_restore_dataset():
        """Test update/restore dataset from/to another dataset or dictionary"""
        dataset = Parameters()
        dsdict = {
            "integer": 1,
            "float_slider": 1.0,
            "bool1": True,
            "string": "test",
            "floatarray": np.array([1, 2, 3]),
            "dictionary": {"a": 1, "b": 2},
        }
        # Update dataset from dictionary
        update_dataset(dataset, dsdict)
        # Check dataset values
        assert dataset.integer == dsdict["integer"]
        assert dataset.float_slider == dsdict["float_slider"]
        assert dataset.bool1 == dsdict["bool1"]
        assert dataset.string == dsdict["string"]
        assert np.all(dataset.floatarray == dsdict["floatarray"])
        assert dataset.dictionary == dsdict["dictionary"]
        # Update dataset from another dataset
        dataset2 = Parameters()
        update_dataset(dataset2, dataset)
        # Check dataset values
        assert dataset2.integer == dataset.integer
        assert dataset2.float_slider == dataset.float_slider
        assert dataset2.bool1 == dataset.bool1
        assert dataset2.string == dataset.string
        assert np.all(dataset2.floatarray == dataset.floatarray)
        assert dataset2.dictionary == dataset.dictionary
        # Restore dataset from dictionary
        restore_dataset(dataset, dsdict)
        # Check dataset values
        assert dataset.integer == dsdict["integer"]
        assert dataset.float_slider == dsdict["float_slider"]
        assert dataset.bool1 == dsdict["bool1"]
        assert dataset.string == dsdict["string"]
        assert np.all(dataset.floatarray == dsdict["floatarray"])
        assert dataset.dictionary == dsdict["dictionary"]

    if __name__ == "__main__":
        test_update_restore_dataset()

#### Create a dataset class from a function signature

    from __future__ import annotations

    import numpy as np

    from guidata.dataset import create_dataset_from_func
    from guidata.env import execenv

    def func_ok(
        a: int, b: float, c: str = "test", d: dict[str, int] = {"x": 1, "y": 3}
    ) -> None:
        """A function with type annotations"""
        pass

    def func_no_type(a, b, c="test"):
        """A function without type annotations"""
        pass

    def func_no_default(a: int, b: float, c: str, data: np.ndarray) -> None:
        """A function without default values"""
        pass

    def test_dataset_from_func():
        """Test generate dataset class from function"""
        for func in (func_ok, func_no_default):
            execenv.print(func.__name__)
            dataset = create_dataset_from_func(func)
            execenv.print(dataset)
            execenv.print(dataset.create(a=1, b=2.0))
            execenv.print("")
        func = func_no_type
        try:
            create_dataset_from_func(func)
            assert False, "Should have raised a ValueError"
        except ValueError:
            # This is expected
            pass

    if __name__ == "__main__":
        test_dataset_from_func()

#### Create a dataset class from a function dictionary

    import numpy as np

    from guidata.dataset import create_dataset_from_dict
    from guidata.env import execenv

    TEST_DICT1 = {
        "a": 1,
        "b": 2.0,
        "c": "test",
        "d": {"x": 1, "y": 3},
        "data": np.array([1, 2, 3]),
    }
    TEST_DICT2 = {
        "a": 1,
        "unsupported": [2.0, 3.0],
    }

    def test_dataset_from_dict():
        """Test generate dataset class from a dictionary"""
        for dictionary in (TEST_DICT1,):
            execenv.print(dictionary)
            dataset = create_dataset_from_dict(dictionary)
            execenv.print(dataset)
            execenv.print(dataset.create())
            execenv.print("")
        try:
            create_dataset_from_dict(TEST_DICT2)
            assert False, "Should have raised a ValueError"
        except ValueError:
            # This is expected
            pass

    if __name__ == "__main__":
        test_dataset_from_dict()

### Data set HDF5 serialization/deserialization

    import os.path as osp
    import tempfile

    from guidata.dataset import assert_datasets_equal
    from guidata.env import execenv
    from guidata.io import HDF5Reader, HDF5Writer
    from guidata.qthelpers import qt_app_context
    from guidata.tests.dataset.test_all_items import Parameters

    def test_loadsave_hdf5():
        """Test HDF5 I/O"""
        with tempfile.TemporaryDirectory() as temp_dir:
            fname = osp.join(temp_dir, "test.h5")
            with qt_app_context():
                p1 = Parameters()
                # p1.edit()

                # Save to HDF5 file
                writer = HDF5Writer(fname)
                p1.serialize(writer)
                writer.close()

                p2 = Parameters()
                # Set all items to None for testing purposes:
                for item in p2._items:
                    item.__set__(p2, None)

                # Load from HDF5 file
                reader = HDF5Reader(fname)
                p2.deserialize(reader)
                reader.close()

                assert_datasets_equal(p1, p2, "Parameters do not match after HDF5 I/O")
                execenv.print("OK")

    if __name__ == "__main__":
        test_loadsave_hdf5()

### Data set JSON serialization/deserialization

    import os

    from guidata.env import execenv
    from guidata.io import JSONReader, JSONWriter
    from guidata.qthelpers import qt_app_context
    from guidata.tests.dataset.test_all_items import Parameters

    def test_loadsave_json():
        """Test JSON I/O"""
        fname = "test.json"
        with qt_app_context():
            if os.path.exists(fname):
                os.unlink(fname)

            p1 = Parameters()
            if execenv.unattended or p1.edit():
                writer = JSONWriter(fname)
                p1.serialize(writer)
                writer.save()

                p2 = Parameters()
                reader = JSONReader(fname)
                p2.deserialize(reader)
                reader.close()
                p2.edit()
                os.unlink(fname)

            # TODO: Uncomment this part of the test, and make it work!
            # if execenv.unattended:
            #     assert_datasets_equal(p1, p2, "Parameters do not match after HDF5 I/O")

            execenv.print("OK")

    if __name__ == "__main__":
        test_loadsave_json()