Ready-to-use widgets

As a complement to the guidata.dataset module which focus on automatic GUI generation for data sets editing and display, the guidata.widgets module provides a set of ready-to-use widgets for building interactive GUIs.

Note

Most of the widgets originally come from the Spyder project (Copyright © Spyder Project Contributors, MIT-licensed). They were adapted to be used outside of the Spyder IDE and to be compatible with guidata internals.

Python console


from guidata.configtools import get_icon
from guidata.env import execenv
from guidata.qthelpers import qt_app_context
from guidata.widgets.console import Console


def test_console():
    """Test Console widget."""
    with qt_app_context(exec_loop=True):
        widget = Console(debug=False, multithreaded=True)
        widget.resize(800, 600)
        widget.setWindowTitle("Console")
        widget.setWindowIcon(get_icon("guidata.svg"))
        widget.show()
        execenv.print("OK")


if __name__ == "__main__":
    test_console()
_images/console.png

Code editor


from guidata.configtools import get_icon
from guidata.env import execenv
from guidata.qthelpers import qt_app_context
from guidata.widgets import codeeditor


def test_codeeditor():
    """Test Code editor."""
    with qt_app_context(exec_loop=True):
        widget = codeeditor.CodeEditor(language="python")
        widget.set_text_from_file(codeeditor.__file__)
        widget.resize(800, 600)
        widget.setWindowTitle("Code editor")
        widget.setWindowIcon(get_icon("guidata.svg"))
        widget.show()
        execenv.print("OK")


if __name__ == "__main__":
    test_codeeditor()
_images/codeeditor.png

Array editor


import numpy as np

from guidata.env import execenv
from guidata.qthelpers import exec_dialog, qt_app_context
from guidata.widgets.arrayeditor import ArrayEditor


def launch_arrayeditor(data, title="", xlabels=None, ylabels=None, variable_size=False):
    """Helper routine to launch an arrayeditor and return its result"""
    dlg = ArrayEditor()
    dlg.setup_and_check(
        data, title, xlabels=xlabels, ylabels=ylabels, variable_size=variable_size
    )
    exec_dialog(dlg)
    return dlg.get_value()


def test_arrayeditor():
    """Test array editor for all supported data types"""
    with qt_app_context():
        # Variable size version
        for title, data in (
            ("string array", np.array(["kjrekrjkejr"])),
            (
                "masked array",
                np.ma.array([[1, 0], [1, 0]], mask=[[True, False], [False, False]]),
            ),
            ("int array", np.array([1, 2, 3], dtype="int8")),
        ):
            launch_arrayeditor(data, "[Variable size] " + title, variable_size=True)
        for title, data in (
            ("string array", np.array(["kjrekrjkejr"])),
            ("unicode array", np.array(["ñññéáíó"])),
            (
                "masked array",
                np.ma.array([[1, 0], [1, 0]], mask=[[True, False], [False, False]]),
            ),
            (
                "record array",
                np.zeros(
                    (2, 2),
                    {
                        "names": ("red", "green", "blue"),
                        "formats": (np.float32, np.float32, np.float32),
                    },
                ),
            ),
            (
                "record array with titles",
                np.array(
                    [(0, 0.0), (0, 0.0), (0, 0.0)],
                    dtype=[(("title 1", "x"), "|i1"), (("title 2", "y"), ">f4")],
                ),
            ),
            ("bool array", np.array([True, False, True])),
            ("int array", np.array([1, 2, 3], dtype="int8")),
            ("float16 array", np.zeros((5, 5), dtype=np.float16)),
        ):
            launch_arrayeditor(data, title)
        for title, data, xlabels, ylabels in (
            ("float array", np.random.rand(5, 5), ["a", "b", "c", "d", "e"], None),
            (
                "complex array",
                np.round(np.random.rand(5, 5) * 10)
                + np.round(np.random.rand(5, 5) * 10) * 1j,
                np.linspace(-12, 12, 5),
                np.linspace(-12, 12, 5),
            ),
        ):
            launch_arrayeditor(data, title, xlabels, ylabels)

        arr = np.zeros((3, 3, 4))
        arr[0, 0, 0] = 1
        arr[0, 0, 1] = 2
        arr[0, 0, 2] = 3
        launch_arrayeditor(arr, "3D array")

        execenv.print("OK")


if __name__ == "__main__":
    test_arrayeditor()
_images/arrayeditor.png

Collection editor


import datetime

import numpy as np

from guidata.env import execenv
from guidata.qthelpers import qt_app_context
from guidata.widgets.collectionseditor import CollectionsEditor

try:
    from PIL import Image as PILImage
except ImportError:
    # PIL is not installed
    PILImage = None


def get_test_data():
    """Create test data."""
    testdict = {"d": 1, "a": np.random.rand(10, 10), "b": [1, 2]}
    testdate = datetime.date(1945, 5, 8)
    test_timedelta = datetime.timedelta(days=-1, minutes=42, seconds=13)

    try:
        import pandas as pd
    except (ModuleNotFoundError, ImportError):
        test_timestamp = None
        test_pd_td = None
        test_dtindex = None
        test_series = None
        test_df = None
    else:
        test_timestamp = pd.Timestamp("1945-05-08T23:01:00.12345")
        test_pd_td = pd.Timedelta(days=2193, hours=12)
        test_dtindex = pd.date_range(start="1939-09-01T", end="1939-10-06", freq="12h")
        test_series = pd.Series({"series_name": [0, 1, 2, 3, 4, 5]})
        test_df = pd.DataFrame(
            {
                "string_col": ["a", "b", "c", "d"],
                "int_col": [0, 1, 2, 3],
                "float_col": [1.1, 2.2, 3.3, 4.4],
                "bool_col": [True, False, False, True],
            }
        )

    class Foobar:
        """ """

        def __init__(self):
            self.text = "toto"
            self.testdict = testdict
            self.testdate = testdate

    foobar = Foobar()
    test_data = {
        "object": foobar,
        "module": np,
        "str": "kjkj kj k j j kj k jkj",
        "unicode": "éù",
        "list": [1, 3, [sorted, 5, 6], "kjkj", None],
        "tuple": ([1, testdate, testdict, test_timedelta], "kjkj", None),
        "dict": testdict,
        "float": 1.2233,
        "int": 223,
        "bool": True,
        "array": np.random.rand(10, 10).astype(np.int64),
        "masked_array": np.ma.array(
            [[1, 0], [1, 0]], mask=[[True, False], [False, False]]
        ),
        "1D-array": np.linspace(-10, 10).astype(np.float16),
        "3D-array": np.random.randint(2, size=(5, 5, 5)).astype(np.bool_),
        "empty_array": np.array([]),
        "date": testdate,
        "datetime": datetime.datetime(1945, 5, 8, 23, 1, 0, int(1.5e5)),
        "timedelta": test_timedelta,
        "complex": 2 + 1j,
        "complex64": np.complex64(2 + 1j),
        "complex128": np.complex128(9j),
        "int8_scalar": np.int8(8),
        "int16_scalar": np.int16(16),
        "int32_scalar": np.int32(32),
        "int64_scalar": np.int64(64),
        "float16_scalar": np.float16(16),
        "float32_scalar": np.float32(32),
        "float64_scalar": np.float64(64),
        "bool_scalar": bool,
        "bool__scalar": np.bool_(8),
        "timestamp": test_timestamp,
        "timedelta_pd": test_pd_td,
        "datetimeindex": test_dtindex,
        "series": test_series,
        "ddataframe": test_df,
        "None": None,
        "unsupported1": np.arccos,
        "unsupported2": np.cast,
        # Test for Issue #3518
        "big_struct_array": np.zeros(
            1000, dtype=[("ID", "f8"), ("param1", "f8", 5000)]
        ),
    }
    if PILImage is not None:
        image = PILImage.fromarray(np.random.randint(256, size=(100, 100)), mode="P")
        test_data["image"] = image
    return test_data


def test_collectionseditor():
    """Test Collections editor."""
    with qt_app_context(exec_loop=True):
        dialog = CollectionsEditor()
        dialog.setup(get_test_data())
        dialog.show()
        execenv.print("OK")


if __name__ == "__main__":
    test_collectionseditor()
_images/collectioneditor.png

Dataframe editor


import numpy as np
import pytest

try:
    import pandas as pd
    import pandas.testing as pdt

    from guidata.widgets.dataframeeditor import DataFrameEditor
except ImportError:
    # pandas is not installed
    pd = pdt = DataFrameEditor = None

from guidata.env import execenv
from guidata.qthelpers import exec_dialog, qt_app_context


@pytest.mark.skipif(pd is None, reason="pandas is not installed")
def test_dataframeeditor():
    """DataFrame editor test"""

    def test_edit(data, title="", parent=None):
        """Test subroutine"""
        dlg = DataFrameEditor(parent=parent)

        if dlg.setup_and_check(data, title=title):
            exec_dialog(dlg)
            return dlg.get_value()
        else:
            import sys

            sys.exit(1)

    with qt_app_context():
        df1 = pd.DataFrame(
            [
                [True, "bool"],
                [1 + 1j, "complex"],
                ["test", "string"],
                [1.11, "float"],
                [1, "int"],
                [np.random.rand(3, 3), "Unkown type"],
                ["Large value", 100],
                ["áéí", "unicode"],
            ],
            index=["a", "b", np.nan, np.nan, np.nan, "c", "Test global max", "d"],
            columns=[np.nan, "Type"],
        )
        out = test_edit(df1)
        pdt.assert_frame_equal(df1, out)

        result = pd.Series([True, "bool"], index=[np.nan, "Type"], name="a")
        out = test_edit(df1.iloc[0])
        pdt.assert_series_equal(result, out)

        df1 = pd.DataFrame(np.random.rand(100100, 10))
        out = test_edit(df1)
        pdt.assert_frame_equal(out, df1)

        series = pd.Series(np.arange(10), name=0)
        out = test_edit(series)
        pdt.assert_series_equal(series, out)

        execenv.print("OK")


if __name__ == "__main__":
    test_dataframeeditor()
_images/dataframeeditor.png

Import wizard


import pytest

from guidata.env import execenv
from guidata.qthelpers import exec_dialog, qt_app_context
from guidata.widgets.importwizard import ImportWizard


@pytest.fixture()
def text():
    """Return text to test"""
    return "17/11/1976\t1.34\n14/05/09\t3.14"


def test_importwizard(text):
    """Test"""
    with qt_app_context():
        dialog = ImportWizard(None, text)
        if exec_dialog(dialog):
            execenv.print(dialog.get_data())
        execenv.print("OK")


if __name__ == "__main__":
    test_importwizard("17/11/1976\t1.34\n14/05/09\t3.14")
_images/importwizard.png