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:
8
BEdit/.vscode/tasks.json
vendored
8
BEdit/.vscode/tasks.json
vendored
@@ -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
194
BEdit/AGENTS.md
Normal 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.
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|
||||||
|
|||||||
5
BEdit/src/bedit/core/__init__.py
Normal file
5
BEdit/src/bedit/core/__init__.py
Normal 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"]
|
||||||
32
BEdit/src/bedit/core/libraries.py
Normal file
32
BEdit/src/bedit/core/libraries.py
Normal 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]
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|
||||||
212
BEdit/src/bedit/data/libraries/example.json
Normal file
212
BEdit/src/bedit/data/libraries/example.json
Normal 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": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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",
|
|
||||||
]
|
|
||||||
1
BEdit/src/bedit/gui/__init__.py
Normal file
1
BEdit/src/bedit/gui/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Qt user interface and application adapters."""
|
||||||
@@ -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:
|
||||||
5
BEdit/src/bedit/gui/controllers/__init__.py
Normal file
5
BEdit/src/bedit/gui/controllers/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Qt-aware application controllers and undo commands."""
|
||||||
|
|
||||||
|
from bedit.gui.controllers.document import DocumentController
|
||||||
|
|
||||||
|
__all__ = ["DocumentController"]
|
||||||
@@ -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):
|
||||||
@@ -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):
|
||||||
1
BEdit/src/bedit/gui/dialogs/__init__.py
Normal file
1
BEdit/src/bedit/gui/dialogs/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Application dialogs."""
|
||||||
@@ -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):
|
||||||
@@ -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
|
||||||
@@ -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):
|
||||||
1
BEdit/src/bedit/gui/generated/__init__.py
Normal file
1
BEdit/src/bedit/gui/generated/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Generated Qt code. Do not edit these modules by hand."""
|
||||||
@@ -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):
|
||||||
5
BEdit/src/bedit/gui/graphics/__init__.py
Normal file
5
BEdit/src/bedit/gui/graphics/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Graphics scenes, views, editors, and renderers."""
|
||||||
|
|
||||||
|
from bedit.gui.graphics.workspace import GraphWorkspaceView
|
||||||
|
|
||||||
|
__all__ = ["GraphWorkspaceView"]
|
||||||
@@ -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:
|
||||||
@@ -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:
|
||||||
@@ -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"
|
||||||
@@ -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):
|
||||||
1
BEdit/src/bedit/gui/models/__init__.py
Normal file
1
BEdit/src/bedit/gui/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Qt model/view adapters and repositories."""
|
||||||
31
BEdit/src/bedit/gui/models/library_repository.py
Normal file
31
BEdit/src/bedit/gui/models/library_repository.py
Normal 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)
|
||||||
@@ -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
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
from bedit.library.repository import LibraryRepository, default_library_paths
|
|
||||||
|
|
||||||
__all__ = ["LibraryRepository", "default_library_paths"]
|
|
||||||
@@ -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))
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
from bedit.workspace.view import GraphWorkspaceView
|
|
||||||
|
|
||||||
__all__ = ["GraphWorkspaceView"]
|
|
||||||
|
|
||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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
450
BEdit/ui/main_window_ui.py
Normal 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
|
||||||
|
|
||||||
Reference in New Issue
Block a user