Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

comfy_graph_bind

comfy_graph_bind is a visual input mapper for ComfyUI API-format workflows. It provides a Qt-based editor UI that lets you wire a configurable Start node to workflow inputs, defining how your application communicates with ComfyUI workflows.

This library supports only the ComfyUI LiteGraph API format.

Features

  • Displays the nodes and links of a ComfyUI workflow JSON.
  • Nodes are read-only; only the special Start node can be the source of new links (and only its outputs are connectable).
  • The Start node title is configurable via EditorConfig.start_node_title (defaults to "Start").
  • Inputs that are wired in the source JSON are visible on each node and can be the target of a drag-to-link from the Start node.
  • Drag an output (left dot) to an input (right dot) to create a rope-like bezier link.
  • Remove a link by dragging from a Start output and releasing over empty space — the existing link from that output is removed.
  • Zoom with Ctrl + wheel (or the dialog toolbar buttons), pan with the middle mouse button, and reset the view (positions + zoom) with the toolbar Reset button or the R key.
  • Modal QDialog wrapper (GraphEditorDialog) that hosts the editor with its own toolbar and Ok/Cancel buttons. The dialog can be reopened with a previously obtained result to preload the Start-node links.

Install

git clone https://github.com/dacert/comfy_graph_bind.git
cd comfy_graph_bind
pip install -e .

Quick start

import json

from PyQt5.QtWidgets import QApplication

from comfy_graph_bind import (
    EditorConfig,
    GraphEditorDialog,
    StartOutputConfig,
    parse_workflow_json,
)


app = QApplication([])
graph = parse_workflow_json("path/to/workflow.json")  # returns a WorkflowGraph

dialog = GraphEditorDialog(
    graph,
    EditorConfig(
        workflow_name="My workflow",
        start_node_title="Inputs",  # optional, defaults to "Start"
        start_outputs=[
            StartOutputConfig("prompt", "STRING"),
            StartOutputConfig("seed", "INT"),
            StartOutputConfig("image", "IMAGE"),
        ],
    ),
)
dialog.exec_()
result = dialog.editor_result()  # None if the user cancelled
if result is not None:
    print(json.dumps(result, indent=2))
    # {
    #     "workflow_name": "My workflow",
    #     "inputs": {
    #         "prompt": {"node_id": "97", "property": "prompt"},
    #         "seed":   {"node_id": "3",  "property": "seed"},
    #         "image":  {"node_id": "109","property": "image"},
    #     },
    # }

Alternatively, use from_api_workflow with a raw API dict parsed from any source (file, clipboard, network, …):

import json
from pathlib import Path

from PyQt5.QtWidgets import QApplication

from comfy_graph_bind import EditorConfig, GraphEditorDialog, StartOutputConfig


app = QApplication([])
raw_api = json.loads(Path("path/to/workflow.json").read_text())

dialog = GraphEditorDialog.from_api_workflow(
    raw_api,
    EditorConfig(
        workflow_name="My workflow",
        start_outputs=[StartOutputConfig("prompt", "STRING")],
    ),
)
dialog.exec_()
result = dialog.editor_result()

GraphEditor is also available as a headless widget (no top-level window) you can embed in your own QMainWindow, QDialog or any other container.

Reopening with a preloaded result

import json
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QPlainTextEdit,
    QPushButton,
    QVBoxLayout,
    QWidget,
)
from comfy_graph_bind import EditorConfig, GraphEditorDialog, StartOutputConfig


class Host(QMainWindow):
    def __init__(self, path: str) -> None:
        super().__init__()
        self._path = path
        self._cfg = EditorConfig(
            workflow_name=path.rsplit("/", 1)[-1],
            start_outputs=[
                StartOutputConfig("prompt", "STRING"),
                StartOutputConfig("seed", "INT"),
            ],
        )
        central = QWidget(self)
        self.setCentralWidget(central)
        layout = QVBoxLayout(central)
        self._button = QPushButton("Open graph editor\u2026", central)
        self._textarea = QPlainTextEdit(central)
        self._textarea.setReadOnly(True)
        layout.addWidget(self._button)
        layout.addWidget(self._textarea, 1)
        self._button.clicked.connect(self._open)

    def _open(self) -> None:
        text = self._textarea.toPlainText().strip()
        initial = json.loads(text) if text else None
        with open(self._path) as f:
            raw_api = json.load(f)
        dialog = GraphEditorDialog.from_api_workflow(
            raw_api,
            self._cfg,
            initial_result=initial,
            parent=self,
        )
        dialog.exec_()
        result = dialog.editor_result()  # None if the user cancelled
        if result is not None:
            self._textarea.setPlainText(json.dumps(result, indent=2))


app = QApplication([])
window = Host("path/to/workflow.json")
window.show()
app.exec_()

When initial_result is provided, the dialog preloads the Start-node links so the user can immediately see the existing wiring. Any entry that does not resolve to a known node/property is silently ignored.

A runnable version of this example lives at examples/dialog_demo.py.

Used in the krita_comfyui plugin https://github.com/dacert/krita-comfyui

Select Workflow

About

**comfy_graph_bind** is a visual input mapper for ComfyUI API-format workflows. It provides a Qt-based editor UI that lets you wire a configurable **Start** node to workflow inputs, defining how your application communicates with ComfyUI workflows.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages