215 lines
9.4 KiB
Markdown
215 lines
9.4 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```text
|
|
.
|
|
├── 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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 JSON or `.beb` document files and folders.
|
|
|
|
Every component owns its ports, declarative icon, properties, and child graph:
|
|
|
|
```json
|
|
{
|
|
"format": "bedit-document",
|
|
"version": 1,
|
|
"roots": [{
|
|
"id": "my-component",
|
|
"name": "My Component",
|
|
"position": {"x": 0, "y": 0},
|
|
"interface": {
|
|
"ports": [
|
|
{"id": "in", "name": "Input", "type": "signal", "orientation": "input",
|
|
"properties": {"iconPosition": {"x": 0, "y": 40}}},
|
|
{"id": "out", "name": "Output", "type": "signal", "orientation": "output",
|
|
"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.
|