Files
BondGraph/BEdit/README.md
2026-07-20 13:50:33 +02:00

9.3 KiB

BEdit Qt starter

A small desktop application scaffold using Python and PySide6 (the official Qt bindings). Its interface is maintained in Qt Designer and it includes a main window, menu bar, blank central workspace, common keyboard shortcuts, and persisted window geometry. The application forces Qt's light Fusion palette, so it remains light even when the operating-system theme is dark.

Run it

Python 3.10 or newer is required. From this directory:

python -m venv .venv

Activate the environment:

  • Windows PowerShell: .venv\Scripts\Activate.ps1
  • Windows Command Prompt: .venv\Scripts\activate.bat
  • Linux/macOS: source .venv/bin/activate

Then install and launch:

python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
python -m bedit

After installation, the bedit command also launches the application.

Project layout

.
├── pyproject.toml          dependencies, package metadata, and `bedit` command
├── README.md
├── ui/                     editable Qt Designer sources
└── src/bedit
    ├── __main__.py         minimal `python -m bedit` entry point
    ├── core/               Qt-free domain model, port types, files, libraries
    └── gui/                every PySide-dependent module
        ├── app.py          Qt startup and application palette
        ├── main_window.py  top-level UI orchestration
        ├── controllers/    document controller and undo commands
        ├── dialogs/        dialog behavior
        ├── graphics/       graph workspace and vector icon editor
        ├── models/         Qt tree models and repository adapters
        └── generated/      generated UI/resources; do not hand-edit

Designing the UI further

The project now uses Qt Designer. It is included with PySide6 on most installations. Open the existing form with:

pyside6-designer ui/main_window.ui

In Designer, use the Widget Box to add controls, the Object Inspector to select them, and the Property Editor to name and configure them. Always put widgets in a layout (horizontal, vertical, grid, or form) so the window resizes correctly.

Save the form, close the running application if necessary, then regenerate its Python wrapper:

pyside6-uic --from-imports ui/main_window.ui \
  -o src/bedit/gui/generated/ui_main_window.py

Do not hand-edit files in gui/generated; change the .ui source and regenerate it. Add behavior and signal connections in gui/main_window.py. Widget names from Designer are available there through self.ui, such as self.ui.graphView.

Substantial views are all represented in Designer: the main window, settings, component options, port options, shape options, and icon editor. Python binds data and behavior to those forms; it does not rebuild their layouts at runtime. Only tiny generic prompts may be code-only.

In VS Code, the same commands are available through Terminal → Run Task:

  • Qt: Open Main Window in Designer opens the form for visual editing.
  • Qt: Build Designer Files compiles resources and then regenerates the UI wrapper. It is the default build task, available with Ctrl+Shift+B.
  • The separate resource and UI compilation tasks remain available when only one generated file needs rebuilding.

Architecture rules

  • bedit.core must remain importable without PySide6.
  • bedit.gui may depend on core; core must never import gui.
  • Designer output and resource output belong only in gui/generated.
  • Domain serialization and library parsing stay in core; Qt signals, models, undo integration, painting, and widgets stay in gui.

Graph and library prototype

Documents and libraries use the same recursive format: a library is simply a BEdit document used as a copy source. The built-in example defines A, B, and C.

  • A document can own multiple independent top-level graph or text components. Right-click Current Document to create one, and double-click a current component in the tree to activate it.
  • Drag a component from Libraries onto the workspace. Placement recursively copies it with new IDs, leaving no link to the source.
  • Drag components to move them; movement participates in undo and redo.
  • Components, interface terminals, and connections are selectable. Use a rubber band or Ctrl-click for multiple selection, Delete to remove items, and the standard Cut/Copy/Paste shortcuts to duplicate selected component groups.
  • Switch the graph header to Connect and click two blocks or their temporary port hints. A chooser lists every compatible output/input pairing in both directions and preselects the pairing implied by the clicks. Empty-canvas clicks add freely angled, grid-snapped vertices; right-click cancels.
  • Select a routed connection to reveal its draggable nodes. Right-click a line to add a node, or right-click a node to delete it. Visible wire endpoints are projected onto each icon's hitbox where the ray from its center toward the adjacent route point crosses the boundary. Hidden semantic port positions do not constrain the wire.
  • Use Box, Line, and Text to add persistent graph annotations. Lines use the same freely angled polyline and draggable-node behavior as connections. Annotation context menus provide the same shape styling as the icon editor and can move annotations through integer layers below or above graph layer 0.
  • Double-click a graph component or select it and use Down to open its owned subgraph; use Up to return.
  • Graph components show Pointer, Input, and Output tools. Select an interface tool and click the canvas to add a visible internal terminal and a corresponding external block port. Interface terminals can be moved afterward.
  • Right-click a component on the canvas or in Current Document to edit its name, icon shape, icon text, fill color, and border color. The same dialog can hide that component's contained subtree from the Libraries tree.
  • The icon editor uses Pointer and click-drag drawing tools. Its toolbar and mouse wheel provide zoom in, zoom out, and fit-to-canvas controls. Selected rectangles expose draggable corner and edge resize handles in both editors.
  • Double-click a text component to edit its input list, output list, and implementation.source JSON.
  • Right-click any graph component under Current Document to add nested graph or text blocks. Any current-document component can also be deleted there.
  • The left navigator presents the current Document and external Libraries as tabs.
  • Select one or more blocks and press Ctrl+R, or use Rotate in the graph header, to rotate them clockwise by 90 degrees. Rotation is saved and supports undo/redo. Apply JSON updates that source and participates in undo/redo.
  • File → Save writes the complete recursive document to JSON.
  • File → Close Document removes the active document and returns to an empty workspace. An open graph uses a light gray grid whose visible and snapping spacing are configured separately.
  • Edit → Settings → Libraries accepts document files or folders of JSON files.

Every component owns its ports, declarative icon, properties, and child graph:

{
  "format": "bedit-document",
  "version": 1,
  "roots": [{
    "id": "my-component",
    "name": "My Component",
    "position": {"x": 0, "y": 0},
    "interface": {
      "inputs": [{"id": "in", "name": "Input", "type": "signal", "properties": {
        "iconPosition": {"x": 0, "y": 40}
      }}],
      "outputs": [{"id": "out", "name": "Output", "type": "signal", "properties": {
        "iconPosition": {"x": 128, "y": 64}
      }}]
    },
    "icon": {
      "size": {"width": 128, "height": 128},
      "elements": [{
        "type": "rectangle", "x": 32, "y": 32,
        "width": 64, "height": 64,
        "cornerRadius": 5,
        "fill": "#dbeafe", "stroke": "#245c9c",
        "lineWidth": 1.5, "lineStyle": "solid"
      }]
    },
    "properties": {},
    "implementation": {
      "kind": "text",
      "source": {
        "equations": ["out = gain * in"],
        "parameters": {"gain": 1.0}
      }
    }
  }]
}

Graph components use "implementation": {"kind": "graph", "graph": ...}; text components use "implementation": {"kind": "text", "source": ...} and never own a graph. Vector icons can contain rectangles, circles, ellipses, lines, triangles, and text. Each element owns its geometry, fill, stroke, and line style; ports keep their icon anchor in properties.iconPosition. The port type registry controls which types may connect (currently signal only). The workspace grid size, its finer snapping size, and the icon grid size are configured independently in Settings. The recursive model and file loading are under src/bedit/core/; the live Qt trees and graphics are isolated under src/bedit/gui/models/ and src/bedit/gui/graphics/.

Optional tools

You do not need another GUI framework. Useful additions are:

  • Qt Designer for drag-and-drop form layout.
  • Ruff for formatting/linting (ruff check .).
  • pytest-qt later for GUI interaction tests.
  • PyInstaller or Nuitka later to produce a Windows .exe.

The light colors are defined in apply_light_theme() in src/bedit/app.py. Adjust that palette if you want different light colors. Avoid a large stylesheet unless the application needs highly customized controls; palettes preserve more of Qt's standard behavior.