.. |ico3| image:: /images/rightarrow.jpeg :height: 2ex **GUI API Tutorials** ===================== Four short tutorials build up everything the GUI API offers, from a single question to a dockable tool. Each one is a complete script: download it, open it in the Python Script editor (Main Menu |ico3| Edit |ico3| Python Script) or drag and drop the file onto CLO, and run it. At the end you will find complete workflow tools built the same way. If you have not read it yet, :doc:`gui_api` explains handles, layout, callbacks and values in a few minutes. Every function is listed in the :doc:`gui_api_reference`. .. contents:: On this page :local: :depth: 1 | 1. Ask the user: modal dialogs ------------------------------ :download:`Download 01_dialogs.py ` A modal dialog opens, waits for the user and returns the answer, so the script can use it on the next line. Use dialogs for a quick confirmation or a few settings before a script does its work. **Yes or no.** ``AskConfirm`` returns ``True`` only when the accept button is pressed. .. code-block:: python if not gui_api.AskConfirm("Dialog tour", "Walk through the five gui_api dialogs?", "Start", "Cancel"): raise SystemExit("cancelled") .. figure:: /images/gui_api/dialog_confirm.png :align: center **A line of text.** ``AskText`` returns a result with ``accepted`` and ``value``. Always check ``accepted``: the user may press Cancel or close the dialog. .. code-block:: python name = gui_api.AskText("Dialog tour", "Style name", "Spring tee", "type a name") if name.accepted: print(name.value) .. figure:: /images/gui_api/dialog_text.png :align: center **One option or several.** ``AskChoice`` returns the picked index and caption, ``AskMultiChoice`` returns every checked index in ``selectedIndices``. .. code-block:: python size = gui_api.AskChoice("Dialog tour", "Base size", ["XS", "S", "M", "L", "XL"], 2) outputs = ["3D thumbnail", "2D thumbnail", "OBJ", "BOM"] picked = gui_api.AskMultiChoice("Dialog tour", "Outputs to create", outputs, [0, 1]) print(size.selectedValue, [outputs[i] for i in picked.selectedIndices]) .. grid:: 1 1 2 2 :gutter: 2 .. grid-item:: .. figure:: /images/gui_api/dialog_choice.png :align: center .. grid-item:: .. figure:: /images/gui_api/dialog_multichoice.png :align: center **A small form.** ``AskForm`` builds one row per ``ApiTypes.CloApiGuiFormField``. Each field has a unique ``key``; the result maps every key to its value as text (``"true"`` / ``"false"`` for check boxes, decimal text for numbers). .. code-block:: python def field(key, label, kind, default="", options=None): f = ApiTypes.CloApiGuiFormField() f.key, f.label, f.type, f.defaultValue = key, label, kind, default f.options = options or [] return f form = gui_api.AskForm("Dialog tour - style details", [ field("style", "Style number", ApiTypes.GUI_FIELD_TEXT, "ST-2401"), field("season", "Season", ApiTypes.GUI_FIELD_CHOICE, "FW", ["SS", "FW", "Resort"]), field("lined", "Lined", ApiTypes.GUI_FIELD_CHECK, "false"), field("price", "Target price", ApiTypes.GUI_FIELD_NUMBER, "29.9"), ]) if form.accepted: values = dict(form.values) # {"style": "ST-2401", "season": "FW", "lined": "false", "price": "29.9"} .. figure:: /images/gui_api/dialog_form.png :align: center .. dropdown:: Full script: 01_dialogs.py .. literalinclude:: samples/gui_api/01_dialogs.py :language: python | 2. A tool window with widgets and callbacks ------------------------------------------- :download:`Download 02_tool_window.py ` A tool window is the right choice for a tool the user keeps open while working. This tutorial builds a *Quick Export* window that saves captures of the 3D and 2D windows with a few options. .. figure:: /images/gui_api/tool_window.png :align: center **Step 1: create the window.** ``CreateToolWindow`` returns the window handle. Close any window from a previous run first. .. code-block:: python gui_api.CloseAllWindows() window = gui_api.CreateToolWindow("Quick Export", 460, 560) **Step 2: add a menu bar.** ``AddMenu`` creates the menu bar on first use; ``AddMenuItem`` takes a callable. .. code-block:: python file_menu = gui_api.AddMenu(window, "File") gui_api.AddMenuItem(file_menu, "Open output folder", lambda: open_folder(OUTPUT)) gui_api.AddMenuItem(file_menu, "Close", lambda: gui_api.CloseWindow(window)) **Step 3: lay out the inputs.** A group collects related settings, a row puts a label next to its drop-down. Keep every handle you need later in a dictionary. .. code-block:: python ui = {} settings = gui_api.AddGroup(window, "Settings") ui["name"] = gui_api.AddTextInput(settings, utility_api.GetProjectName() or "untitled", "file name") row = gui_api.AddRow(settings) gui_api.AddLabel(row, "View") ui["view"] = gui_api.AddDropdown(row, ["3D window", "2D window", "Both"], 0) ui["open"] = gui_api.AddCheckBox(settings, "Open the folder when done", False) ui["count"] = gui_api.AddSlider(settings, 1, 1, 5, lambda v: gui_api.SetWidgetText(ui["count_label"], "%d file(s) per view" % v)) ui["count_label"] = gui_api.AddLabel(settings, "1 file(s) per view") The slider callback receives the new value and updates a label right away. **Step 4: read the values when the user acts.** The button callback reads every input with ``GetWidgetValue`` / ``GetWidgetSelectedIndex``, does the work and reports progress. .. code-block:: python def export(): base = gui_api.GetWidgetValue(ui["name"]).strip() or "untitled" view = gui_api.GetWidgetSelectedIndex(ui["view"]) # 0, 1 or 2 count = int(gui_api.GetWidgetValue(ui["count"])) # slider value as text ... gui_api.SetWidgetValue(ui["progress"], str(percent)) # progress bar 0..100 if gui_api.GetWidgetValue(ui["open"]) == "true": # check box open_folder(OUTPUT) actions = gui_api.AddRow(window) gui_api.AddButton(actions, "Export", export) **Step 5: show it.** ``ShowWindow`` displays the window. The script ends, the window stays. ``SetWindowCloseCallback`` lets you react when the user closes it. .. code-block:: python gui_api.SetWindowCloseCallback(window, lambda: print("Quick Export closed")) gui_api.ShowWindow(window) .. dropdown:: Full script: 02_tool_window.py .. literalinclude:: samples/gui_api/02_tool_window.py :language: python | 3. A dock panel next to the Library ----------------------------------- :download:`Download 03_dock_panel.py ` A dock panel holds the same widgets as a tool window but lives inside the CLO main window. The user can drag it to another side, float it, or fold it into the side bar with the button in its title bar, exactly like the built-in panels. .. figure:: /images/gui_api/dock_panel.gif :align: center :width: 100% This tutorial builds a *Pattern Browser*: a table of every pattern piece; selecting a row selects the piece in CLO and lets the user rename it. .. figure:: /images/gui_api/dock_panel.png :align: center **Step 1: create the panel.** Give it a side (``left``, ``right`` or ``bottom``) and a stable ``name``. With the same name, the panel returns to the place the user moved it to, in later runs and sessions. .. code-block:: python panel = gui_api.CreateDockPanel("Pattern Browser", "right", "pattern_browser") **Step 2: a table with a selection callback.** ``AddTable`` takes the column captions and the rows; the callback receives the selected row. Here it selects the pattern in the 2D and 3D windows. .. code-block:: python def on_select(row): pattern_api.SelectPatternViaIndex(row, False) gui_api.SetWidgetValue(ui["new_name"], pattern_api.GetPatternPieceName(row)) gui_api.SetWidgetEnabled(ui["rename_row"], True) ui["table"] = gui_api.AddTable(panel, ["#", "Pattern", "Area (cm2)"], [], on_select) **Step 3: fill and refresh.** ``SetTableRows`` replaces every row. Call it whenever the scene may have changed. .. code-block:: python def refresh(): rows = [[str(i), pattern_api.GetPatternPieceName(i), "%.0f" % (pattern_api.GetPatternPieceArea(i) / 100.0)] for i in range(pattern_api.GetPatternCount())] gui_api.SetTableRows(ui["table"], rows) **Step 4: enable controls only when they make sense.** ``SetWidgetEnabled`` works on any widget and on whole rows and groups. The rename row is greyed out until a pattern is selected. .. code-block:: python ui["rename_row"] = gui_api.AddRow(panel) ui["new_name"] = gui_api.AddTextInput(ui["rename_row"], "", "new name") gui_api.AddButton(ui["rename_row"], "Rename", rename) gui_api.SetWidgetEnabled(ui["rename_row"], False) .. dropdown:: Full script: 03_dock_panel.py .. literalinclude:: samples/gui_api/03_dock_panel.py :language: python | 4. Live previews of the 3D and 2D windows ----------------------------------------- :download:`Download 04_view_capture.py ` ``export_api.ExportThumbnail3D`` and ``export_api.ExportThumbnail2D`` save what the 3D and 2D windows show right now. ``AddImage`` displays a PNG or JPEG file inside a window, scaled to the size you give, and ``SetWidgetValue`` with a new path replaces the picture in place. .. figure:: /images/gui_api/view_capture.png :align: center .. code-block:: python shown = {} def show(key, group, path): if key in shown: gui_api.SetWidgetValue(shown[key], path) # replace the picture else: shown[key] = gui_api.AddImage(group, path, 340, 380) gui_api.AddButton(buttons, "Capture 3D", lambda: show("3D", group_3d, export_api.ExportThumbnail3D(PATH_3D))) gui_api.AddButton(buttons, "Capture 2D", lambda: show("2D", group_2d, export_api.ExportThumbnail2D(PATH_2D))) The same pattern works for any image your script produces: a texture, a chart, a render. .. dropdown:: Full script: 04_view_capture.py .. literalinclude:: samples/gui_api/04_view_capture.py :language: python | .. _gui_api_workflow_samples: Workflow samples ---------------- These complete tools combine the GUI API with the rest of the CLO API. Use them as they are, or as a starting point for your own tools. Colorway Lab ~~~~~~~~~~~~ :download:`Download colorway_lab.py ` Turns a mood board image, a base color with a harmony rule, or a curated palette into up to six colorway options of the open garment. Every option is rendered and shown with its palette; keep the ones you like, and the panel adds them to the project as real colorways and writes a lookbook image. .. figure:: /images/gui_api/colorway_lab.gif :align: center :width: 100% Project Doctor ~~~~~~~~~~~~~~ :download:`Download project_doctor.py ` Checks a project before it goes to a vendor or into a tech pack: duplicate or meaningless pattern names, pieces that are not sewn, fabrics without an item number, library entries nobody uses, oversized textures and default names. Shows a health score, selects the piece behind a finding, fixes the safe findings, renames pieces in bulk with a preview and writes a CSV and Markdown report. .. figure:: /images/gui_api/project_doctor.png :align: center BOM Workflow ~~~~~~~~~~~~ :download:`Download bom_workflow.py ` Loads a project, lists every pattern with its fabric, size and grain, and summarises the fabrics with piece count, total area and an estimated length. Exports the summary as CSV or the full BOM package. .. figure:: /images/gui_api/bom_workflow.png :align: center Batch Automation ~~~~~~~~~~~~~~~~ :download:`Download batch_automation.py ` Runs a pipeline over a folder of projects: simulate, 3D and 2D thumbnails, OBJ, GLB, POM, BOM and a project copy, with a result table per project and a CSV run log. .. figure:: /images/gui_api/batch_automation.png :align: center .. note:: The batch runs inside the button callback, so CLO is busy until the last project is done. | Tips and troubleshooting ------------------------ - **Nothing appears.** Check ``gui_api.IsGuiAvailable()``. Windows need ``ShowWindow``; a handle of ``0`` means the parent handle was not valid. - **Two windows after a second run.** Call ``gui_api.CloseAllWindows()`` at the top of the script. - **A value looks wrong.** Values are text: compare a check box with ``"true"``, convert numbers with ``float()`` / ``int()``. - **A callback does nothing.** It may have raised an exception; CLO logs it and keeps the window open. Wrap the work in ``try`` / ``except`` and report the error in a label while developing. - **The window freezes during a long job.** The callback is still running. Split the work, or show a progress bar and a status message before starting. - **Saving the user's settings.** Write them to a JSON file when they change and read it back when the script starts, as the Batch Automation sample does.