drawlib.diagrams.state

Finite State Machine (FSM) and UML 2.x statechart diagrams featuring entry/do/exit compartments, initial/final/choice/fork-join pseudo-states, self-loops, and curved guard transitions. Canonical import: from drawlib.diagrams.state import ChoiceState, FinalState, InitialState, State, StateDiagram.

24_diagrams_state_1
Connection State Machine with Internal Compartments, Choice Diamond, and Curved Retry Arc
Source Code
from drawlib.canvas import save, setup
from drawlib.diagrams.state import ChoiceState, FinalState, InitialState, State, StateDiagram
from drawlib.styles import Colors, Styles

setup(width=120, height=54)

sd = StateDiagram(
    node_style=Styles.Neutral.patch(text_size=10.5),
    edge_style=Styles.DarkBold,
    edge_text_style=Styles.Dark.patch(text_size=10.0),
    margin=4.0,
)

init = sd.add(InitialState(), xy=(6.0, 34.0))
idle = sd.add(State("Idle", width=17, height=11), xy=(21.0, 34.0))
connecting = sd.add(
    State(
        "Connecting",
        entry="openSocket()",
        do="handshake()",
        width=29,
        style=Styles.PrimaryNeutral.patch(text_size=10.5),
    ),
    xy=(54.0, 34.0),
)
choice = sd.add(ChoiceState(style=Styles.SecondaryNeutral), xy=(80.0, 34.0))
connected = sd.add(
    State(
        "Connected",
        width=21,
        height=11,
        style=Styles.PrimaryFlat.patch(text_color=Colors.White, text_size=10.5),
    ),
    xy=(103.5, 34.0),
)
final = sd.add(FinalState(), xy=(103.5, 11.0))

sd.connect(init, idle, routing="direct", padding=0.8)
sd.connect(idle, connecting, event="connect", start_side="right", end_side="left", routing="direct", padding=0.8)
sd.connect(connecting, choice, event="done", start_side="right", end_side="left", routing="direct", padding=0.8)
sd.connect(choice, connected, guard="ok", start_side="right", end_side="left", routing="direct", padding=0.8)
sd.connect(choice, idle, guard="retry", start_side="bottom", end_side="bottom", bend=-0.26, padding=0.8)
sd.connect(
    connected,
    final,
    event="close",
    start_side="bottom",
    end_side="top",
    routing="direct",
    padding=0.8,
    text_style=Styles.Dark.patch(text_size=10.0, halign="left"),
)

sd.draw(xy=(60.0, 27.0), align="center")
save()

classChoiceState#

UML 2.x dynamic conditional branch pseudo-state rendered as a diamond. Subclass of StateNodeBase.

ChoiceState(
    name: str = "",
    size: float = 4.5,
    style: Style | None = None,
    show: bool = True,
) -> None
  • Coordinate Anchor: Center (cx, cy) when registered via sd.add(choice, xy=(cx, cy)).

Constructor Parameters#

Parameter Type Default Description
name str "" Optional identifier for the choice node.
size float 4.5 Diamond width and height in local diagram units.
style Style | None None Optional fill and border style override (falls back to sd.node_style).
show bool True Visibility flag (False hides the diamond and incident transitions).

Public Methods & Properties#

Member Kind Signature Description
center property -> tuple[float, float] Relative center coordinate (cx, cy) of the choice diamond.
effective_height property -> float Actual rendered height (size).
effective_width property -> float Actual rendered width (size).
get_anchor method (self, side: Side) -> tuple[float, float] Return diamond vertex/boundary anchor coordinate on side.
get_bounds method (self) -> tuple[float, float, float, float] Return local bounding box (-size/2, -size/2, size/2, size/2) relative to (0, 0).
xy property -> tuple[float, float] Alias returning the relative center coordinate (cx, cy).

classFinalState#

UML 2.x final state node rendered as a bullseye (outer ring enclosing a solid inner circle). Subclass of StateNodeBase.

FinalState(
    name: str = "",
    radius: float = 2.2,
    style: Style | None = None,
    show: bool = True,
) -> None
  • Coordinate Anchor: Center (cx, cy) when registered via sd.add(final, xy=(cx, cy)).

Constructor Parameters#

Parameter Type Default Description
name str "" Optional identifier for the final state.
radius float 2.2 Outer ring radius in local diagram units (total diameter 2 * radius).
style Style | None None Optional stroke/fill color override for the ring and inner dot.
show bool True Visibility flag (False hides this node and incident transitions).

Public Methods & Properties#

Member Kind Signature Description
center property -> tuple[float, float] Relative center coordinate (cx, cy) of the bullseye.
effective_height property -> float Actual rendered diameter (2 * radius).
effective_width property -> float Actual rendered diameter (2 * radius).
get_anchor method (self, side: Side) -> tuple[float, float] Return outer ring boundary coordinate for side.
get_bounds method (self) -> tuple[float, float, float, float] Return local bounding box (-radius, -radius, radius, radius) relative to (0, 0).
xy property -> tuple[float, float] Alias returning the relative center coordinate (cx, cy).

classForkJoinState#

UML 2.x concurrency synchronization bar splitting one incoming transition into orthogonal concurrent regions (fork) or synchronizing multiple regions back together (join). Subclass of StateNodeBase.

ForkJoinState(
    name: str = "",
    orientation: Literal["horizontal", "vertical"] = "horizontal",
    length: float = 16.0,
    thickness: float = 1.8,
    style: Style | None = None,
    show: bool = True,
) -> None
  • Coordinate Anchor: Center (cx, cy) when registered via sd.add(bar, xy=(cx, cy)).

Constructor Parameters#

Parameter Type Default Description
name str "" Optional identifier for the fork/join bar.
orientation Literal["horizontal", "vertical"] "horizontal" Bar axis ("horizontal" wide bar for top-to-bottom flows; "vertical" tall bar for left-to-right flows).
length float 16.0 Primary bar span along orientation in local units.
thickness float 1.8 Cross-axis bar thickness in local units.
style Style | None None Optional solid fill style override for the bar.
show bool True Visibility flag (False hides the bar and incident transitions).

Public Methods & Properties#

Member Kind Signature Description
center property -> tuple[float, float] Relative center coordinate (cx, cy) of the synchronization bar.
effective_height property -> float Rendered height (thickness when "horizontal", length when "vertical").
effective_width property -> float Rendered width (length when "horizontal", thickness when "vertical").
get_anchor method (self, side: Side) -> tuple[float, float] Return boundary coordinate on side.
get_bounds method (self) -> tuple[float, float, float, float] Return local bounding box (min_x, min_y, max_x, max_y) relative to (0, 0).
xy property -> tuple[float, float] Alias returning the relative center coordinate (cx, cy).

classInitialState#

UML 2.x initial pseudo-state rendered as a solid filled circle marking entry into a state machine. Subclass of StateNodeBase.

InitialState(
    name: str = "",
    radius: float = 1.75,
    style: Style | None = None,
    show: bool = True,
) -> None
  • Coordinate Anchor: Center (cx, cy) when registered via sd.add(init, xy=(cx, cy)).

Constructor Parameters#

Parameter Type Default Description
name str "" Optional identifier for the initial pseudo-state.
radius float 1.75 Solid circle radius in local diagram units (total diameter 3.5).
style Style | None None Optional solid fill color override.
show bool True Visibility flag (False hides this node and outgoing transitions).

Public Methods & Properties#

Member Kind Signature Description
center property -> tuple[float, float] Relative center coordinate (cx, cy) of the circle.
effective_height property -> float Actual rendered diameter (2 * radius).
effective_width property -> float Actual rendered diameter (2 * radius).
get_anchor method (self, side: Side) -> tuple[float, float] Return perimeter anchor coordinate on side.
get_bounds method (self) -> tuple[float, float, float, float] Return local bounding box (-radius, -radius, radius, radius) relative to (0, 0).
xy property -> tuple[float, float] Alias returning the relative center coordinate (cx, cy).

typePaddingType#

Union type alias specifying endpoint clearance gap distances on StateTransition arrows.

PaddingType = float | tuple[float, float]
  • Coordinate Anchor: Offsets transition line endpoints outward from start and end state boundaries (float symmetric or (start_pad, end_pad) tuple).

typeRoutingType#

Literal type controlling curve/segment geometry for StateTransition connectors.

RoutingType = Literal["curved", "orthogonal", "direct"]
  • Coordinate Anchor: Governs path interpolation between start and end boundary anchors.
Literal Value Path Geometry
"curved" Smooth quadratic arc controlled by bend (default; separates bidirectional A <-> B transitions cleanly).
"orthogonal" Right-angled horizontal and vertical Manhattan segments.
"direct" Straight Euclidean line segment between resolved anchors.

typeShapeType#

Literal type selecting the visual geometry of a State node.

ShapeType = Literal["box", "ellipse", "circle", "double_circle", "text_only"]
  • Coordinate Anchor: All state shapes are centered at (cx, cy).
Literal Value Visual Rendering Typical Usage
"box" Rounded rectangle (with optional internal action compartment divider) Standard UML 2.x statechart state (default).
"ellipse" Oval / ellipse Conceptual FSM or Moore/Mealy state node.
"circle" Single-ring circle Mathematical DFA / NFA state vertex (q0, q1).
"double_circle" Concentric double-ring circle DFA / NFA accepting state vertex.
"text_only" Borderless text label Minimalist state label with invisible bounds.

typeSide#

Literal type specifying attachment ports (including diagonal corners) on StateNodeBase nodes.

Side = Literal[
    "left",
    "right",
    "top",
    "bottom",
    "auto",
    "top_right",
    "top_left",
    "bottom_right",
    "bottom_left",
]
  • Coordinate Anchor: Resolves to the exact perimeter point on box, circle, ellipse, or diamond state boundaries ("auto" chooses the nearest facing side).

classState#

Primary state node supporting UML rounded boxes (with optional entry /, do /, exit / internal behavior compartment) as well as mathematical FSM circle/double-circle shapes. Subclass of StateNodeBase.

State(
    name: str,
    shape: ShapeType = "box",
    entry: str = "",
    do: str = "",
    exit: str = "",
    width: float | None = None,
    height: float | None = None,
    style: Style | None = None,
    show: bool = True,
) -> None
  • Coordinate Anchor: Center (cx, cy) when registered via sd.add(state, xy=(cx, cy)).

Constructor Parameters#

Parameter Type Default Description
name str (Required) State title rendered in the main compartment (or header when internal actions exist).
shape ShapeType "box" Node geometry ("box", "ellipse", "circle", "double_circle", "text_only").
entry str "" Optional entry behavior formatted as entry / <entry> on "box" states.
do str "" Optional ongoing activity formatted as do / <do> on "box" states.
exit str "" Optional exit behavior formatted as exit / <exit> on "box" states.
width float | None None Optional explicit width (None auto-sizes from text/actions).
height float | None None Optional explicit height (None auto-sizes from header and action rows).
style Style | None None Optional fill, stroke, and text style override (falls back to sd.node_style).
show bool True Visibility flag (False hides this state and incident transitions).

Public Methods & Properties#

Member Kind Signature Description
add_action method (self, kind: str, action: str) -> State Append a custom StateAction row ("{kind} / {action}") to the internal compartment and return self.
center property -> tuple[float, float] Relative center coordinate (cx, cy) of the state node.
effective_height property -> float Computed rendered height of the state node.
effective_width property -> float Computed rendered width of the state node.
get_anchor method (self, side: Side) -> tuple[float, float] Return perimeter anchor coordinate on side.
get_bounds method (self) -> tuple[float, float, float, float] Return local bounding box (-effective_width/2, -effective_height/2, ...) relative to (0, 0).
xy property -> tuple[float, float] Alias returning the relative center coordinate (cx, cy).

classStateAction#

Validated Pydantic data model representing a single internal state activity row (entry /, do /, exit /, or custom trigger) inside a State box. Typically registered via State(..., entry=..., do=..., exit=...) or state.add_action(kind, action).

StateAction(
    *,
    kind: str,
    action: str,
) -> None
  • Coordinate Anchor: Rendered left-aligned inside the lower compartment of a "box" State.

Constructor Parameters & Fields#

Parameter Type Default Description
kind str (Required) Trigger label such as "entry", "do", "exit", or an event name (keyword-only).
action str (Required) Behavior expression or function signature string (keyword-only).

Public Methods & Properties#

Member Kind Signature Description
display_text property -> str Formatted UML internal behavior string ("{kind} / {action}").
model_copy method (self, *, update: Mapping[str, Any] | None = None, deep: bool = False) -> Self Create a shallow or deep copy with optional field updates (pydantic.BaseModel).
model_dump method (self, *, mode: str = "python", ...) -> dict[str, Any] Serialize fields to a Python dictionary (pydantic.BaseModel).
model_dump_json method (self, *, indent: int | None = None, ...) -> str Serialize fields to a JSON string (pydantic.BaseModel).
model_validate classmethod (obj: Any, *, strict: bool | None = None, ...) -> Self Validate an arbitrary mapping or object into StateAction.
model_validate_json classmethod (json_data: str | bytes | bytearray, ...) -> Self Parse and validate JSON input into StateAction.
model_validate_strings classmethod (obj: Any, ...) -> Self Validate string-typed input dictionary into StateAction.
model_construct classmethod (_fields_set: set[str] | None = None, **values: Any) -> Self Construct an unvalidated instance directly from field values.
model_json_schema classmethod (by_alias: bool = True, ...) -> dict[str, Any] Generate a JSON Schema dictionary for StateAction.

(Also inherits standard pydantic.BaseModel lifecycle/compatibility members: model_extra, model_fields_set, model_parametrized_name, model_post_init, model_rebuild, construct, copy, dict, from_orm, json, parse_file, parse_obj, parse_raw, schema, schema_json, update_forward_refs, and validate.)


classStateDiagram#

Top-level container managing states, pseudo-states, transitions, and bounding-box layout for UML statecharts and FSM diagrams.

StateDiagram(
    *,
    node_style: Style,
    edge_style: Style,
    edge_text_style: Style,
    title: str = "",
    title_style: Style | None = None,
    style: Style | None = None,
    width: float | None = None,
    height: float | None = None,
    margin: float = 5.0,
) -> None
  • Coordinate Anchor: draw(xy=(0.0, 0.0), align="bottom_left") shifts local coordinates by xy (or centers get_bounds() at xy when align="center").

Constructor Parameters#

Parameter Type Default Description
node_style Style (Required) Base style applied to state nodes and pseudo-states (keyword-only).
edge_style Style (Required) Base stroke and arrowhead style applied to StateTransition edges (keyword-only).
edge_text_style Style (Required) Base typography style applied to transition event [guard] / action labels (keyword-only).
title str "" Optional diagram title rendered centered above the statechart.
title_style Style | None None Optional typography style override for title.
style Style | None None Optional outer background card style for the diagram container.
width float | None None Optional fixed diagram width override (None auto-computes from get_bounds() + margin).
height float | None None Optional fixed diagram height override (None auto-computes from get_bounds() + margin).
margin float 5.0 Outer margin padding surrounding all registered states and self-loops.

Public Methods & Properties#

Member Kind Signature Description
add method (self, item: NodeT, xy: tuple[float, float] = (0.0, 0.0), width: float | None = None, height: float | None = None, style: Style | None = None, show: bool | None = None) -> NodeT Register a State or pseudo-state centered at xy (with optional size/style overrides) and return it.
add_transition method (self, trans: StateTransition) -> StateTransition Register a pre-constructed StateTransition with this diagram.
connect method (self, source: StateNodeBase, target: StateNodeBase, *, label: str = "", event: str = "", guard: str = "", action: str = "", side: LoopSide = "top", bend: float = 0.0, start_side: Side = "auto", end_side: Side = "auto", routing: RoutingType = "curved", style: Style | None = None, text_style: Style | None = None, padding: PaddingType = 0.0, loop_width: float | None = None, loop_height: float | None = None, loop_ratio: float = 0.88, show: bool = True) -> StateTransition Create and register a StateTransition (or self-loop when source is target).
draw method (self, xy: tuple[float, float] = (0.0, 0.0), *, align: Literal["bottom_left", "center"] = "bottom_left", scale: PosFloat = 1.0) -> None Render the complete state diagram onto the active canvas anchored at xy.
get_bounds method (self) -> tuple[float, float, float, float] Compute enclosing (min_x, min_y, max_x, max_y) of all states and self-transition loops.
get_size method (self) -> tuple[float, float] Compute overall (width, height) of the diagram including margin.

classStateNodeBase#

Abstract/common base class shared by State, InitialState, FinalState, ChoiceState, and ForkJoinState.

StateNodeBase(
    name: str = "",
    width: float = 20.0,
    height: float = 12.0,
    style: Style | None = None,
    show: bool = True,
) -> None
  • Coordinate Anchor: Center (cx, cy) when registered on StateDiagram.

Constructor Parameters#

Parameter Type Default Description
name str "" Node identifier or label string.
width float 20.0 Base bounding box width in local units.
height float 12.0 Base bounding box height in local units.
style Style | None None Optional Style override.
show bool True Visibility flag for this state node.

Public Methods & Properties#

Member Kind Signature Description
center property -> tuple[float, float] Relative center coordinate (cx, cy) of the state node.
effective_height property -> float Actual rendered height of the state node.
effective_width property -> float Actual rendered width of the state node.
get_anchor method (self, side: Side) -> tuple[float, float] Return boundary anchor coordinate on side.
get_bounds method (self) -> tuple[float, float, float, float] Return local bounding box (min_x, min_y, max_x, max_y) relative to (0, 0).
xy property -> tuple[float, float] Alias returning the relative center coordinate (cx, cy).

classStateTransition#

Directed transition edge (or self-loop ellipse) connecting two StateNodeBase nodes with automatic UML "event [guard] / action" label formatting.

StateTransition(
    start: StateNodeBase,
    end: StateNodeBase,
    label: str = "",
    event: str = "",
    guard: str = "",
    action: str = "",
    bend: float = 0.0,
    start_side: Side = "auto",
    end_side: Side = "auto",
    routing: RoutingType = "curved",
    style: Style | None = None,
    text_style: Style | None = None,
    padding: PaddingType = 0.0,
    loop_side: LoopSide = "top",
    loop_width: float | None = None,
    loop_height: float | None = None,
    loop_ratio: float = 0.88,
    is_loop: bool = False,
    show: bool = True,
) -> None
  • Coordinate Anchor: Connects start_side on start to end_side on end (or attaches a self-loop arc to loop_side when start is end).

Constructor Parameters#

Parameter Type Default Description
start StateNodeBase (Required) Source state or pseudo-state.
end StateNodeBase (Required) Target state or pseudo-state (start for a self-transition).
label str "" Explicit full transition label (overrides event/guard/action formatting when non-empty).
event str "" Trigger event name formatted at the start of "event [guard] / action".
guard str "" Guard condition automatically wrapped in [guard].
action str "" Transition effect automatically prefixed with / action.
bend float 0.0 Arc deflection amount for "curved" routing (0.0 straight; 0.25–0.45 separates bidirectional transitions).
start_side Side "auto" Departure anchor side on start.
end_side Side "auto" Arrival anchor side on end.
routing RoutingType "curved" Path routing strategy ("curved", "orthogonal", or "direct").
style Style | None None Optional line stroke style override (falls back to sd.edge_style).
text_style Style | None None Optional label typography style override (falls back to sd.edge_text_style).
padding PaddingType 0.0 Clearance offset between state borders and transition endpoints.
loop_side Literal["top", "bottom", "left", "right", ...] "top" Attachment side for self-loops (sd.connect(..., side=...) forwards to loop_side).
loop_width float | None None Optional custom ellipse width for self-transition loops.
loop_height float | None None Optional custom ellipse height for self-transition loops.
loop_ratio float 0.88 Fraction of loop ellipse circumference drawn (0.0–1.0).
is_loop bool False Explicit self-loop flag (automatically True whenever start is end).
show bool True Whether to render this transition.

Public Methods & Properties#

Member Kind Signature Description
effective_label property -> str Resolved transition label (label if non-empty, otherwise formatted "event [guard] / action").
is_self_transition property -> bool True when this transition loops back onto the same state (is_loop or start is end).