Reorganized the application around a clear frontend/backend boundary.

src/bedit/
├── __main__.py
├── core/                   # Pure Python, no PySide
│   ├── model.py
│   ├── port_types.py
│   ├── serializer.py
│   └── libraries.py
└── gui/                    # All Qt-dependent code
    ├── app.py
    ├── main_window.py
    ├── preferences.py
    ├── controllers/
    ├── dialogs/
    ├── graphics/
    ├── models/
    └── generated/          # Designer/resource output only
Notable improvements:
Domain models, serialization, port types, and library parsing are now Qt-free.
Qt signals and undo infrastructure are explicitly isolated under gui/controllers.
Library parsing is separated from the Qt repository and tree models.
All generated Python is contained in gui/generated.
Designer build tasks now write to the generated directory.
The application entry point and package metadata use the new paths.
README now documents the structure and dependency rules.
Removed the old mixed document, library, and workspace packages.
This commit is contained in:
2026-07-20 12:09:00 +02:00
parent 1a47952358
commit 48a2b4c8d0
43 changed files with 1011 additions and 310 deletions

View File

@@ -48,7 +48,7 @@
"args": [ "args": [
"${workspaceFolder}/resources/resources.qrc", "${workspaceFolder}/resources/resources.qrc",
"-o", "-o",
"${workspaceFolder}/src/bedit/resources_rc.py" "${workspaceFolder}/src/bedit/gui/generated/resources_rc.py"
], ],
"options": { "options": {
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
@@ -68,7 +68,7 @@
"--from-imports", "--from-imports",
"${workspaceFolder}/ui/main_window.ui", "${workspaceFolder}/ui/main_window.ui",
"-o", "-o",
"${workspaceFolder}/src/bedit/ui_main_window.py" "${workspaceFolder}/src/bedit/gui/generated/ui_main_window.py"
], ],
"options": { "options": {
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
@@ -87,7 +87,7 @@
"--from-imports", "--from-imports",
"${workspaceFolder}/ui/settings_dialog.ui", "${workspaceFolder}/ui/settings_dialog.ui",
"-o", "-o",
"${workspaceFolder}/src/bedit/ui_settings_dialog.py" "${workspaceFolder}/src/bedit/gui/generated/ui_settings_dialog.py"
], ],
"options": { "options": {
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
@@ -106,7 +106,7 @@
"--from-imports", "--from-imports",
"${workspaceFolder}/ui/component_options_dialog.ui", "${workspaceFolder}/ui/component_options_dialog.ui",
"-o", "-o",
"${workspaceFolder}/src/bedit/ui_component_options_dialog.py" "${workspaceFolder}/src/bedit/gui/generated/ui_component_options_dialog.py"
], ],
"options": {"cwd": "${workspaceFolder}"}, "options": {"cwd": "${workspaceFolder}"},
"problemMatcher": [], "problemMatcher": [],

194
BEdit/AGENTS.md Normal file
View File

@@ -0,0 +1,194 @@
# AGENTS.md
This file is the working guide for AI coding agents contributing to BEdit.
Its instructions apply to the entire repository.
## Project purpose
BEdit is a Python/PySide6 graphical editor for hierarchical block and bond-graph
models. A document contains reusable components, typed and oriented ports,
connections, nested graphs, and editable vector icons.
The application is evolving quickly. Prefer small, coherent changes that improve
the architecture without introducing compatibility layers unless compatibility is
explicitly requested.
## Architecture
The main boundary is strict:
```text
src/bedit/
├── __main__.py
├── core/ # Pure Python; must not import PySide6
│ ├── model.py # Document, component, graph, port, icon data
│ ├── port_types.py # Port type definitions and compatibility
│ ├── serializer.py # JSON persistence
│ └── libraries.py # Library file discovery and parsing
└── gui/ # All Qt-dependent code
├── app.py # QApplication startup and palette
├── main_window.py # Top-level UI orchestration
├── preferences.py # Explicit disk-backed QSettings factory
├── controllers/ # Document controller and undo commands
├── dialogs/ # Dialog behavior
├── graphics/ # Workspace, icon editor, vector rendering
├── models/ # Qt tree models and repository adapters
└── generated/ # Generated UI/resource Python; never hand-edit
```
Dependency direction:
- `bedit.core` may depend only on the Python standard library.
- `bedit.gui` may depend on `bedit.core` and PySide6.
- `bedit.core` must never import `bedit.gui` or PySide6.
- Data parsing and validation belong in `core`.
- Signals, widgets, painting, Qt models, undo integration, and settings UI belong
in `gui`.
Keep the root of `src/bedit` minimal. New UI modules should go into the relevant
`gui` subpackage rather than the package root.
## Important behavior and design decisions
- Components may contain nested graph or text implementations.
- Ports retain stable IDs. Display names, types, orientation, positions, and icon
anchors may change without changing IDs.
- Port orientation is presented as one unified list in the UI, while the model
indexes inputs and outputs separately for connection semantics.
- Port types are registered in `core/port_types.py`. Only compatible types may be
connected. `signal` is currently the only type.
- Port removal or reorientation must be rejected when it would invalidate an
existing connection.
- Connections reference port IDs, never port names.
- Icon editing uses a fixed 128×128 coordinate space.
- The visible/selectable component hitbox is calculated from vector elements,
not from the complete 128×128 icon canvas.
- New component icons default to a centered 64×64 shape.
- Vector elements include rectangles, circles, ellipses, lines, triangles, and
text. Shape styling and geometry are stored in document JSON.
- Icon port anchors are stored in `Port.properties["iconPosition"]`.
- Workspace grid size, workspace snapping size, and icon grid size are separate
persisted settings.
- Settings use `gui.preferences.application_settings()` and are stored under the
explicit `BEdit/BEdit` identity. Do not create anonymous `QSettings()` objects.
- Avoid the QSettings group name `general`; Qt treats `General` specially in INI
files. Autosave keys live under `autosave/`.
- User-visible document edits should participate in undo/redo.
## Qt Designer and generated files
Editable Designer sources are in `ui/`. Resources are defined in
`resources/resources.qrc`.
Never hand-edit files in `src/bedit/gui/generated/`. Modify the corresponding
`.ui` or `.qrc` source and regenerate instead.
Use the configured VS Code task **Qt: Build Designer Files**, or run the relevant
commands directly:
```bash
pyside6-rcc resources/resources.qrc \
-o src/bedit/gui/generated/resources_rc.py
pyside6-uic --from-imports ui/main_window.ui \
-o src/bedit/gui/generated/ui_main_window.py
pyside6-uic --from-imports ui/settings_dialog.ui \
-o src/bedit/gui/generated/ui_settings_dialog.py
pyside6-uic --from-imports ui/component_options_dialog.ui \
-o src/bedit/gui/generated/ui_component_options_dialog.py
```
When adding a promoted/custom widget in Designer, its header must use the real
Python module path, for example `bedit.gui.graphics.workspace`.
Some dialogs are currently assembled programmatically. Keep that code inside
`gui/dialogs`; do not put widget creation in `core`.
## Editing conventions
- Preserve stable document IDs and existing connections.
- Validate candidate document changes before pushing an undo command.
- Keep model serialization symmetrical: additions to `to_dict()` require matching
handling in `from_dict()` and cloning where relevant.
- Use descriptive domain names; avoid generic `utils.py` modules.
- Prefer focused classes and helpers over growing `main_window.py` further.
- Shared vector calculations that do not require Qt belong in `core`; Qt painter
code belongs in `gui/graphics`.
- Do not silently swallow malformed document data. Raise a useful `ValueError` in
`core`, then present it through the GUI layer.
- Preserve unrelated user changes. The worktree may already be dirty.
- Do not delete or overwrite library/document JSON unless the requested workflow
explicitly calls for it.
## Document and library files
- Documents use the `bedit-document` JSON format.
- `test.bedit.json` is a useful manually created example during development.
- Library documents use the same recursive document model.
- Library parsing belongs in `core/libraries.py`; Qt change notifications belong
in `gui/models/library_repository.py`.
- Package library data, if present, belongs under `src/bedit/data/libraries/`.
## Validation
There is not yet a complete automated test suite. For every change, run at least:
```bash
python3 -m compileall -q src
git diff --check
```
Run imports with the source tree explicitly available:
```bash
PYTHONPATH=src python3 -c "import bedit.core; import bedit.gui.app"
```
Verify the backend remains Qt-free after core changes:
```bash
PYTHONPATH=src python3 - <<'PY'
import sys
import bedit.core
assert not any(name.startswith("PySide6") for name in sys.modules)
PY
```
For model changes, add a focused in-memory round-trip check using
`GraphDocument.to_dict()` and `GraphDocument.from_dict()`. For controller changes,
exercise undo and redo when applicable.
GUI smoke tests may fail in headless environments because the system Qt platform
theme tries to access a display even with an offscreen platform. Do not claim an
interactive GUI test passed unless a display-capable environment was actually
used. Compilation, imports, and pure model/controller checks remain useful.
If Ruff is installed, also run:
```bash
ruff check src
```
## Running the application
Install the package in editable mode or run it from the source tree:
```bash
PYTHONPATH=src python3 -m bedit
```
The installed GUI entry point is `bedit.gui.app:main`.
## Completion checklist
Before handing off a change:
1. Confirm the `core`/`gui` dependency boundary is intact.
2. Confirm generated files were not hand-edited.
3. Check serialization and cloning for model changes.
4. Check undo/redo for document mutations.
5. Run compilation and `git diff --check`.
6. Report any GUI behavior that could not be tested interactively.
7. Update README or this file if the architecture or workflow changed.

View File

@@ -36,12 +36,18 @@ After installation, the `bedit` command also launches the application.
. .
├── pyproject.toml dependencies, package metadata, and `bedit` command ├── pyproject.toml dependencies, package metadata, and `bedit` command
├── README.md ├── README.md
├── ui/main_window.ui editable Qt Designer source ├── ui/ editable Qt Designer sources
└── src/bedit └── src/bedit
├── __main__.py supports `python -m bedit` ├── __main__.py minimal `python -m bedit` entry point
├── app.py starts Qt and applies the forced light palette ├── core/ Qt-free domain model, port types, files, libraries
── main_window.py behavior and signal connections ── gui/ every PySide-dependent module
└── ui_main_window.py generated from the Designer file; do not hand-edit ├── 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 ## Designing the UI further
@@ -61,11 +67,12 @@ Save the form, close the running application if necessary, then regenerate its
Python wrapper: Python wrapper:
```bash ```bash
pyside6-uic ui/main_window.ui -o src/bedit/ui_main_window.py pyside6-uic --from-imports ui/main_window.ui \
-o src/bedit/gui/generated/ui_main_window.py
``` ```
Do not hand-edit the generated Python file; change the `.ui` file and regenerate Do not hand-edit files in `gui/generated`; change the `.ui` source and regenerate
it. Add behavior and signal connections in `main_window.py`. Widget names from 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`. Designer are available there through `self.ui`, such as `self.ui.graphView`.
In VS Code, the same commands are available through **Terminal → Run Task**: In VS Code, the same commands are available through **Terminal → Run Task**:
@@ -76,16 +83,13 @@ In VS Code, the same commands are available through **Terminal → Run Task**:
- The separate resource and UI compilation tasks remain available when only one - The separate resource and UI compilation tasks remain available when only one
generated file needs rebuilding. generated file needs rebuilding.
## A sensible next design pass ## Architecture rules
1. Sketch the main tasks and screens before choosing widgets. - `bedit.core` must remain importable without PySide6.
2. Turn each major area into its own widget class in `src/bedit/widgets/`. - `bedit.gui` may depend on `core`; `core` must never import `gui`.
3. Use a `QStackedWidget` for page-like navigation, or `QDockWidget` for movable - Designer output and resource output belong only in `gui/generated`.
tool panels in an editor-style application. - Domain serialization and library parsing stay in `core`; Qt signals, models,
4. Use reusable `QAction` objects for menu commands and any future toolbars. undo integration, painting, and widgets stay in `gui`.
5. Keep file/data operations outside widget classes as the application grows.
6. Add icons through a Qt resource file (`.qrc`) so packaging is reliable.
7. Test on Windows regularly; fonts, scaling, and native dialogs vary by platform.
## Graph and library prototype ## Graph and library prototype
@@ -170,10 +174,9 @@ lines, triangles, and text. Each element owns its geometry, fill, stroke, and
line style; ports keep their icon anchor in `properties.iconPosition`. The line style; ports keep their icon anchor in `properties.iconPosition`. The
port type registry controls which types may connect (currently `signal` only). 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 The workspace grid size, its finer snapping size, and the icon grid size are
configured independently in Settings. The configured independently in Settings. The recursive model and file loading are
recursive model is under `src/bedit/document/`, library loading under `src/bedit/core/`; the live Qt trees and graphics are isolated under
and the live Current Document tree are under `src/bedit/library/`, and graphics `src/bedit/gui/models/` and `src/bedit/gui/graphics/`.
are isolated under `src/bedit/workspace/`.
## Optional tools ## Optional tools

View File

@@ -1,162 +0,0 @@
{
"format": "bedit-document",
"version": 1,
"metadata": {
"name": "Untitled"
},
"roots": [
{
"id": "5ee742db-b25c-4d86-b4ca-cc0e61c4002a",
"name": "New Graph Block 1",
"position": {
"x": 0.0,
"y": 0.0
},
"rotation": 0.0,
"interface": {
"inputs": [],
"outputs": []
},
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Graph",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"type": "rectangle",
"x": 1.0,
"y": 1.0,
"width": 126.0,
"height": 126.0,
"fill": "#f4f4f4",
"stroke": "#303030",
"lineWidth": 1.5,
"lineStyle": "solid",
"cornerRadius": 5.0
},
{
"type": "text",
"x": 8.0,
"y": 8.0,
"width": 112.0,
"height": 112.0,
"text": "Graph",
"color": "#202020",
"fontSize": 12.0
}
]
},
"properties": {},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "graph",
"graph": {
"blocks": [
{
"id": "89944479-e73e-446a-8d58-ebf35ac4144b",
"name": "New Graph Block 1",
"position": {
"x": 0.0,
"y": -96.0
},
"rotation": 0.0,
"interface": {
"inputs": [
{
"id": "port-e5cb81a5",
"name": "Port 1",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 32.0,
"y": 64.0
}
},
"type": "signal"
}
],
"outputs": [
{
"id": "port-eb0034af",
"name": "Port 2",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 96.0,
"y": 64.0
}
},
"type": "signal"
}
]
},
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Graph",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"type": "rectangle",
"x": 32.0,
"y": 32.0,
"width": 64.0,
"height": 64.0,
"fill": "#f4f4f4",
"stroke": "#303030",
"lineWidth": 1.5,
"lineStyle": "solid",
"cornerRadius": 5.0
},
{
"type": "text",
"x": 40.0,
"y": 40.0,
"width": 48.0,
"height": 48.0,
"text": "Graph",
"color": "#303030",
"fontSize": 12.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#303030",
"fill": "#ffffff"
}
]
},
"properties": {},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "graph",
"graph": {
"blocks": [],
"connections": []
}
}
}
],
"connections": []
}
}
}
]
}

View File

@@ -19,7 +19,7 @@ dev = [
] ]
[project.gui-scripts] [project.gui-scripts]
bedit = "bedit.app:main" bedit = "bedit.gui.app:main"
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
where = ["src"] where = ["src"]

View File

@@ -1,6 +1,5 @@
from bedit.app import main from bedit.gui.app import main
if __name__ == "__main__": if __name__ == "__main__":
raise SystemExit(main()) raise SystemExit(main())

View File

@@ -0,0 +1,5 @@
"""Pure BEdit domain model and persistence; this package has no Qt dependency."""
from bedit.core.model import Component, Connection, Endpoint, Graph, GraphDocument, Icon, Port
__all__ = ["Component", "Connection", "Endpoint", "Graph", "GraphDocument", "Icon", "Port"]

View File

@@ -0,0 +1,32 @@
import json
from dataclasses import dataclass
from pathlib import Path
from bedit.core.model import GraphDocument
@dataclass(frozen=True)
class LibraryDocument:
name: str
document: GraphDocument
source_path: str
def bundled_library_path() -> Path:
return Path(__file__).resolve().parents[1] / "data" / "libraries" / "example.json"
def default_library_paths() -> list[str]:
return [str(bundled_library_path())]
def load_library_file(path: Path) -> LibraryDocument:
with path.open(encoding="utf-8") as file:
data = json.load(file)
document = GraphDocument.from_dict(data)
name = str(document.metadata.get("name") or path.stem)
return LibraryDocument(name, document, str(path))
def library_candidates(path: Path) -> list[Path]:
return sorted(path.glob("*.json")) if path.is_dir() else [path]

View File

@@ -5,7 +5,7 @@ from dataclasses import dataclass, field
from typing import Any from typing import Any
from uuid import uuid4 from uuid import uuid4
from bedit.document.port_types import PortTypeRegistry from bedit.core.port_types import PortTypeRegistry
@dataclass @dataclass

View File

@@ -1,7 +1,7 @@
import json import json
from pathlib import Path from pathlib import Path
from bedit.document.model import GraphDocument from bedit.core.model import GraphDocument
class JsonDocumentSerializer: class JsonDocumentSerializer:
@@ -20,4 +20,3 @@ class JsonDocumentSerializer:
json.dump(document.to_dict(), file, indent=2) json.dump(document.to_dict(), file, indent=2)
file.write("\n") file.write("\n")
temporary_path.replace(path) temporary_path.replace(path)

View File

@@ -0,0 +1,212 @@
{
"format": "bedit-document",
"version": 1,
"metadata": {
"name": "Example"
},
"roots": [
{
"id": "bf714517-e7b2-4f4b-8b53-55642e3beb28",
"name": "A",
"position": {
"x": 0.0,
"y": 0.0
},
"rotation": 0.0,
"interface": {
"inputs": [
{
"id": "port-5c5d4695",
"name": "Port 1",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 32.0,
"y": 64.0
}
},
"type": "signal"
}
],
"outputs": [
{
"id": "port-de109124",
"name": "Port 2",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 96.0,
"y": 64.0
}
},
"type": "signal"
}
]
},
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Text",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"type": "rectangle",
"x": 32.0,
"y": 32.0,
"width": 64.0,
"height": 64.0,
"fill": "#f4f4f4",
"stroke": "#303030",
"lineWidth": 1.5,
"lineStyle": "solid",
"cornerRadius": 5.0
},
{
"type": "text",
"x": 40.0,
"y": 40.0,
"width": 48.0,
"height": 48.0,
"text": "A",
"color": "#303030",
"fontSize": 12.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#303030",
"fill": "#ffffff"
}
]
},
"properties": {},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "text",
"source": {
"equations": [],
"parameters": {}
}
}
},
{
"id": "73c9d0c0-293d-4a55-8b89-a1f095dfa75f",
"name": "B",
"position": {
"x": 0.0,
"y": 0.0
},
"rotation": 0.0,
"interface": {
"inputs": [
{
"id": "port-4f732b3e",
"name": "Port 1",
"position": {
"x": -176.0,
"y": -144.0
},
"properties": {
"iconPosition": {
"x": 32.0,
"y": 48.0
}
},
"type": "signal"
},
{
"id": "port-ef7c4218",
"name": "Port 2",
"position": {
"x": -176.0,
"y": -16.0
},
"properties": {
"iconPosition": {
"x": 32.0,
"y": 80.0
}
},
"type": "signal"
}
],
"outputs": [
{
"id": "port-b68679b2",
"name": "Port 3",
"position": {
"x": 128.0,
"y": -80.0
},
"properties": {
"iconPosition": {
"x": 96.0,
"y": 64.0
}
},
"type": "signal"
}
]
},
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Graph",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"type": "rectangle",
"x": 32.0,
"y": 32.0,
"width": 64.0,
"height": 64.0,
"fill": "#f4f4f4",
"stroke": "#303030",
"lineWidth": 1.5,
"lineStyle": "solid",
"cornerRadius": 5.0
},
{
"type": "text",
"x": 40.0,
"y": 40.0,
"width": 48.0,
"height": 48.0,
"text": "B",
"color": "#303030",
"fontSize": 12.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#303030",
"fill": "#ffffff"
}
]
},
"properties": {},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "graph",
"graph": {
"blocks": [],
"connections": []
}
}
}
]
}

View File

@@ -1,13 +0,0 @@
from bedit.document.controller import DocumentController
from bedit.document.model import Component, Connection, Endpoint, Graph, GraphDocument, Icon, Port
__all__ = [
"Component",
"Connection",
"DocumentController",
"Endpoint",
"Graph",
"GraphDocument",
"Icon",
"Port",
]

View File

@@ -0,0 +1 @@
"""Qt user interface and application adapters."""

View File

@@ -4,7 +4,7 @@ from PySide6.QtCore import QCoreApplication, Qt
from PySide6.QtGui import QColor, QPalette from PySide6.QtGui import QColor, QPalette
from PySide6.QtWidgets import QApplication, QStyleFactory from PySide6.QtWidgets import QApplication, QStyleFactory
from bedit.main_window import MainWindow from bedit.gui.main_window import MainWindow
def apply_light_theme(app: QApplication) -> None: def apply_light_theme(app: QApplication) -> None:

View File

@@ -0,0 +1,5 @@
"""Qt-aware application controllers and undo commands."""
from bedit.gui.controllers.document import DocumentController
__all__ = ["DocumentController"]

View File

@@ -1,7 +1,7 @@
from PySide6.QtCore import QPointF from PySide6.QtCore import QPointF
from PySide6.QtGui import QUndoCommand from PySide6.QtGui import QUndoCommand
from bedit.document.model import Component, Connection, Port from bedit.core.model import Component, Connection, Port
class AddComponentCommand(QUndoCommand): class AddComponentCommand(QUndoCommand):

View File

@@ -5,7 +5,7 @@ from uuid import uuid4
from PySide6.QtCore import QObject, QPointF, Signal from PySide6.QtCore import QObject, QPointF, Signal
from PySide6.QtGui import QUndoStack from PySide6.QtGui import QUndoStack
from bedit.document.commands import ( from bedit.gui.controllers.commands import (
AddComponentCommand, AddComponentCommand,
AddConnectionCommand, AddConnectionCommand,
AddInterfacePortCommand, AddInterfacePortCommand,
@@ -20,7 +20,7 @@ from bedit.document.commands import (
ReplaceSourceCommand, ReplaceSourceCommand,
RotateComponentsCommand, RotateComponentsCommand,
) )
from bedit.document.model import ( from bedit.core.model import (
Component, Component,
Connection, Connection,
Endpoint, Endpoint,
@@ -29,8 +29,8 @@ from bedit.document.model import (
Port, Port,
clone_component, clone_component,
) )
from bedit.document.port_types import PortTypeRegistry from bedit.core.port_types import PortTypeRegistry
from bedit.document.serializer import JsonDocumentSerializer from bedit.core.serializer import JsonDocumentSerializer
class DocumentController(QObject): class DocumentController(QObject):

View File

@@ -0,0 +1 @@
"""Application dialogs."""

View File

@@ -1,8 +1,8 @@
from PySide6.QtWidgets import QDialog, QMessageBox, QPushButton from PySide6.QtWidgets import QDialog, QMessageBox, QPushButton
from bedit.document.model import Component from bedit.core.model import Component
from bedit.icon_editor import IconEditorDialog from bedit.gui.graphics.icon_editor import IconEditorDialog
from bedit.ui_component_options_dialog import Ui_ComponentOptionsDialog from bedit.gui.generated.ui_component_options_dialog import Ui_ComponentOptionsDialog
class ComponentOptionsDialog(QDialog): class ComponentOptionsDialog(QDialog):

View File

@@ -19,8 +19,8 @@ from PySide6.QtWidgets import (
QWidget, QWidget,
) )
from bedit.document.model import Component, Port from bedit.core.model import Component, Port
from bedit.document.port_types import PortTypeRegistry from bedit.core.port_types import PortTypeRegistry
PORT_ROLE = Qt.ItemDataRole.UserRole PORT_ROLE = Qt.ItemDataRole.UserRole

View File

@@ -3,9 +3,9 @@ from pathlib import Path
from PySide6.QtCore import QSettings, Signal from PySide6.QtCore import QSettings, Signal
from PySide6.QtWidgets import QDialog, QFileDialog, QFormLayout, QGroupBox, QSpinBox from PySide6.QtWidgets import QDialog, QFileDialog, QFormLayout, QGroupBox, QSpinBox
from bedit.library.repository import default_library_paths from bedit.core.libraries import default_library_paths
from bedit.preferences import application_settings from bedit.gui.preferences import application_settings
from bedit.ui_settings_dialog import Ui_SettingsDialog from bedit.gui.generated.ui_settings_dialog import Ui_SettingsDialog
class SettingsDialog(QDialog): class SettingsDialog(QDialog):

View File

@@ -0,0 +1 @@
"""Generated Qt code. Do not edit these modules by hand."""

View File

@@ -22,7 +22,7 @@ from PySide6.QtWidgets import (QApplication, QDockWidget, QFrame, QHBoxLayout,
QSpacerItem, QSplitter, QStackedWidget, QToolBar, QSpacerItem, QSplitter, QStackedWidget, QToolBar,
QToolButton, QTreeView, QVBoxLayout, QWidget) QToolButton, QTreeView, QVBoxLayout, QWidget)
from bedit.workspace.view import GraphWorkspaceView from bedit.gui.graphics.workspace import GraphWorkspaceView
from . import resources_rc from . import resources_rc
class Ui_MainWindow(object): class Ui_MainWindow(object):

View File

@@ -0,0 +1,5 @@
"""Graphics scenes, views, editors, and renderers."""
from bedit.gui.graphics.workspace import GraphWorkspaceView
__all__ = ["GraphWorkspaceView"]

View File

@@ -27,9 +27,9 @@ from PySide6.QtWidgets import (
QWidget, QWidget,
) )
from bedit.document.model import Component, Icon, Port from bedit.core.model import Component, Icon, Port
from bedit.icon_renderer import _pen from bedit.gui.graphics.icon_renderer import _pen
from bedit.preferences import application_settings from bedit.gui.preferences import application_settings
def _icon_grid_size() -> int: def _icon_grid_size() -> int:

View File

@@ -1,7 +1,7 @@
from PySide6.QtCore import QPointF, QRectF, Qt from PySide6.QtCore import QPointF, QRectF, Qt
from PySide6.QtGui import QColor, QFont, QIcon, QPainter, QPen, QPixmap, QPolygonF from PySide6.QtGui import QColor, QFont, QIcon, QPainter, QPen, QPixmap, QPolygonF
from bedit.document.model import Icon from bedit.core.model import Icon
def icon_bounds(icon: Icon) -> QRectF: def icon_bounds(icon: Icon) -> QRectF:

View File

@@ -28,11 +28,11 @@ from PySide6.QtWidgets import (
QWidget, QWidget,
) )
from bedit.document.controller import DocumentController from bedit.core.model import Component, Connection, Endpoint, Port
from bedit.document.model import Component, Connection, Endpoint, Port from bedit.gui.controllers.document import DocumentController
from bedit.library.tree_model import COMPONENT_MIME_TYPE from bedit.gui.models.library_tree import COMPONENT_MIME_TYPE
from bedit.icon_renderer import icon_bounds, paint_icon from bedit.gui.graphics.icon_renderer import icon_bounds, paint_icon
from bedit.preferences import application_settings from bedit.gui.preferences import application_settings
SELECTION_MIME_TYPE = "application/x-bedit-selection" SELECTION_MIME_TYPE = "application/x-bedit-selection"

View File

@@ -6,23 +6,23 @@ from PySide6.QtCore import QSize, Qt, Slot
from PySide6.QtGui import QAction, QCloseEvent from PySide6.QtGui import QAction, QCloseEvent
from PySide6.QtWidgets import QFileDialog, QMainWindow, QMenu, QMessageBox, QToolBar from PySide6.QtWidgets import QFileDialog, QMainWindow, QMenu, QMessageBox, QToolBar
from bedit.component_options_dialog import ComponentOptionsDialog from bedit.core.model import Component, Port
from bedit.document.controller import DocumentController from bedit.core.serializer import JsonDocumentSerializer
from bedit.document.model import Component, Port from bedit.gui.controllers.document import DocumentController
from bedit.document.serializer import JsonDocumentSerializer from bedit.gui.dialogs.component_options import ComponentOptionsDialog
from bedit.item_options_dialog import ItemOptionsDialog from bedit.gui.dialogs.item_options import ItemOptionsDialog
from bedit.library.repository import LibraryRepository from bedit.gui.models.library_repository import LibraryRepository
from bedit.library.tree_model import ( from bedit.gui.models.library_tree import (
COMPONENT_ID_ROLE, COMPONENT_ID_ROLE,
COMPONENT_INSTANCE_ROLE, COMPONENT_INSTANCE_ROLE,
ITEM_KIND_ROLE, ITEM_KIND_ROLE,
DocumentTreeModel, DocumentTreeModel,
LibraryTreeModel, LibraryTreeModel,
) )
from bedit.settings_dialog import SettingsDialog from bedit.gui.dialogs.settings import SettingsDialog
from bedit.port_options_dialog import PortOptionsDialog from bedit.gui.dialogs.port_options import PortOptionsDialog
from bedit.preferences import application_settings from bedit.gui.preferences import application_settings
from bedit.ui_main_window import Ui_MainWindow from bedit.gui.generated.ui_main_window import Ui_MainWindow
class MainWindow(QMainWindow): class MainWindow(QMainWindow):

View File

@@ -0,0 +1 @@
"""Qt model/view adapters and repositories."""

View File

@@ -0,0 +1,31 @@
import json
from pathlib import Path
from PySide6.QtCore import QObject, Signal
from bedit.core.libraries import LibraryDocument, library_candidates, load_library_file
class LibraryRepository(QObject):
librariesChanged = Signal()
loadWarningsChanged = Signal(list)
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.libraries: list[LibraryDocument] = []
self.load_warnings: list[str] = []
def load_paths(self, paths: list[str]) -> None:
libraries: list[LibraryDocument] = []
warnings: list[str] = []
for raw_path in paths:
path = Path(raw_path).expanduser()
for candidate in library_candidates(path):
try:
libraries.append(load_library_file(candidate))
except (OSError, ValueError, KeyError, TypeError, json.JSONDecodeError) as error:
warnings.append(f"{candidate}: {error}")
self.libraries = libraries
self.load_warnings = warnings
self.librariesChanged.emit()
self.loadWarningsChanged.emit(warnings)

View File

@@ -3,10 +3,10 @@ import json
from PySide6.QtCore import QByteArray, QMimeData, QModelIndex, Qt, Signal from PySide6.QtCore import QByteArray, QMimeData, QModelIndex, Qt, Signal
from PySide6.QtGui import QStandardItem, QStandardItemModel from PySide6.QtGui import QStandardItem, QStandardItemModel
from bedit.document.controller import DocumentController from bedit.core.model import Component
from bedit.document.model import Component from bedit.gui.controllers.document import DocumentController
from bedit.icon_renderer import library_icon from bedit.gui.graphics.icon_renderer import library_icon
from bedit.library.repository import LibraryRepository from bedit.gui.models.library_repository import LibraryRepository
COMPONENT_ROLE = Qt.ItemDataRole.UserRole + 1 COMPONENT_ROLE = Qt.ItemDataRole.UserRole + 1

View File

@@ -1,3 +0,0 @@
from bedit.library.repository import LibraryRepository, default_library_paths
__all__ = ["LibraryRepository", "default_library_paths"]

View File

@@ -1,56 +0,0 @@
import json
from dataclasses import dataclass
from pathlib import Path
from PySide6.QtCore import QObject, Signal
from bedit.document.model import GraphDocument
@dataclass(frozen=True)
class LibraryDocument:
name: str
document: GraphDocument
source_path: str
def bundled_library_path() -> Path:
return Path(__file__).resolve().parents[1] / "data" / "libraries" / "example.json"
def default_library_paths() -> list[str]:
return [str(bundled_library_path())]
class LibraryRepository(QObject):
librariesChanged = Signal()
loadWarningsChanged = Signal(list)
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.libraries: list[LibraryDocument] = []
self.load_warnings: list[str] = []
def load_paths(self, paths: list[str]) -> None:
libraries: list[LibraryDocument] = []
warnings: list[str] = []
for raw_path in paths:
path = Path(raw_path).expanduser()
candidates = sorted(path.glob("*.json")) if path.is_dir() else [path]
for candidate in candidates:
try:
libraries.append(self._load_file(candidate))
except (OSError, ValueError, KeyError, TypeError, json.JSONDecodeError) as error:
warnings.append(f"{candidate}: {error}")
self.libraries = libraries
self.load_warnings = warnings
self.librariesChanged.emit()
self.loadWarningsChanged.emit(warnings)
@staticmethod
def _load_file(path: Path) -> LibraryDocument:
with path.open(encoding="utf-8") as file:
data = json.load(file)
document = GraphDocument.from_dict(data)
name = str(document.metadata.get("name") or path.stem)
return LibraryDocument(name, document, str(path))

View File

@@ -1,4 +0,0 @@
from bedit.workspace.view import GraphWorkspaceView
__all__ = ["GraphWorkspaceView"]

View File

@@ -64,7 +64,7 @@
"name": "New Graph Block 1", "name": "New Graph Block 1",
"position": { "position": {
"x": 0.0, "x": 0.0,
"y": -96.0 "y": -64.0
}, },
"rotation": 0.0, "rotation": 0.0,
"interface": { "interface": {

View File

@@ -484,7 +484,7 @@
<customwidget> <customwidget>
<class>GraphWorkspaceView</class> <class>GraphWorkspaceView</class>
<extends>QGraphicsView</extends> <extends>QGraphicsView</extends>
<header>bedit.workspace.view</header> <header>bedit.gui.graphics.workspace</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources>

450
BEdit/ui/main_window_ui.py Normal file
View File

@@ -0,0 +1,450 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'main_window.ui'
##
## Created by: Qt User Interface Compiler version 6.11.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QCursor, QFont, QFontDatabase, QGradient,
QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWidgets import (QApplication, QDockWidget, QFrame, QHBoxLayout,
QHeaderView, QLabel, QMainWindow, QMenu,
QMenuBar, QPlainTextEdit, QPushButton, QSizePolicy,
QSpacerItem, QSplitter, QStackedWidget, QToolBar,
QToolButton, QTreeView, QVBoxLayout, QWidget)
from bedit.gui.graphics.workspace import GraphWorkspaceView
import resources_rc
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(1000, 700)
self.actionNew = QAction(MainWindow)
self.actionNew.setObjectName(u"actionNew")
icon = QIcon()
icon.addFile(u":/icons/icons/document-new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionNew.setIcon(icon)
self.actionRotateClockwise = QAction(MainWindow)
self.actionRotateClockwise.setObjectName(u"actionRotateClockwise")
icon1 = QIcon()
icon1.addFile(u":/icons/icons/transform-rotate.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionRotateClockwise.setIcon(icon1)
self.actionOpen = QAction(MainWindow)
self.actionOpen.setObjectName(u"actionOpen")
icon2 = QIcon()
icon2.addFile(u":/icons/icons/document-open.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionOpen.setIcon(icon2)
self.actionSave = QAction(MainWindow)
self.actionSave.setObjectName(u"actionSave")
icon3 = QIcon()
icon3.addFile(u":/icons/icons/document-save.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionSave.setIcon(icon3)
self.actionSaveAs = QAction(MainWindow)
self.actionSaveAs.setObjectName(u"actionSaveAs")
icon4 = QIcon()
icon4.addFile(u":/icons/icons/document-save-as.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionSaveAs.setIcon(icon4)
self.actionExit = QAction(MainWindow)
self.actionExit.setObjectName(u"actionExit")
self.actionClose = QAction(MainWindow)
self.actionClose.setObjectName(u"actionClose")
self.actionUndo = QAction(MainWindow)
self.actionUndo.setObjectName(u"actionUndo")
icon5 = QIcon()
icon5.addFile(u":/icons/icons/edit-undo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionUndo.setIcon(icon5)
self.actionRedo = QAction(MainWindow)
self.actionRedo.setObjectName(u"actionRedo")
icon6 = QIcon()
icon6.addFile(u":/icons/icons/edit-redo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionRedo.setIcon(icon6)
self.actionCut = QAction(MainWindow)
self.actionCut.setObjectName(u"actionCut")
icon7 = QIcon()
icon7.addFile(u":/icons/icons/edit-cut.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCut.setIcon(icon7)
self.actionCopy = QAction(MainWindow)
self.actionCopy.setObjectName(u"actionCopy")
icon8 = QIcon()
icon8.addFile(u":/icons/icons/edit-copy.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCopy.setIcon(icon8)
self.actionPaste = QAction(MainWindow)
self.actionPaste.setObjectName(u"actionPaste")
icon9 = QIcon()
icon9.addFile(u":/icons/icons/edit-paste.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionPaste.setIcon(icon9)
self.actionSelectAll = QAction(MainWindow)
self.actionSelectAll.setObjectName(u"actionSelectAll")
self.actionDelete = QAction(MainWindow)
self.actionDelete.setObjectName(u"actionDelete")
self.actionAbout = QAction(MainWindow)
self.actionAbout.setObjectName(u"actionAbout")
self.actionSettings = QAction(MainWindow)
self.actionSettings.setObjectName(u"actionSettings")
self.actionAboutQt = QAction(MainWindow)
self.actionAboutQt.setObjectName(u"actionAboutQt")
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.workspaceLayout = QHBoxLayout(self.centralwidget)
self.workspaceLayout.setSpacing(0)
self.workspaceLayout.setObjectName(u"workspaceLayout")
self.workspaceLayout.setContentsMargins(0, 0, 0, 0)
self.workspaceSplitter = QSplitter(self.centralwidget)
self.workspaceSplitter.setObjectName(u"workspaceSplitter")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.workspaceSplitter.sizePolicy().hasHeightForWidth())
self.workspaceSplitter.setSizePolicy(sizePolicy)
self.workspaceSplitter.setOrientation(Qt.Orientation.Horizontal)
self.workspaceSplitter.setChildrenCollapsible(False)
self.leftDockHost = QMainWindow(self.workspaceSplitter)
self.leftDockHost.setObjectName(u"leftDockHost")
self.leftDockHost.setMinimumSize(QSize(220, 0))
self.panel_libraries = QDockWidget(self.leftDockHost)
self.panel_libraries.setObjectName(u"panel_libraries")
self.panel_libraries.setMinimumSize(QSize(220, 91))
self.dockWidgetContents = QWidget()
self.dockWidgetContents.setObjectName(u"dockWidgetContents")
self.librariesLayout = QVBoxLayout(self.dockWidgetContents)
self.librariesLayout.setSpacing(0)
self.librariesLayout.setObjectName(u"librariesLayout")
self.librariesLayout.setContentsMargins(0, 0, 0, 0)
self.treeView = QTreeView(self.dockWidgetContents)
self.treeView.setObjectName(u"treeView")
self.treeView.setAlternatingRowColors(True)
self.treeView.setUniformRowHeights(True)
self.librariesLayout.addWidget(self.treeView)
self.panel_libraries.setWidget(self.dockWidgetContents)
self.leftDockHost.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.panel_libraries)
self.panel_document = QDockWidget(self.leftDockHost)
self.panel_document.setObjectName(u"panel_document")
self.panel_document.setMinimumSize(QSize(220, 91))
self.documentDockContents = QWidget()
self.documentDockContents.setObjectName(u"documentDockContents")
self.documentPanelLayout = QVBoxLayout(self.documentDockContents)
self.documentPanelLayout.setSpacing(0)
self.documentPanelLayout.setObjectName(u"documentPanelLayout")
self.documentPanelLayout.setContentsMargins(0, 0, 0, 0)
self.documentTreeView = QTreeView(self.documentDockContents)
self.documentTreeView.setObjectName(u"documentTreeView")
self.documentTreeView.setAlternatingRowColors(True)
self.documentTreeView.setUniformRowHeights(True)
self.documentPanelLayout.addWidget(self.documentTreeView)
self.panel_document.setWidget(self.documentDockContents)
self.leftDockHost.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.panel_document)
self.workspaceSplitter.addWidget(self.leftDockHost)
self.workspace = QWidget(self.workspaceSplitter)
self.workspace.setObjectName(u"workspace")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
sizePolicy1.setHorizontalStretch(1)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.workspace.sizePolicy().hasHeightForWidth())
self.workspace.setSizePolicy(sizePolicy1)
self.workspaceEditorLayout = QVBoxLayout(self.workspace)
self.workspaceEditorLayout.setSpacing(0)
self.workspaceEditorLayout.setObjectName(u"workspaceEditorLayout")
self.workspaceEditorLayout.setContentsMargins(0, 0, 0, 0)
self.workspaceHeader = QFrame(self.workspace)
self.workspaceHeader.setObjectName(u"workspaceHeader")
self.workspaceHeader.setMinimumSize(QSize(0, 34))
self.workspaceHeader.setMaximumSize(QSize(16777215, 34))
self.workspaceHeader.setFrameShape(QFrame.Shape.StyledPanel)
self.workspaceHeaderLayout = QHBoxLayout(self.workspaceHeader)
self.workspaceHeaderLayout.setObjectName(u"workspaceHeaderLayout")
self.workspaceHeaderLayout.setContentsMargins(6, 2, 6, 2)
self.navigateUpButton = QToolButton(self.workspaceHeader)
self.navigateUpButton.setObjectName(u"navigateUpButton")
self.workspaceHeaderLayout.addWidget(self.navigateUpButton)
self.graphBreadcrumbLabel = QLabel(self.workspaceHeader)
self.graphBreadcrumbLabel.setObjectName(u"graphBreadcrumbLabel")
self.workspaceHeaderLayout.addWidget(self.graphBreadcrumbLabel)
self.workspaceModeLabel = QLabel(self.workspaceHeader)
self.workspaceModeLabel.setObjectName(u"workspaceModeLabel")
self.workspaceHeaderLayout.addWidget(self.workspaceModeLabel)
self.workspaceHeaderSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.workspaceHeaderLayout.addItem(self.workspaceHeaderSpacer)
self.applyJsonButton = QPushButton(self.workspaceHeader)
self.applyJsonButton.setObjectName(u"applyJsonButton")
self.applyJsonButton.setVisible(False)
self.workspaceHeaderLayout.addWidget(self.applyJsonButton)
self.pointerToolButton = QToolButton(self.workspaceHeader)
self.pointerToolButton.setObjectName(u"pointerToolButton")
self.pointerToolButton.setCheckable(True)
self.pointerToolButton.setChecked(True)
self.pointerToolButton.setAutoExclusive(True)
self.workspaceHeaderLayout.addWidget(self.pointerToolButton)
self.inputToolButton = QToolButton(self.workspaceHeader)
self.inputToolButton.setObjectName(u"inputToolButton")
self.inputToolButton.setCheckable(True)
self.inputToolButton.setAutoExclusive(True)
self.workspaceHeaderLayout.addWidget(self.inputToolButton)
self.outputToolButton = QToolButton(self.workspaceHeader)
self.outputToolButton.setObjectName(u"outputToolButton")
self.outputToolButton.setCheckable(True)
self.outputToolButton.setAutoExclusive(True)
self.workspaceHeaderLayout.addWidget(self.outputToolButton)
self.workspaceEditorLayout.addWidget(self.workspaceHeader)
self.workspaceStack = QStackedWidget(self.workspace)
self.workspaceStack.setObjectName(u"workspaceStack")
self.graphPage = QWidget()
self.graphPage.setObjectName(u"graphPage")
self.graphPageLayout = QVBoxLayout(self.graphPage)
self.graphPageLayout.setObjectName(u"graphPageLayout")
self.graphPageLayout.setContentsMargins(0, 0, 0, 0)
self.graphView = GraphWorkspaceView(self.graphPage)
self.graphView.setObjectName(u"graphView")
self.graphPageLayout.addWidget(self.graphView)
self.workspaceStack.addWidget(self.graphPage)
self.jsonPage = QWidget()
self.jsonPage.setObjectName(u"jsonPage")
self.jsonPageLayout = QVBoxLayout(self.jsonPage)
self.jsonPageLayout.setObjectName(u"jsonPageLayout")
self.jsonPageLayout.setContentsMargins(0, 0, 0, 0)
self.jsonEditor = QPlainTextEdit(self.jsonPage)
self.jsonEditor.setObjectName(u"jsonEditor")
self.jsonEditor.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
self.jsonPageLayout.addWidget(self.jsonEditor)
self.workspaceStack.addWidget(self.jsonPage)
self.emptyPage = QWidget()
self.emptyPage.setObjectName(u"emptyPage")
self.emptyPageLayout = QVBoxLayout(self.emptyPage)
self.emptyPageLayout.setObjectName(u"emptyPageLayout")
self.emptyWorkspaceLabel = QLabel(self.emptyPage)
self.emptyWorkspaceLabel.setObjectName(u"emptyWorkspaceLabel")
self.emptyWorkspaceLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.emptyPageLayout.addWidget(self.emptyWorkspaceLabel)
self.workspaceStack.addWidget(self.emptyPage)
self.workspaceEditorLayout.addWidget(self.workspaceStack)
self.workspaceSplitter.addWidget(self.workspace)
self.workspaceLayout.addWidget(self.workspaceSplitter)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 1000, 24))
self.menuFile = QMenu(self.menubar)
self.menuFile.setObjectName(u"menuFile")
self.menuEdit = QMenu(self.menubar)
self.menuEdit.setObjectName(u"menuEdit")
self.menuView = QMenu(self.menubar)
self.menuView.setObjectName(u"menuView")
self.menuPanels = QMenu(self.menuView)
self.menuPanels.setObjectName(u"menuPanels")
self.menuToolbars = QMenu(self.menuView)
self.menuToolbars.setObjectName(u"menuToolbars")
self.menuHelp = QMenu(self.menubar)
self.menuHelp.setObjectName(u"menuHelp")
MainWindow.setMenuBar(self.menubar)
self.fileToolbar = QToolBar(MainWindow)
self.fileToolbar.setObjectName(u"fileToolbar")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.fileToolbar.sizePolicy().hasHeightForWidth())
self.fileToolbar.setSizePolicy(sizePolicy2)
self.fileToolbar.setMinimumSize(QSize(0, 40))
self.fileToolbar.setMaximumSize(QSize(16777215, 40))
self.fileToolbar.setIconSize(QSize(24, 24))
MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.fileToolbar)
self.editToolbar = QToolBar(MainWindow)
self.editToolbar.setObjectName(u"editToolbar")
MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.editToolbar)
self.transformToolbar = QToolBar(MainWindow)
self.transformToolbar.setObjectName(u"transformToolbar")
MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.transformToolbar)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuEdit.menuAction())
self.menubar.addAction(self.menuView.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
self.menuFile.addAction(self.actionNew)
self.menuFile.addAction(self.actionOpen)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionSave)
self.menuFile.addAction(self.actionSaveAs)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionClose)
self.menuFile.addAction(self.actionExit)
self.menuEdit.addAction(self.actionUndo)
self.menuEdit.addAction(self.actionRedo)
self.menuEdit.addSeparator()
self.menuEdit.addAction(self.actionCopy)
self.menuEdit.addAction(self.actionCut)
self.menuEdit.addAction(self.actionPaste)
self.menuEdit.addAction(self.actionDelete)
self.menuEdit.addAction(self.actionSelectAll)
self.menuEdit.addSeparator()
self.menuEdit.addAction(self.actionSettings)
self.menuView.addAction(self.menuPanels.menuAction())
self.menuView.addAction(self.menuToolbars.menuAction())
self.menuHelp.addAction(self.actionAbout)
self.menuHelp.addAction(self.actionAboutQt)
self.fileToolbar.addAction(self.actionNew)
self.fileToolbar.addAction(self.actionOpen)
self.fileToolbar.addAction(self.actionSave)
self.fileToolbar.addAction(self.actionSaveAs)
self.editToolbar.addAction(self.actionUndo)
self.editToolbar.addAction(self.actionRedo)
self.editToolbar.addAction(self.actionCopy)
self.editToolbar.addAction(self.actionCut)
self.editToolbar.addAction(self.actionPaste)
self.transformToolbar.addAction(self.actionRotateClockwise)
self.retranslateUi(MainWindow)
self.workspaceStack.setCurrentIndex(0)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"BEdit", None))
self.actionNew.setText(QCoreApplication.translate("MainWindow", u"&New", None))
#if QT_CONFIG(statustip)
self.actionNew.setStatusTip(QCoreApplication.translate("MainWindow", u"Create a new document", None))
#endif // QT_CONFIG(statustip)
#if QT_CONFIG(shortcut)
self.actionNew.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+N", None))
#endif // QT_CONFIG(shortcut)
self.actionRotateClockwise.setText(QCoreApplication.translate("MainWindow", u"Rotate Clockwise", None))
#if QT_CONFIG(tooltip)
self.actionRotateClockwise.setToolTip(QCoreApplication.translate("MainWindow", u"Rotate selected blocks clockwise by 90 degrees", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(shortcut)
self.actionRotateClockwise.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+R", None))
#endif // QT_CONFIG(shortcut)
self.actionOpen.setText(QCoreApplication.translate("MainWindow", u"&Open\u2026", None))
#if QT_CONFIG(statustip)
self.actionOpen.setStatusTip(QCoreApplication.translate("MainWindow", u"Open a document", None))
#endif // QT_CONFIG(statustip)
#if QT_CONFIG(shortcut)
self.actionOpen.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+O", None))
#endif // QT_CONFIG(shortcut)
self.actionSave.setText(QCoreApplication.translate("MainWindow", u"&Save", None))
#if QT_CONFIG(statustip)
self.actionSave.setStatusTip(QCoreApplication.translate("MainWindow", u"Save the current document", None))
#endif // QT_CONFIG(statustip)
#if QT_CONFIG(shortcut)
self.actionSave.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+S", None))
#endif // QT_CONFIG(shortcut)
self.actionSaveAs.setText(QCoreApplication.translate("MainWindow", u"Save &As\u2026", None))
#if QT_CONFIG(shortcut)
self.actionSaveAs.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+Shift+S", None))
#endif // QT_CONFIG(shortcut)
self.actionExit.setText(QCoreApplication.translate("MainWindow", u"E&xit", None))
#if QT_CONFIG(shortcut)
self.actionExit.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+Q", None))
#endif // QT_CONFIG(shortcut)
self.actionClose.setText(QCoreApplication.translate("MainWindow", u"&Close Document", None))
#if QT_CONFIG(shortcut)
self.actionClose.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+W", None))
#endif // QT_CONFIG(shortcut)
self.actionUndo.setText(QCoreApplication.translate("MainWindow", u"&Undo", None))
#if QT_CONFIG(shortcut)
self.actionUndo.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+Z", None))
#endif // QT_CONFIG(shortcut)
self.actionRedo.setText(QCoreApplication.translate("MainWindow", u"&Redo", None))
#if QT_CONFIG(shortcut)
self.actionRedo.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+Y", None))
#endif // QT_CONFIG(shortcut)
self.actionCut.setText(QCoreApplication.translate("MainWindow", u"Cu&t", None))
#if QT_CONFIG(shortcut)
self.actionCut.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+X", None))
#endif // QT_CONFIG(shortcut)
self.actionCopy.setText(QCoreApplication.translate("MainWindow", u"&Copy", None))
#if QT_CONFIG(shortcut)
self.actionCopy.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+C", None))
#endif // QT_CONFIG(shortcut)
self.actionPaste.setText(QCoreApplication.translate("MainWindow", u"&Paste", None))
#if QT_CONFIG(shortcut)
self.actionPaste.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+V", None))
#endif // QT_CONFIG(shortcut)
self.actionSelectAll.setText(QCoreApplication.translate("MainWindow", u"Select &All", None))
#if QT_CONFIG(shortcut)
self.actionSelectAll.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+A", None))
#endif // QT_CONFIG(shortcut)
self.actionDelete.setText(QCoreApplication.translate("MainWindow", u"&Delete", None))
#if QT_CONFIG(shortcut)
self.actionDelete.setShortcut(QCoreApplication.translate("MainWindow", u"Del", None))
#endif // QT_CONFIG(shortcut)
self.actionAbout.setText(QCoreApplication.translate("MainWindow", u"&About BEdit", None))
self.actionSettings.setText(QCoreApplication.translate("MainWindow", u"&Settings\u2026", None))
#if QT_CONFIG(statustip)
self.actionSettings.setStatusTip(QCoreApplication.translate("MainWindow", u"Configure BEdit", None))
#endif // QT_CONFIG(statustip)
self.actionAboutQt.setText(QCoreApplication.translate("MainWindow", u"About &Qt", None))
self.panel_libraries.setWindowTitle(QCoreApplication.translate("MainWindow", u"Libraries", None))
self.panel_document.setWindowTitle(QCoreApplication.translate("MainWindow", u"Document", None))
self.navigateUpButton.setText(QCoreApplication.translate("MainWindow", u"Up", None))
#if QT_CONFIG(tooltip)
self.navigateUpButton.setToolTip(QCoreApplication.translate("MainWindow", u"Open the containing graph", None))
#endif // QT_CONFIG(tooltip)
self.graphBreadcrumbLabel.setText(QCoreApplication.translate("MainWindow", u"Untitled", None))
self.workspaceModeLabel.setText(QCoreApplication.translate("MainWindow", u"Graph", None))
self.applyJsonButton.setText(QCoreApplication.translate("MainWindow", u"Apply JSON", None))
self.pointerToolButton.setText(QCoreApplication.translate("MainWindow", u"Pointer", None))
self.inputToolButton.setText(QCoreApplication.translate("MainWindow", u"Input", None))
#if QT_CONFIG(tooltip)
self.inputToolButton.setToolTip(QCoreApplication.translate("MainWindow", u"Add an interface input", None))
#endif // QT_CONFIG(tooltip)
self.outputToolButton.setText(QCoreApplication.translate("MainWindow", u"Output", None))
#if QT_CONFIG(tooltip)
self.outputToolButton.setToolTip(QCoreApplication.translate("MainWindow", u"Add an interface output", None))
#endif // QT_CONFIG(tooltip)
self.jsonEditor.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Component JSON", None))
self.emptyWorkspaceLabel.setText(QCoreApplication.translate("MainWindow", u"No document open", None))
self.menuFile.setTitle(QCoreApplication.translate("MainWindow", u"&File", None))
self.menuEdit.setTitle(QCoreApplication.translate("MainWindow", u"&Edit", None))
self.menuView.setTitle(QCoreApplication.translate("MainWindow", u"&View", None))
self.menuPanels.setTitle(QCoreApplication.translate("MainWindow", u"&Panels", None))
self.menuToolbars.setTitle(QCoreApplication.translate("MainWindow", u"&Toolbars", None))
self.menuHelp.setTitle(QCoreApplication.translate("MainWindow", u"&Help", None))
self.fileToolbar.setWindowTitle(QCoreApplication.translate("MainWindow", u"File", None))
self.editToolbar.setWindowTitle(QCoreApplication.translate("MainWindow", u"Edit", None))
self.transformToolbar.setWindowTitle(QCoreApplication.translate("MainWindow", u"Transform", None))
# retranslateUi