Skip to content

Latest commit

 

History

History
159 lines (124 loc) · 11.9 KB

user_guide.md

File metadata and controls

159 lines (124 loc) · 11.9 KB

pyironflow

The visual programming interface pyironflow is a gui skin based on ReactFlow that works on top of pyiron_workflow.

Table of Contents

  1. Installing pyironflow
  2. Launching pyironflow
    1. Node library
  3. Basic usage
    1. Viewing
    2. Running
    3. Modifying
    4. Saving/Loading
  4. Warnings
  5. For Developers
    1. Hints for GUI data input
    2. Installation
    3. Developing the JS

Installing-pyironflow

A package of pyironflow is available on PyPI and conda-forge. This can be installed using:

conda install -c conda-forge pyironflow   # recommended
pip install pyironflow                    # also available from PyPI

The pyironflow gui is intended to be used inside a Jupyter notebook, was additionally recommend installing jupyterlab:

conda install -c conda-forge jupyterlab

Launching pyironflow

It is recommended to use pyironflow in a jupyterlab or notebook instance launched from a terminal. Launching them from code editors such Visual Studio Code and PyCharm may cause unexpected behavior and rendering errors. To launch pyironflow, the following code should be executed in jupyter:

from pyironflow.pyironflow import PyironFlow
pf = PyironFlow()
pf.gui

It can also be launched with one or more pyiron_workflow.Workflow instances:

pf = PyironFlow([wf])

The widget consists of a set of control and information panels on the left, and a set of workflow graph tabs on the right. You can adjust the relative sizes of these portions by clicking and dragging the vertical bar dividing them.

Setting the Node library

The "Node Library" tab lets you add new nodes to your workflow. It automatically scrapes python files and finds anything that is marked as a node (e.g. decorated by a @flowrep.workflow or @flowrep.atomic decorator), or that might possibly be interpreted as one (e.g., any standard python function definition). The library always starts with two standard entries: std, flowrep's standard operators, and pyironflow_std, typed input nodes (input_int, input_float, input_str, input_bool) for typing a value in once and feeding it to several nodes. After those come your own roots, which default to the current working directory. Use library_roots to choose them: a directory or python file, an imported module, or a list of these, or None for the standard entries only:

import pyiron_nodes

pf = PyironFlow([wf], library_roots=['../some_other_directory', pyiron_nodes])

Each root appears as its own top-level entry, and each is added to your python path for the lifetime of the GUI (if it isn't part of your sys.path already). pyiron_nodes is no longer picked up automatically; pass it as above. The old root_path argument still works as an alias for library_roots, but is deprecated.

  • Click on orange folder or green file icons to expand the folder/file
  • flowrep-decorated atomic, dataclass, and workflow definitions are shown with red wireframe, green table, and blue process symbols, respectively
  • pyiron_workflow-decorated function (aka atomic) and macro (aka workflow) definitions are similarly shown in red wireframe or blue process symbols
  • All remaining class and function definitions are shown with a grey symbol -- these may or may not parse to nodes, but clicking on them will attempt to make an atomic node out of the definition
    • This allows you to immediately leverage many python packages that know nothing about pyiron workflows!
  • Click on any of these node items to add it to the workflow area

Note: The refresh button updates the nodes in the library reflecting any new nodes. However, nodes already in the workflow will not be automatically refreshed.

Basic usage

Viewing

  • Use the mouse wheel to zoom in and out.
  • Hold left-click in an empty area and move the mouse to pan.
  • Left-click on a node, hold and move the mouse to move a node around.
  • Click on "Reset Layout" in the bottom-right of the workflow viewport to automatically rearrange nodes.
  • Click on a node and press "Info" to open the "Node Info" panel on that node: its status square (as of when the panel was shown), its "Last Input" (what it ran on in the most recent run or pull, including any loaded run), its "Last Output", and its source code. While "Node Info" is open, it follows whichever single node is selected.
  • Hovering over the label of a node's port will display a tooltip with the data type of the port.

Running

  • Click on "Run" in the top of the workflow viewport to run all nodes in the workflow tab.
  • Click on a node and press "Pull" to execute the node and all upstream nodes that connect to it. The output of the whole pulled subgraph is shown in "Global Output", and the "Node Info" panel opens on this node's "Last Output".
  • Fields marked with an asterisk (*) require an input from the user in the form of GUI data input or an incoming graph edge.
  • The square box next to the name of the node shows what the latest run or pull did to it, and updates live while it runs:
    • White: not part of the latest run or pull (or nothing has run yet)
    • Yellow: running
    • Green: finished successfully
    • Red: failed
  • Every run or pull that has enough input to start first resets all nodes to white. Editing the graph afterwards leaves the colours as they were until the next run or pull.

Modifying

  • Open the "Node Library" and click on nodes to add them to the graph.
  • Set input data by interacting with text-editable fields, check-boxes, and drop-down menus (available where the input port has a JSONable type hint).
    • The keyword "None" is reserved for the value None (python NoneType). Entering this in a text field will always be parsed as None.
  • Control the flow of data by clicking and dragging from one node's output port to another node's input port to form a new data-flow edge.
  • Delete existing nodes or edges by clicking on them to select them, and then pressing "backspace" on the keyboard to delete.
  • Select two or more nodes (hold shift while clicking) and press "Group" to move them into a new subgraph node, named at the prompt. Every port of the grouped nodes that is not connected inside the group appears on the new node as <node>__<port>, keeping any values you typed or locked; an input with a default that you left untouched stays inside the group, still using its default.
  • Click on a subgraph node (a workflow or macro) and press "Ungroup" to move its children back into the workflow, named <subgraph>_<child>. A macro defined in python loses its link to that python definition, so its "Ungroup" asks for a second click; click anywhere else to cancel.

Saving/Loading

  • "Export" sends the active workflow's flowrep recipe to JSON.
  • "Import" opens a new workflow in a new tab based on a flowrep recipe loaded from JSON.
  • "Save" sends the last run pyiron_workflow.schemas.Run output to a file, either pickle bytes or a bagofholding hdf5 file
  • "Load" opens a new tab from a saved run, rebuilding the workflow from the run's recipe and keeping the run as that tab's last run. The format is inferred from the extension (.pckl or .h5) unless you choose one. A run of something other than a plain workflow arrives as the single node of a new workflow, whose last run then has the loaded run as its only step.
  • A workflow in the gui can be exported out within your jupyter notebook scope using: wf_gui = pf.get_workflow(). This new object behaves like a conventional pyiron_workflow object.

Warnings

  • Currently, if files in the node library are updated while the GUI is running, the kernel has to be restarted to use the new nodes listed when the "refresh" button is pressed
  • The "Node Library" will scrape all possible def and class declarations in order to let you scrape nodes from packages that know nothing about graph-based workflows, but these are not guaranteed to be parsable; un-parsable declarations will complain when you try to add them to the workflow.
  • Triggering a run with "Run" or "Pull" will monopolize the python process, but not the GUI; e.g. if you start a "Run", then use the GUI to delete a node, first the run will complete, then the deletion will be processed.

For Developers

Hints for GUI data input

Nodes hinted as flowrep.schemase.JSONABLE types get exposed as user-typable input right in the GUI, where

JSONABLE = typing.TypeAliasType(
    "JSONABLE",
    "dict[str, JSONABLE] | list[JSONABLE] | str | int | float | bool | None",
)

In addition to this, if you hint some Literal[{something jsonable}] | Literal[{something else jsonable}] | ..., you'll get a drop-down choice menu in the GUI.

Installation

  • Clone the repository to your file system
  • Install dependencies into your environment, e.g. from .ci_support/environment.yml
    • This includes the JS toolchain: Node.js >=24.21.0,<25 with npm 11. These ranges are declared in package.json under engines, and .npmrc sets engine-strict=true, so npm refuses to run with any other version
  • Run ./.dev-build.sh --clean to completely rebuild the JS bundle, reinstalling JS dependencies exactly as locked in package-lock.json
  • Launch a jupyter notebook and make sure the clone of pyironflow is the one in your sys.path, and use pyironflow as usual
  • For live JS development, run ./.dev-build.sh --watch and start Jupyter with ANYWIDGET_HMR=1 so rebuilt bundles hot-reload without a kernel restart. This requires watchfiles (included in the dev extra: pip install -e ".[dev]")

Developing the JS

How the JS bundle is built

  • pyironflow/static/{{widget/splitter}.{js/css}} are a build artefacts and are not tracked by git.
  • The hatch build hook in hatch_build.py runs npm ci && npm run build whenever a Python build (pip install ., pip install -e ., hatchling build) finds no existing bundle. npm ci installs exactly what is in package-lock.json and fails if it disagrees with package.json.
  • If a bundle already exists, the hook keeps it and does not rebuild. A local pip install . will therefore ship whatever is in pyironflow/static/, stale or not; run ./.dev-build.sh (or --clean) first if you have changed JS sources or dependencies.
  • CI and releases start from a clean checkout, so they always build from the lockfile. The sdist ships the built bundle, so downstream builds from the sdist (e.g. conda-forge) reuse it rather than rebuilding.

Updating JS dependencies

  • ./.dev-build.sh --update upgrades packages within the ranges in package.json and rewrites package-lock.json.
  • For major-version bumps, edit the ranges in package.json (or run npx npm-check-updates -u), then run ./.dev-build.sh.
  • The plain ./.dev-build.sh uses npm install, which may also rewrite package-lock.json if package.json changed; --clean uses npm ci and never touches the lockfile.
  • Test, then commit package.json and package-lock.json together. CI and releases install exactly what is in the lockfile, so never delete it to "get the latest"; use --update instead.
  • To change the Node.js version, update engines in package.json and the nodejs pins in .ci_support/*.yml together; a mismatch fails every build.