added stub simulation entry and logs

This commit is contained in:
2026-07-20 15:26:18 +02:00
parent 495edd42f4
commit 9ed8e371ef
20 changed files with 1047 additions and 123 deletions

View File

@@ -24,7 +24,8 @@ src/bedit/
│ ├── 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
── libraries.py # Library file discovery and parsing
│ └── simulation/ # Qt-free simulation service and compiler/runtime code
└── gui/ # All Qt-dependent code
├── app.py # QApplication startup and palette
├── main_window.py # Top-level UI orchestration
@@ -91,6 +92,9 @@ Keep the root of `src/bedit` minimal. New UI modules should go into the relevant
- 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.
- Application-wide messages use `core.application_log.get_logger()`. The main
window installs the Qt log-panel handler; core code must only use standard
Python logging and must not import the GUI handler.
## Qt Designer and generated files
@@ -127,6 +131,9 @@ pyside6-uic --from-imports ui/icon_editor_dialog.ui \
pyside6-uic --from-imports ui/text_definition_editor.ui \
-o src/bedit/gui/generated/ui_text_definition_editor.py
pyside6-uic --from-imports ui/simulation_settings_dialog.ui \
-o src/bedit/gui/generated/ui_simulation_settings_dialog.py
```
When adding a promoted/custom widget in Designer, its header must use the real

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,5 +1,7 @@
<RCC>
<qresource prefix="icons">
<file>icons/run-build.png</file>
<file>icons/preferences-system.png</file>
<file>icons/draw-triangle.png</file>
<file>icons/draw-ellipse.png</file>
<file>icons/draw-circle.png</file>

View File

@@ -215,6 +215,129 @@ ff\xa6\x5cP`\xb7\xadY\x1b\xde\x8b\xcb\x97/\xc3\
\x80\x90\x84\xfe\x83\xfe\x0fl\x00\x00/\xbf\xf2\xf2\x7f\x9c\
\xfd\x0bVu/\x8d\xbcS'\xfc\x00\x00\x00\x00IE\
ND\xaeB`\x82\
\x00\x00\x07\x88\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\
\x9bx\x00\x00\x00\x07tIME\x07\xd9\x08\x0b\x00-\
\x1f\xb3\xb6|\x9d\x00\x00\x00\x06bKGD\x00\xff\x00\
\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x07\x08IDATx\
\xda\xbdWiLTW\x18\x95\xda\xa0P1]b\x89\
\x0a.EQ\xdcj\x5c\xd3FcMj\xda\xa4I-\
?J4\xc4(1\xadMl\xd5\x22*h,\x88K\
\xd0b+\xa6\x15Y\x0dV\x94\x08U\x16A\x10\x18`\
f`\x18\x96a\x1f\x86a\xd8\x94}\x11T\x90\xa0v\
N\xefy\x99\x99L\xa8\x88&\xd6\x97|\xb9\xf7\xdd\xf7\
\xbes\xce\xb7\xdc\xfbf&\xbc\xea\x15\x10\x10\xc0a\xa2\
\xb0\x0f\xd7\xaf_\xbfx\xcd\x9a5\x8b\xc5\xdcQ\xd8\xdb\
\x9e\x9e\x9e\x13\xde\xc4\xf5\x96L&\xd3<z\xf4\x08C\
CC\x92q\x9e\x9e\x9e\xae\xe6\xb37!`jbb\
b\xdf\xc8\xc8\x08\x86\x87\x87%\xe3\xfc\xc6\x8d\x1b\xbd|\
\xf6:\x89\xec\x97/_\xfe\xc9s\xd6?\xc8\xcc\xcc\x1c\
z\xf2\xe4\x09\x89%\xe3<##c\x88\xcfF\xbf\xbc\
z\xf5\xeaO\xc5`\xf7\xaa\xe4\xd3\x93\x92\x92\xaa\x9e>\
}\x0a__\xdf\x10\xa6\x965\xde\xbcy\xb3\xfb\xb5k\
\xd7\xd2\xef\xdf\xbf\x8f\xc1\xc1AK\x098\xef\xeb\xebC\
\x5c\x5c\x5c\xba\xbb\xbb\xfb7|W\x98MHH\xc8\x9f\
\xcf\x9e=CUU\x95\x8e\x98/K\xee\x94\x9b\x9b\xdb\
A\xd0\x81\x81\x01#\xeb\x1b\x15\x15\x95\xabP(\x1a\x19\
-\xd7\x1f>|\xf8\x5c\xe3\xb3\xc7\x8f\x1fC\xa9T6\
\xc4\xc7\xc7\xcb9'F\x7f\x7f?t:]\x0f\xb1\xc7\
\xad\xef\xcd\x9b7k\xe8\xd0\xdb\xdbKcd\x12\xb8X\
32ra\x9c\x8f6\xae[\x9bQ\x10K\xfe==\
=\xe8\xec\xecDww7D`\xcc\x84\xc3\x98\xecK\
\x96,\xf1lllDGG\x87\x91Nt\xb6\x16B\
#\x01\x05\xb5\xb7\xb7\xa3\xa6F+\x22\xd3\xa2\xad\xfd\x1e\
\x06\xfa-\x844\x89\xb0\xab\xabKz\xef\xee\xdd\xbbh\
ii1655\xc1\xcd\xcdm\xdb\x8b20\xcd\xcb\
\xcb\xab\x90\xce\xad\xad\xad\x14B \x8b\x08FZZZ\
\x0a\x95J\x85\xb2\xb22\x18\x0c\x06\xd4\xd7\xd7\xa3\xb4\xbc\
\x14\x05\xea<\xa85\x0atuw\x8a\x88\xbb\x18\xb5\x85\
\x9cAq\xdc\xb1c\x87\x8a\x1c\xe3\x95a\x9e\xbf\xbf\x7f\
3\x05\xd0\x89 \x8c\x88\xf3\xc2\xc2Bid$4\x02\
744\x083P\x8c4\x97\x17fA\xdf(\xb2\xd2\
\xd6\x86{\xf7\xeeI\xebuuu8r\xe4H3\xb1\
_\xa6\x09\xa7\x5c\xbdz\xb5\xddD\xc2\xd4ID\x15\x15\
\x15\xbc7Y\xb3467\xb7H\xf3\xc6\x86F)\x13\
z\xbd^\x90\xe9Q\xa4\xc9G\x9dAKA\xa2L5\
\x92oddd\xab\xc0~g\x5c\xf6\x0d\x1b6\xec2\
\xa5\x96\x00\x82\xa4\x99\x913\x0a\x0a1E\xde\x84\xd4\xcc\
X\xc4\xa7\xfe\x86\x84\xb4\xb3H\xbc\x1d\x05\xadVKr\
v\xbb4W\x15\xcbQ\xab\xd3J\xa5***Be\
e%\xc4\x99\xb0}<\xfe\x89'O\x9eT\x92\x8c&\
\x84P=\xf7\xb1\x14I\xbd\xbe\x1e\x9a\xb2b\x84_\xff\
\x19%\xf510t\xa7\xa0\xb1/\x0d\xd5-q\xf8+\
\xc5\x07\xd99)\xa8\xd3\xd5I\xefj4\x1a\xa8\xd4\x0a\
\xa8\x0b\xd5(..Fuu5\x04v\x1e9\xfes\
\xe2\x89\xed\x91#\x946\x09\xa5\x9d\x8c\x80/3\x0a\xa6\
T\xacS\xbdiM\x87#\xe77BYw\x1a%\xad\
\xe7\xa0\xeb\x8f\x82\xfe\xc1%Tt\xfc\x81\x82\x86_q\
\xe2\xd2zA\x5c\x86\xf2\xf2\x0a\xa9Q\x15\xca<\x8e\x14\
#\x05Q[[\x0br\x88\xfb&\xb9\x5c\x9eCn\x0a\
X\x91\x97':X\xadFII\x89p.\xb7DL\
1\xeczf\xa1\xba\xaa\x1a\x97\xaf\x85 8\xcd\x09\x19\
\xb5\xbb\x91\xd3\xbc\x07\x8a6\x1f(\xdb}\x90{w\x1f\
\xb2\xf4?!<\x7f\x11\xceE\xee%\x0e\xf7<\xb2\xb3\
\xb3I\xce\x00\x88Il\xe2Q\x04\xf2\xf3\xf3!\xb8?\
\xa6\x80\x8dB\x0d\xeb\xccT\xf1\x05\xb3\x08\x0a\xb0\x88\xaa\
\xa9\xd6\xc2\xffw\x0f\x9cW\xccDh\xd12$h\xbf\
FR\xbd\x07\x92\x0c\x1e\xf8[\xe7\x8e\xb0\xd2U8\xa7\
t\x82\xef\xb9\xb5P\x15\xa8q\xe7\xce\x1d\xe4\xe4\xe40\
\x10f\x8fAP\x0c9$.r\x0a\xee\xcf(`\x93\
\xf8\xbc\xf2\xf8d\xba\xa8\xce,\x82}`\xb9\xaf\x16\x02\
|\x83\xbeBH\x9e3N\xcb\xe7\xe0x\xfe\x1c\x04\xaa\
\x5c\x11X\xe0\x8a\x00\xe5\x5c\x9cR|\x84`\xf9l\xec\
\x0dZ,\xa2W0\x03\x8c\x92e\x1cM\xceu\x8a\xa3\
\x80\xcf)`mrr2\x15Q\x849\x13t\xa0z\
\xf6\x00\xef\x05H%\x02\x82\xbeGP\xea\x0c\x9c\x92\xcd\
\xc5Q\xd9<\x1c\x92-\xc0A\xd9B\xf8\xc9\x84\x88l\
\x17\x04\xcbf\xe1\x07\xdfu(((\x14V@_\xf6\
\x12Gf\xd2BN\xae\x94\x94\x14\x0aXC\x01\xb3\x8e\
\x1d;\xd6\x1e\x1d\x1d=\x12\x1b\x1b\xfb\x0f\xb3`]\x0e\
\x8a\xe0=\xe7\xf1\x09\xd7\xe1\xf5\xcb\xbb\x08\xca\x9a\x83\xc3\
\xe9\xf3\xb0/m!~Lu\xc3\xfe\xb4\x05\xf0\xcf\xf8\
\x08\xde\xe1\xd3p2\xe8(\xfb\x89Fr\xfaY\x22'\
69\xc8%8\xdb\xc8=\xc1\xd6\xd6\xd6\x86\x13a+\
\x99\x92C\x87\x0e\x19\xa8\xd2\x5c\x0e\xa6P|\x09%\x00\
\xb5\xb8\xf7>\xe8\x85\xefN\xbc\x8fc\xb7f\xc3\xe7\x96\
+v'\xbb\xe1p\xaa\x10\x13:\x0d\xdbwmb\xea\
\x05y\x11\xb3\xc9\xb3\xc4B\xce\x8c\x08\xeczr\x90\x8b\
\x9c\x93'O&\xb7\xd5\xc5'+W\x1e\xa7\x83UO\
0\x0b\xe2>_\x02J\xcfH\xc3A\xbf=\xf8v\xe7\
Rx\xfa\xcc\xc06\xdf\xe9\xf0\xd8%2\xb1\xcf\x0bI\
\xc9\x89\xa2Wj\xe8\xc3\xc6\xb3\xae\xb9\xb4\xb6b\xc5\x8a\
\xe3/s\x14\xbb^\xbcx\xf1\x81Pl\xa43\x1d\xcb\
+\xcb\xa0P\xcb\x90\x9d\x95-uwrR\x12\x22\xa2\
\xc2\x10|6H\xd8i\xc4\x5c\x8eFn\x9e\x8c\xa4\xdc\
r\xdc=l`\xd6\x9d\xa5` \xc6\xd0\xd0\xd0\x07\xc4\
~\x19\x01\xb6\xde\xde\xde\x1a6\x0a;\x95\xc6]\x92)\
O\xc6-\xc5U\xdc\xbe\x9d\xce4\xf3\x84\x93\x1aU\xda\
\xb2\x15U\xect6\x9b\xf9\xf8f\xfa\xf9\xcc\xd2\x0f\x07\
\x0e\x1c\xd0\x10{<r\x9b\xfd\xfb\xf7\x9fa\xbdx\x88\
\x90\x98\x87\x14S\xc8\xba\x96jJ\xa07h\x19!\xc1\
\xcd\x8d\xca3\x83\xc76?\x5c\xec\x19\x9a\xf9\x9b`\xf9\
|\xf3]??\xbf3\xe4\x18\x93\xdd\xc5\xc5\xe5K:\
1\xfdt2\x9d\x8e\xe6H-\xdf\x05\xa6\xd9\xfa\xe3D\
B\x1e\xb3\xd6f}\x00\xb1\x8c,\x03\xb1\xe7\xcf\x9f\xff\
\xc5\x8b2\xb0L\xd4\xaa\x8f\xc4\xa6\xfa3\x02\xb3\x00#\
\xc1L\xc7*\x8d\x04c\x99\x91#E\xd3\x97\x18\xc4\x14\
\xbd\xd5K\x8e1\xd9\x1d\x1c\x1c8\xb8'$$\x0c\x0b\
'Fod4b\xcf\xf6\x84\x87\x87\x0f\xb2\xb6\xcf#\
\xb7^\xe3;\x11\x11\x11\x83\x81\x81\x81=\xa6o\x89\x91\
X\xe2G\xea0\xb1\xa7N\x1d\xe7\xaf\x83\xbd\xbd=\x7f\
\x82{\x84\x85\x85\xddgCm\xd9\xb2\x85?\xa3\x96\x0a\
[\xe5\xec\xec|A\xfcLo1\xa5w\xb4\xb1\xc6\xcd\
NNN\x17\xf8.}\xb6n\xdd\xaa\x22\x06\xb1\x889\
e\xca\x14b\x8f\x7f\xd9\xd9\xd9qX\xe7\xe8\xe8xB\
\x8c3\xad\x9bT\xd8\xa2+W\xae<\xb2\x16\xc19\xd7\
\xf8lT\x93\xcd$\x06\xb1\x88\xf9\xba\xae\xf7bbb\
\xbaX\x1a6\x15\x8ds\xae\xf1\xd9\x847p\xd9\x07\x07\
\x07k\xb9\x03\xf8\xb3\x9b\xc69\xd7\xf8\xec\x7fg\x9f4\
i\x92\x8d\xe9,\xdf)\xcc\x87\xc69\xd7\xf8]yU\
\xbc\x7f\x01\x06A\xf8\xf9\xb2\x87\xf2i\x00\x00\x00\x00I\
END\xaeB`\x82\
\x00\x00\x05\x08\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -1360,6 +1483,144 @@ $&\x93\xc8\x7f\xb3\xe7\xcc*\xc2w\xf1nHG\x09\
\xf9\x5c\x8a\xda\xed\x0c\xf6t\xf6\xe1\xf9-'\x97\x16\xdc\
5\x03\xef$\x18\xdea\xfc\x07;\x00\xdf\x09\xb1\xdb|\
\x84\x00\x00\x00\x00IEND\xaeB`\x82\
\x00\x00\x08{\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\
\x00\x00\x00\x09pHYs\x00\x00\x03v\x00\x00\x03v\
\x01}\xd5\x82\xcc\x00\x00\x00\x19tEXtSof\
tware\x00www.inksca\
pe.org\x9b\xee<\x1a\x00\x00\x07\xf8ID\
ATx\xda\xb5W\x0bX\xcdi\x1a\xff,\xd5\xa2\xc1\
\x8e\x8ad<\xe52.\xab\xc9\x0e\xe5t*a\x0c!\
\x95\xa2\x9abZ\x97U\xf3<\x96M3I5\x83\xd5\
E24\x88RjY\xc3&\x0c]\xb6D\xd3\x95t\
t\x8fD[\x9d\xee\xb7\xa3\x9e\xd2)\xba\xfd\xf6\xfdR\
\xf3\xa4Q\x1d\xb3\xcf~\xcf\xf3{\xce9\xdf\xff\xfd\xfe\
\xef\xef{\xef\x87\x01\xf8\xbf\x80\xd6(\x82\x02\xe1\x03\xc2\
x\xc2\x18\xbe7X\xae_x\xf4)\x7f\xff\xd5\xf49\
\x8e\x0b\xfd\x8f\x8a\x7f\xa7\xac\xac\xac\xe8\xe5\xe5e\x1b\x15\
\x1d\xf5$;;\xbb;//\xefUVvV\xd1\xd5\
\xabWWr]o\xcb\xd3\xc6\x95+W\xdc_\xbe|\
\x89R\xb1\xf8UXx\xf8~\xbe\xf7[o=g\xce\
\x1c\xe5\x90\x90\x90\xc2\xfc\xc7\xf9(+/Cuu5\
jkk!\x91HPRZ\xd2q\xe8\xd0\xa1\xf5\x03\
/\xc9\xd7\xa4\xe2\xe2\xe2\xd7\xaf;:P]S\x83\xb4\
\x87\x0f{\xec\xec\xec\xcc\xf8M~\x03\x81\xdf\xef\xdd\xbb\
\xf7\x87\xb4\x87i\xa8\xac\xaaD\x83\xa4\x01\x12Btt\
\xf4kWW\xd7\xeaE\x8b\x16]TTT\x9c\xcf\xdd\
\xf1\x16\x813g\xce\xdc\xae\xac\xac\x84\xb8\xbc\x1c\xa2G\
\x8f`\xef\xe0\xd0\xbax\xf1\xe2\x19\xef\xeb\x0eZ\x93\x8f\
\x1f\xf7-\xab\xa8(\xefU\xdc\xda\xda\xca-\xd0\xad\xa4\
\xa4\xf4\x1d=[F\x98\xceI\x0e\xb6\xc0\x18\xc2\xbc\x8b\
\x97.\x15\xdf\x89\x8b\xebIJNF[[\x1b6[\
Z\xa5\xd3\xfe\x04YIp9\x15\x15\x95YQQ\x91\
\xdd\x9c@SS\x13\xda\xdb\xdbPZZ\xfa\x8a\xbf\x9f\
\xa00\x5c\x10\xf2\x87\x0bW\xafYs\xd6v\xeb\x97\xad\
\x05\x05\x05\x90\x12\x89\x8d\xe6\x16Q\xea\xea\xea\x93d!\
AK\xce\xd2\xd2\xd2.%%\x19999\x90\xbc\x90\
\xf4\x92\xa8\xab\xaf\x83\xa1\xa1\xe1\x12\xee\xd2\x01\xd91\xa6\
?+\xdez\x01a\x9a\xbc\xbc\xbc\xf9\xe7\xab\xd7\x14<\
\x7f\xfe\xbc\xd7\x84\xebML\x13\x96-[\xa6<\x12\x09\
Z\x8a\xbbw\xef\xfe\xd7\xdd{q\xc8\xc8\xc8@Uu\
\x15\xaa\x09\x15\x15\x15\xb8r\xf5\xc7V\x1b\x1b\x9b5\x5c\
\xe6\xb0\xc7\xe1\x8d\xc1\xc1A\x05_\xd1\xe2:\x7f\x95B\
\x84\x89\xdc_\x02]afIi)\x9a\x9b\x9bac\
\xfbe\xba\x85\x85\xc5\xd4\xe1\x02\x93\xd6\x94s\x01\xe7Z\
\x92\x92\x93\xb8\xd9\xf1 \xed\x01\x0a\x0b\x0b\xf1\xecY!\
\x92S\x92p\xd2\xef\xfb\xce\xd8;\xb1\x92\xd4\xd4T\xf8\
\xfa\x1ek\xd7\xd1\xd1\xb1\xe1\xf10T\x01\x19O\x10\x0a\
\x04\xba\xa9t\x032c=\x1c\xbe\xda\x9d\xbfk\xd7\xae\
\x19\xef\x22\xc1\xd3\xd6\xca\xca\xca\xfa\xe6O7\x10p>\
\x08!\xff\x08\x81\xe7QO\xf8\xf8\xfa\x22+;\x93\x02\
;\x1d!\xa1!\xe0\xca\xc3\xc2\xc2(\xc8\xed\xcb\xe9\xcc\
,\xfe\xae\xe1\xaa\xd88\x82\xb6\xbe\xbe\xc1\xdd\xba\xbaz\
TP\x968\xefw)rvv\x9e3\x98\x04\x0f\xd6\
\xc3\x87\x0f%\xe4\xe5\xe5b\x87\xbd=\x5c\x5c\xbf\x81(\
\xe3!\xf4\xf4\x84\xa4\x5c\x84;q\xb1\x08%R\x91\x91\
\x11\xf0\xf4\xf4\xe8\xa6Z\xf15?\xc3\xcf\x8eTJ\xc7\
\x12\xfedh\xb8<\xb2\xb1\xb1\xb1\xa7\xb8\xa4\x04\x01\x81\
\x81\xe5TL\x16\x0e\x08*y\xf2\xfd\x0e\xba]\x17\xcf\
\x9e\xa0\xe0\x0b\xd8ha\x86\xfc\xc7\xb9ptr\xc4\x8d\
\x9b7@\xae\x81\xb7\xb7\x17\xdc\xbfu\xeb\xd6\xd4\xd4\xfc\
\x91\xceh\xf4\x17;Y\xeb\xb9\xe6\x8a\x15+\xc3ZZ\
Z\xba\x9f\x17\x15!]$\xaa;q\xe2\xc4\x12n%\
r\x8b3/\xb7\xe1\xe1\xd7\x90\x90\x90\x80\xb3g\xcf\xc2\
\xda\xfa\x0bxy{\x83d\xe0\xe6\xee\xd6\xe3\xe8\xf8\xb7\
\xd7\xdb\xb6o\xab\xa7\x9b\x1f\xa33\xdc\x82r\xfd:d\
-0\xf2\x84\x05\xe6\xe6\xe6\x17\xa5Ri\xd7\xe3'O\
@\xf5\xbd\xe9\xe0\xc1\x83\x17sss{\xbc\xbc<\xb1\
b\xe5r\x1c8\xe0\x82\xd3\xa7O\xc3\xc5\xc5\xa5\xd5\xd6\
\xd6\xf6\xa9\x96\x96\xd69:gM\xd0%\xcc$|\xd8\
_\x05e%08M\xe7n\xd9\xb2%\x80\xfaFg\
Nn.'\x01\x0fO\x0f,_n\x88\xcd\x9b7\xe1\
\xf2\xe5\xcb \xc5i$\xb7\x81\xf01aJ_7\x94\
\x1f*\x8d\x07m\xc8d\x89\x85\xdb\xb7o\x7f\xc2\x8bL\
Ff\x16\xe5|&v\xd9\xef\x02u:888\xe4\
\xd3s\x01W*k/y\xdf\xfe\xae\xe8\xed\xed}\xf3\
\xde\xbd{\xe4g+P\xff\xa0xx\xd4K\xc2\xc7\xc7\
\xa7!1\xd4\xf2N\x84\xbb\xfaw\x03\xeb\xfdH\x90Y\
\xf9$Z\xd4\xb4\xe2\xf2\xf3\xf3\xb1u\xab-\x96\x0a\xb4\
\xa1o\xa0\xcf\x8b\x0e\xa5Z\x06\xca\xf2\xfcz\xa4e!\
(\xba\xa0\x88\xa0\x1dl_\x7f\xa0\x8d\x04\x99\x06\x0c\x0a\
&\x15\xea\xf1\xf7333a\xfd\x85\x15\x0c\x0c\xf5\xa1\
\xa5\xf5\x09\x8e\x1d;\x06rG\x9b\xf8\xd1\xdf;:\xcb\
>D\xdb\xd3y\x90\xde\x1c\x0f\x91\x1fk\xf8h2S\
\x93\xc5\x0a#*\x17\x08\x04j\xd7\xae]\xcbNII\
\xc1\xa6M\xe6tk=\xcc\x9f?\xbf7\xc5N\x9e<\
\xd9\xeaj16\x5c|\x95u7\xddg\xe8\x8egx\
\x95\xccP\x13\xcb\xf0\xef#,\xf2\xaf\x9f\xb1\x05\xdc\x12\
\xc3\x11\x19V\xf9\xaaU\xabf\xd20\xf1466\x96\
:\xa3\x19\xf4\xf4u1k\xf6LP\xfa!((\xa8\
\x9d\x88\xec\xfb\x5c\x8b9\xdc\xfe\x9a\xb5\xd7]fh\x89\
c\xe8,a\x90\x1604g04\x8a\x14\xba\x13}\
\x14\xc3\x94\x95\x99\xe2P$\x86Tnjj\xba >\
>\xbe4<<\x1c\xa6f&\x10\xea\x090c\xc6G\
\xd8\xb3g\x0fO\xb7\x0e===\x1elj\x04\xd5y\
j\xcc\xde\xc7\x96\x15\xfd\xec3\xaaK\x92B$\xc4\x0c\
]\xe5\xa3\xd1.6AQ(C\xe4>\x16MM\xfd\
\x9dm\x9d\xbd\xab\xb1P_\xff\x94JkuH\xc8\x05\
l05\x86@\xb8\x14jj\xd3(\xf8\xb6\xe2\xfa\xf5\
\xeb]FFF\xa7Hn\x06\x97\xed\xeb\xa0\xfc\xe5\x9a\
\x1fOc.\x0fN\xb2\xce\x17\x0f\x88D\xc5\x04t\xd4\
\xedEC\xb2\x12\x8a\x7f\xea%\xf1d\xc9\x5c\xa64\x98\
\xc4\xaf\x94S\xa11\x10\x89D\x12\xbf\x1f\xfc`l\xb2\
\x9e\xa2]\x07SU\xa7\xc2\xd8\xd8\x18QQQ=D\
\xee2\xefd\x83+\x1a\xffM\x98\xe9m\xcd\xd2\x8b\xc9\
\x1d\x0d\x89\x14\x0f\xc5c\xd1Qk\x0bi\x8e\x12\xc41\
\x14\x17\xfbX\xa9\xd9\xa7Lu`\x8dxK9\xd5\xf5\
\xd5T\xd7\x9b=<<\xb0~\xc3:\xe8,]\xd2\xab\
\xdc\xc0\xc0\x00111=;w\xee\x8cf\xecM`\
\x0d5\x94N\x99\xc8V\x86\xda\xb3\x17\xff\xf9'C9\
\xdd\x5c\x92\x22\x87\x8ej#H\xb3'\xa3\xe2.C\xcc\
7\xac\xc6Z\xc0\xd4\xfbI\xfc\xa2\x9c\xa6Y3\x1a\xc5\
Z\x1f\xd1Pz\xfc\xfb\xe3\x10\x0au1e\x8a\x0ah\
\x92\x05\x0fB''\xa7\xfb\xbc3\x12\x14F(V\x13\
\xc7)0\x93\xf3;Y}m\xfc\x1b\x12\xf5?\x8fF\
G\x956\xa4Y\x7f@u\x02\xc3\xdd\x03\xec\x85\xfb\x06\
\xf6GN\xa2\xf7\x90\x9a\x9a\xdat\xba\xb9\xd4\xd3\xd3\xf3\
\xd9\xbau\xeb\x22)\xbd\x1a]\x5c\xf6\x83\xba\x17\x22\x22\
\x22@\xed\x97\x97X\xa1\x0c\x7f\x5c\xfaILR\x18\xc3\
V\xfb\xff\x99U\xd5\x10\x89\xca\xdb\x94\x9a1\xa3\xd0Y\
9\x1b\xd2LE\xd4\xa52dy\xb3\x12^Y\x09L\
\xde\xcd\xcd\xcd\x8e\x22\xbe\x89\xbe/'\xcc^\xbbv\xad\
3\x99\x9c\xfb\x9c\xe7{)\xed}F\xf8\xe0}&\xe4\
\xbe\x89z\x85\xdf\x16VR\x15K$\x22\xde\xa0\xb3\x5c\
\x09\xcd)\x0a\x90\x5cb\xed}\xcd\x8a\x8d\xa5?\x0d\xdf\
\xc6\xc5\xc5\xb5ihh\xcc\xe5&vtt\xdc\x96\x98\
\x98\xd8\x13\x18\x18X\xab\xa0\xa0`\xc2\xd8\xe0\x14\x92\xbd\
w\x10\xf4\x8fnf\x85\xe2(\x86\xaah\x06\xa9\x88\xa1\
=i4\xe2\x8f\xb2\x87\xf4L\xa9\xd7\x02rrr\xda\
4\xab\xb5\xdf\xbau\xab\x90>\x03(\xd5jN\x9d:\
\xd58a\xc2\x04[.$cg\x1bn\xbe\x14\x1c1\
g9\xf9~\x0c\xe2\xf3\xac'\xdd\x8fU\xd0\xdeb\xae\
\xbb_HE(\x14\xfa\x04\x07\x07\xb7\xf8\xfb\xfb\xb7\x91\
\xcf\xc5\xaa\xaa\xaa\x7f\xe1&\x92Q\xb9,\xa3\x9d\xa6\xb6\
\x06s\xb2\xd6cG\xe8\xfb'}{\xa3~\xc9\x02\xc2\
t\x82\x11a\x13a)a\xb2L\xcae'!\xc7]\
\xc91\xb0?\xfc\x17\xb0l\xc1\xc4\x9c\xd3\xd1\xfc\x00\x00\
\x00\x00IEND\xaeB`\x82\
\x00\x00\x04+\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -2271,6 +2532,10 @@ qt_resource_name = b"\
\x00z\
\x00o\x00o\x00m\x00-\x00o\x00r\x00i\x00g\x00i\x00n\x00a\x00l\x00.\x00p\x00n\x00g\
\
\x00\x0d\
\x0f\xa5%\xc7\
\x00r\
\x00u\x00n\x00-\x00b\x00u\x00i\x00l\x00d\x00.\x00p\x00n\x00g\
\x00\x0f\
\x0bb\xb8g\
\x00d\
@@ -2327,6 +2592,11 @@ qt_resource_name = b"\
\x00e\
\x00d\x00i\x00t\x00-\x00r\x00e\x00d\x00o\x00.\x00p\x00n\x00g\
\x00\x16\
\x01p\xe1\x87\
\x00p\
\x00r\x00e\x00f\x00e\x00r\x00e\x00n\x00c\x00e\x00s\x00-\x00s\x00y\x00s\x00t\x00e\
\x00m\x00.\x00p\x00n\x00g\
\x00\x16\
\x0c\xb7\x89\xc7\
\x00d\
\x00r\x00a\x00w\x00-\x00b\x00e\x00z\x00i\x00e\x00r\x00-\x00c\x00u\x00r\x00v\x00e\
@@ -2383,59 +2653,63 @@ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1a\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1c\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x01\xde\x00\x00\x00\x00\x00\x01\x00\x00G\x5c\
\x00\x00\x01\xfe\x00\x00\x00\x00\x00\x01\x00\x00N\xe8\
\x00\x00\x01\x9f{C\xf1'\
\x00\x00\x02\x9a\x00\x00\x00\x00\x00\x01\x00\x00dK\
\x00\x00\x02>\x00\x00\x00\x00\x00\x01\x00\x00Z\x1a\
\x00\x00\x01\x9f\x7f\xa8\xa8)\
\x00\x00\x02\xec\x00\x00\x00\x00\x00\x01\x00\x00tV\
\x00\x00\x01\x9f{0\xc99\
\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x00<\xa6\
\x00\x00\x01\xc4\x00\x00\x00\x00\x00\x01\x00\x00D2\
\x00\x00\x01\x9f\x7f&\x83\xcd\
\x00\x00\x01\x84\x00\x00\x00\x00\x00\x01\x00\x004\xbe\
\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x00<J\
\x00\x00\x01\x9f{C\xf1\x18\
\x00\x00\x03R\x00\x00\x00\x00\x00\x01\x00\x00}\x9f\
\x00\x00\x03\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x8d\xaa\
\x00\x00\x01\x9f\x7fY\xceg\
\x00\x00\x02P\x00\x00\x00\x00\x00\x01\x00\x00V\xbd\
\x00\x00\x02\xa2\x00\x00\x00\x00\x00\x01\x00\x00f\xc8\
\x00\x00\x01\x9f\x7fV\xd5\xc0\
\x00\x00\x03\x08\x00\x00\x00\x00\x00\x01\x00\x00t\xba\
\x00\x00\x03Z\x00\x00\x00\x00\x00\x01\x00\x00\x84\xc5\
\x00\x00\x01\x9f\x7f&\x83r\
\x00\x00\x02|\x00\x00\x00\x00\x00\x01\x00\x00]P\
\x00\x00\x02\xce\x00\x00\x00\x00\x00\x01\x00\x00m[\
\x00\x00\x01\x9f\x7f&\x83\xb1\
\x00\x00\x00\xfc\x00\x00\x00\x00\x00\x01\x00\x00$|\
\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x00,\x08\
\x00\x00\x01\x9f\x7fY\xce\x82\
\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x00C\xdc\
\x00\x00\x01\xe0\x00\x00\x00\x00\x00\x01\x00\x00Kh\
\x00\x00\x01\x9f{C\xf1.\
\x00\x00\x01d\x00\x00\x00\x00\x00\x01\x00\x00/\x10\
\x00\x00\x01\x84\x00\x00\x00\x00\x00\x01\x00\x006\x9c\
\x00\x00\x01\x9f{{\xa5\xd5\
\x00\x00\x03x\x00\x00\x00\x00\x00\x01\x00\x00\x81\xdf\
\x00\x00\x03\xca\x00\x00\x00\x00\x00\x01\x00\x00\x91\xea\
\x00\x00\x01\x9f\x7f&\x83\x10\
\x00\x00\x00\xde\x00\x00\x00\x00\x00\x01\x00\x00 \xd9\
\x00\x00\x00\xfe\x00\x00\x00\x00\x00\x01\x00\x00(e\
\x00\x00\x01\x9f\x7f&\x83~\
\x00\x00\x02\xe4\x00\x00\x00\x00\x00\x01\x00\x00p\xd7\
\x00\x00\x036\x00\x00\x00\x00\x00\x01\x00\x00\x80\xe2\
\x00\x00\x01\x9f\x7f&\x83T\
\x00\x00\x00^\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xc1\
\x00\x00\x00~\x00\x00\x00\x00\x00\x01\x00\x00\x14M\
\x00\x00\x01\x9f\x7fY\xce^\
\x00\x00\x00\x82\x00\x00\x00\x00\x00\x01\x00\x00\x11\xcd\
\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x19Y\
\x00\x00\x01\x9f{0\xc9B\
\x00\x00\x01D\x00\x00\x00\x00\x00\x01\x00\x00,\xb6\
\x00\x00\x01d\x00\x00\x00\x00\x00\x01\x00\x004B\
\x00\x00\x01\x9f\x7f&\x83)\
\x00\x00\x02\xc2\x00\x00\x00\x00\x00\x01\x00\x00kU\
\x00\x00\x03\x14\x00\x00\x00\x00\x00\x01\x00\x00{`\
\x00\x00\x01\x9f{C\xf1N\
\x00\x00\x02\x1e\x00\x00\x00\x00\x00\x01\x00\x00R\x8e\
\x00\x00\x02p\x00\x00\x00\x00\x00\x01\x00\x00b\x99\
\x00\x00\x01\x9f\x7f&\x82\xf2\
\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x1a9\
\x00\x00\x00\xd0\x00\x00\x00\x00\x00\x01\x00\x00!\xc5\
\x00\x00\x01\x9f{\x8d\xf34\
\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x9f{0\xc9+\
\x00\x00\x01\xfe\x00\x00\x00\x00\x00\x01\x00\x00J\xbc\
\x00\x00\x02\x1e\x00\x00\x00\x00\x00\x01\x00\x00RH\
\x00\x00\x01\x9f{C\xf1=\
\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x05\x86\
\x00\x00\x01\x9f\x7f&\x83\x99\
\x00\x00\x03\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x84u\
\x00\x00\x03\xf4\x00\x00\x00\x00\x00\x01\x00\x00\x94\x80\
\x00\x00\x01\x9f{{\xa5\xe3\
\x00\x00\x01$\x00\x00\x00\x00\x00\x01\x00\x00'\xef\
\x00\x00\x00^\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xc1\
\x00\x00\x01\x9f\x7f\xac\xf2\xc6\
\x00\x00\x01D\x00\x00\x00\x00\x00\x01\x00\x00/{\
\x00\x00\x01\x9f\x7fV\xd5\xe5\
\x00\x00\x03*\x00\x00\x00\x00\x00\x01\x00\x00x\xac\
\x00\x00\x03|\x00\x00\x00\x00\x00\x01\x00\x00\x88\xb7\
\x00\x00\x01\x9f{0\xc9R\
"

View File

@@ -0,0 +1,9 @@
import logging
LOGGER_NAME = "bedit"
def get_logger(name: str | None = None) -> logging.Logger:
"""Return the shared application logger, optionally scoped to a module."""
return logging.getLogger(LOGGER_NAME if name is None else f"{LOGGER_NAME}.{name}")

View File

@@ -255,6 +255,7 @@ class Graph:
connections: dict[str, Connection] = field(default_factory=dict)
annotations: dict[str, Annotation] = field(default_factory=dict)
junctions: dict[str, Junction] = field(default_factory=dict)
simulation_settings: dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> dict[str, Any]:
return {
@@ -262,6 +263,7 @@ class Graph:
"connections": [connection.to_dict() for connection in self.connections.values()],
"annotations": [item.to_dict() for item in self.annotations.values()],
"junctions": [junction.to_dict() for junction in self.junctions.values()],
"simulation": deepcopy(self.simulation_settings),
}
@classmethod
@@ -286,6 +288,7 @@ class Graph:
connections={connection.id: connection for connection in connections},
annotations={item.id: item for item in annotations},
junctions={junction.id: junction for junction in junctions},
simulation_settings=deepcopy(data.get("simulation", {})),
)
@@ -578,6 +581,7 @@ def clone_component(source: Component) -> Component:
)
for junction in current.graph.junctions.values()
},
simulation_settings=deepcopy(current.graph.simulation_settings),
)
return Component(
id=str(uuid4()),

View File

@@ -0,0 +1,3 @@
from bedit.core.simulation.service import Simulation
__all__ = ["Simulation"]

View File

@@ -0,0 +1,18 @@
from copy import deepcopy
from typing import Any
class Simulation:
"""Application-owned simulation service and state container."""
def __init__(self) -> None:
self.state: dict[str, Any] = {}
self.last_compilation_input: dict[str, Any] | None = None
def compile(self, graph: dict[str, Any]) -> None:
"""Compile a graph definition.
This is intentionally a stub. Keeping a copy of the input makes the
service useful for incremental compiler development and UI inspection.
"""
self.last_compilation_input = deepcopy(graph)

View File

@@ -0,0 +1,26 @@
import logging
from PySide6.QtCore import QObject, Signal
from PySide6.QtWidgets import QPlainTextEdit
class _LogEmitter(QObject):
lineReady = Signal(str)
class ApplicationLogHandler(logging.Handler):
"""Thread-safe bridge from Python logging into a Qt text view."""
def __init__(self, output: QPlainTextEdit) -> None:
super().__init__()
self.emitter = _LogEmitter(output)
self.emitter.lineReady.connect(output.appendPlainText)
self.setFormatter(
logging.Formatter("%(asctime)s %(levelname)s %(message)s", "%H:%M:%S")
)
def emit(self, record: logging.LogRecord) -> None:
try:
self.emitter.lineReady.emit(self.format(record))
except Exception:
self.handleError(record)

View File

@@ -137,6 +137,19 @@ class EditGraphItemCommand(QUndoCommand):
self.controller._set_graph_item_data(self.owner_id, self.item_kind, self.item_id, self.old)
class EditSimulationSettingsCommand(QUndoCommand):
def __init__(self, controller, owner_id: str, old: dict, new: dict) -> None:
super().__init__("Edit simulation settings")
self.controller, self.owner_id = controller, owner_id
self.old, self.new = old, new
def redo(self) -> None:
self.controller._set_simulation_settings(self.owner_id, self.new)
def undo(self) -> None:
self.controller._set_simulation_settings(self.owner_id, self.old)
class DeleteAnnotationsCommand(QUndoCommand):
def __init__(self, controller, owner_id: str, annotations: dict[str, Annotation]) -> None:
super().__init__("Delete annotations")

View File

@@ -13,6 +13,7 @@ from bedit.gui.controllers.commands import (
DeleteSelectionCommand,
DeleteAnnotationsCommand,
EditGraphItemCommand,
EditSimulationSettingsCommand,
EditTextDefinitionCommand,
EditComponentAppearanceCommand,
EditComponentPropertiesCommand,
@@ -37,6 +38,7 @@ from bedit.core.model import (
Port,
clone_component,
)
from bedit.core.simulation import Simulation
from bedit.core.port_types import PortTypeRegistry
from bedit.core.serializer import JsonDocumentSerializer
@@ -60,8 +62,9 @@ class DocumentController(QObject):
filePathChanged = Signal(object)
modifiedChanged = Signal(bool)
def __init__(self, parent=None) -> None:
def __init__(self, parent=None, *, simulation: Simulation | None = None) -> None:
super().__init__(parent)
self.simulation = simulation or Simulation()
self.document: GraphDocument | None = None
self.active_component_id: str | None = None
self.file_path: Path | None = None
@@ -378,6 +381,23 @@ class DocumentController(QObject):
self.undo_stack.push(AddAnnotationCommand(self, self.active_component_id, annotation))
return annotation.id
def edit_simulation_settings(self, settings: dict) -> None:
component = self.active_component
if component is None or component.implementation_kind != "graph":
raise ValueError("Open a graph component before editing simulation settings")
old = deepcopy(component.graph.simulation_settings)
new = deepcopy(settings)
if old != new:
self.undo_stack.push(
EditSimulationSettingsCommand(self, component.id, old, new)
)
def compile_active_graph(self) -> None:
component = self.active_component
if component is None or component.implementation_kind != "graph":
raise ValueError("Open a graph component before compiling")
self.simulation.compile(component.to_dict())
def set_route_waypoints(self, item_kind: str, item_id: str, waypoints: list[QPointF]) -> None:
item = (
self.active_graph.connections
@@ -1032,6 +1052,12 @@ class DocumentController(QObject):
if owner_id == self.active_component_id:
self.graphItemChanged.emit(item_kind, item_id)
def _set_simulation_settings(self, owner_id: str, settings: dict) -> None:
owner = self.document.find_component(owner_id) if self.document else None
if owner is not None and owner.implementation_kind == "graph":
owner.graph.simulation_settings = deepcopy(settings)
self.documentReset.emit()
def _replace_component(self, old_id: str, replacement: Component) -> None:
if self.document is None:
return

View File

@@ -0,0 +1,22 @@
from copy import deepcopy
from PySide6.QtWidgets import QDialog
from bedit.gui.generated.ui_simulation_settings_dialog import Ui_SimulationSettingsDialog
class SimulationSettingsDialog(QDialog):
def __init__(self, settings: dict, parent=None) -> None:
super().__init__(parent)
self.ui = Ui_SimulationSettingsDialog()
self.ui.setupUi(self)
self._settings = deepcopy(settings)
self.ui.placeholderCheckBox.setChecked(
bool(self._settings.get("enabled", False))
)
@property
def settings(self) -> dict:
values = deepcopy(self._settings)
values["enabled"] = self.ui.placeholderCheckBox.isChecked()
return values

View File

@@ -215,6 +215,129 @@ ff\xa6\x5cP`\xb7\xadY\x1b\xde\x8b\xcb\x97/\xc3\
\x80\x90\x84\xfe\x83\xfe\x0fl\x00\x00/\xbf\xf2\xf2\x7f\x9c\
\xfd\x0bVu/\x8d\xbcS'\xfc\x00\x00\x00\x00IE\
ND\xaeB`\x82\
\x00\x00\x07\x88\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\
\x9bx\x00\x00\x00\x07tIME\x07\xd9\x08\x0b\x00-\
\x1f\xb3\xb6|\x9d\x00\x00\x00\x06bKGD\x00\xff\x00\
\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x07\x08IDATx\
\xda\xbdWiLTW\x18\x95\xda\xa0P1]b\x89\
\x0a.EQ\xdcj\x5c\xd3FcMj\xda\xa4I-\
?J4\xc4(1\xadMl\xd5\x22*h,\x88K\
\xd0b+\xa6\x15Y\x0dV\x94\x08U\x16A\x10\x18`\
f`\x18\x96a\x1f\x86a\xd8\x94}\x11T\x90\xa0v\
N\xefy\x99\x99L\xa8\x88&\xd6\x97|\xb9\xf7\xdd\xf7\
\xbes\xce\xb7\xdc\xfbf&\xbc\xea\x15\x10\x10\xc0a\xa2\
\xb0\x0f\xd7\xaf_\xbfx\xcd\x9a5\x8b\xc5\xdcQ\xd8\xdb\
\x9e\x9e\x9e\x13\xde\xc4\xf5\x96L&\xd3<z\xf4\x08C\
CC\x92q\x9e\x9e\x9e\xae\xe6\xb37!`jbb\
b\xdf\xc8\xc8\x08\x86\x87\x87%\xe3\xfc\xc6\x8d\x1b\xbd|\
\xf6:\x89\xec\x97/_\xfe\xc9s\xd6?\xc8\xcc\xcc\x1c\
z\xf2\xe4\x09\x89%\xe3<##c\x88\xcfF\xbf\xbc\
z\xf5\xeaO\xc5`\xf7\xaa\xe4\xd3\x93\x92\x92\xaa\x9e>\
}\x0a__\xdf\x10\xa6\x965\xde\xbcy\xb3\xfb\xb5k\
\xd7\xd2\xef\xdf\xbf\x8f\xc1\xc1AK\x098\xef\xeb\xebC\
\x5c\x5c\x5c\xba\xbb\xbb\xfb7|W\x98MHH\xc8\x9f\
\xcf\x9e=CUU\x95\x8e\x98/K\xee\x94\x9b\x9b\xdb\
A\xd0\x81\x81\x01#\xeb\x1b\x15\x15\x95\xabP(\x1a\x19\
-\xd7\x1f>|\xf8\x5c\xe3\xb3\xc7\x8f\x1fC\xa9T6\
\xc4\xc7\xc7\xcb9'F\x7f\x7f?t:]\x0f\xb1\xc7\
\xad\xef\xcd\x9b7k\xe8\xd0\xdb\xdbKcd\x12\xb8X\
32ra\x9c\x8f6\xae[\x9bQ\x10K\xfe==\
=\xe8\xec\xecDww7D`\xcc\x84\xc3\x98\xecK\
\x96,\xf1lllDGG\x87\x91Nt\xb6\x16B\
#\x01\x05\xb5\xb7\xb7\xa3\xa6F+\x22\xd3\xa2\xad\xfd\x1e\
\x06\xfa-\x844\x89\xb0\xab\xabKz\xef\xee\xdd\xbbh\
ii1655\xc1\xcd\xcdm\xdb\x8b20\xcd\xcb\
\xcb\xab\x90\xce\xad\xad\xad\x14B \x8b\x08FZZZ\
\x0a\x95J\x85\xb2\xb22\x18\x0c\x06\xd4\xd7\xd7\xa3\xb4\xbc\
\x14\x05\xea<\xa85\x0atuw\x8a\x88\xbb\x18\xb5\x85\
\x9cAq\xdc\xb1c\x87\x8a\x1c\xe3\x95a\x9e\xbf\xbf\x7f\
3\x05\xd0\x89 \x8c\x88\xf3\xc2\xc2Bid$4\x02\
744\x083P\x8c4\x97\x17fA\xdf(\xb2\xd2\
\xd6\x86{\xf7\xeeI\xebuuu8r\xe4H3\xb1\
_\xa6\x09\xa7\x5c\xbdz\xb5\xddD\xc2\xd4ID\x15\x15\
\x15\xbc7Y\xb3467\xb7H\xf3\xc6\x86F)\x13\
z\xbd^\x90\xe9Q\xa4\xc9G\x9dAKA\xa2L5\
\x92oddd\xab\xc0~g\x5c\xf6\x0d\x1b6\xec2\
\xa5\x96\x00\x82\xa4\x99\x913\x0a\x0a1E\xde\x84\xd4\xcc\
X\xc4\xa7\xfe\x86\x84\xb4\xb3H\xbc\x1d\x05\xadVKr\
v\xbb4W\x15\xcbQ\xab\xd3J\xa5***Be\
e%\xc4\x99\xb0}<\xfe\x89'O\x9eT\x92\x8c&\
\x84P=\xf7\xb1\x14I\xbd\xbe\x1e\x9a\xb2b\x84_\xff\
\x19%\xf510t\xa7\xa0\xb1/\x0d\xd5-q\xf8+\
\xc5\x07\xd99)\xa8\xd3\xd5I\xefj4\x1a\xa8\xd4\x0a\
\xa8\x0b\xd5(..Fuu5\x04v\x1e9\xfes\
\xe2\x89\xed\x91#\x946\x09\xa5\x9d\x8c\x80/3\x0a\xa6\
T\xacS\xbdiM\x87#\xe77BYw\x1a%\xad\
\xe7\xa0\xeb\x8f\x82\xfe\xc1%Tt\xfc\x81\x82\x86_q\
\xe2\xd2zA\x5c\x86\xf2\xf2\x0a\xa9Q\x15\xca<\x8e\x14\
#\x05Q[[\x0br\x88\xfb&\xb9\x5c\x9eCn\x0a\
X\x91\x97':X\xadFII\x89p.\xb7DL\
1\xeczf\xa1\xba\xaa\x1a\x97\xaf\x85 8\xcd\x09\x19\
\xb5\xbb\x91\xd3\xbc\x07\x8a6\x1f(\xdb}\x90{w\x1f\
\xb2\xf4?!<\x7f\x11\xceE\xee%\x0e\xf7<\xb2\xb3\
\xb3I\xce\x00\x88Il\xe2Q\x04\xf2\xf3\xf3!\xb8?\
\xa6\x80\x8dB\x0d\xeb\xccT\xf1\x05\xb3\x08\x0a\xb0\x88\xaa\
\xa9\xd6\xc2\xffw\x0f\x9cW\xccDh\xd12$h\xbf\
FR\xbd\x07\x92\x0c\x1e\xf8[\xe7\x8e\xb0\xd2U8\xa7\
t\x82\xef\xb9\xb5P\x15\xa8q\xe7\xce\x1d\xe4\xe4\xe40\
\x10f\x8fAP\x0c9$.r\x0a\xee\xcf(`\x93\
\xf8\xbc\xf2\xf8d\xba\xa8\xce,\x82}`\xb9\xaf\x16\x02\
|\x83\xbeBH\x9e3N\xcb\xe7\xe0x\xfe\x1c\x04\xaa\
\x5c\x11X\xe0\x8a\x00\xe5\x5c\x9cR|\x84`\xf9l\xec\
\x0dZ,\xa2W0\x03\x8c\x92e\x1cM\xceu\x8a\xa3\
\x80\xcf)`mrr2\x15Q\x849\x13t\xa0z\
\xf6\x00\xef\x05H%\x02\x82\xbeGP\xea\x0c\x9c\x92\xcd\
\xc5Q\xd9<\x1c\x92-\xc0A\xd9B\xf8\xc9\x84\x88l\
\x17\x04\xcbf\xe1\x07\xdfu(((\x14V@_\xf6\
\x12Gf\xd2BN\xae\x94\x94\x14\x0aXC\x01\xb3\x8e\
\x1d;\xd6\x1e\x1d\x1d=\x12\x1b\x1b\xfb\x0f\xb3`]\x0e\
\x8a\xe0=\xe7\xf1\x09\xd7\xe1\xf5\xcb\xbb\x08\xca\x9a\x83\xc3\
\xe9\xf3\xb0/m!~Lu\xc3\xfe\xb4\x05\xf0\xcf\xf8\
\x08\xde\xe1\xd3p2\xe8(\xfb\x89Fr\xfaY\x22'\
69\xc8%8\xdb\xc8=\xc1\xd6\xd6\xd6\x86\x13a+\
\x99\x92C\x87\x0e\x19\xa8\xd2\x5c\x0e\xa6P|\x09%\x00\
\xb5\xb8\xf7>\xe8\x85\xefN\xbc\x8fc\xb7f\xc3\xe7\x96\
+v'\xbb\xe1p\xaa\x10\x13:\x0d\xdbwmb\xea\
\x05y\x11\xb3\xc9\xb3\xc4B\xce\x8c\x08\xeczr\x90\x8b\
\x9c\x93'O&\xb7\xd5\xc5'+W\x1e\xa7\x83UO\
0\x0b\xe2>_\x02J\xcfH\xc3A\xbf=\xf8v\xe7\
Rx\xfa\xcc\xc06\xdf\xe9\xf0\xd8%2\xb1\xcf\x0bI\
\xc9\x89\xa2Wj\xe8\xc3\xc6\xb3\xae\xb9\xb4\xb6b\xc5\x8a\
\xe3/s\x14\xbb^\xbcx\xf1\x81Pl\xa43\x1d\xcb\
+\xcb\xa0P\xcb\x90\x9d\x95-uwrR\x12\x22\xa2\
\xc2\x10|6H\xd8i\xc4\x5c\x8eFn\x9e\x8c\xa4\xdc\
r\xdc=l`\xd6\x9d\xa5` \xc6\xd0\xd0\xd0\x07\xc4\
~\x19\x01\xb6\xde\xde\xde\x1a6\x0a;\x95\xc6]\x92)\
O\xc6-\xc5U\xdc\xbe\x9d\xce4\xf3\x84\x93\x1aU\xda\
\xb2\x15U\xect6\x9b\xf9\xf8f\xfa\xf9\xcc\xd2\x0f\x07\
\x0e\x1c\xd0\x10{<r\x9b\xfd\xfb\xf7\x9fa\xbdx\x88\
\x90\x98\x87\x14S\xc8\xba\x96jJ\xa07h\x19!\xc1\
\xcd\x8d\xca3\x83\xc76?\x5c\xec\x19\x9a\xf9\x9b`\xf9\
|\xf3]??\xbf3\xe4\x18\x93\xdd\xc5\xc5\xe5K:\
1\xfdt2\x9d\x8e\xe6H-\xdf\x05\xa6\xd9\xfa\xe3D\
B\x1e\xb3\xd6f}\x00\xb1\x8c,\x03\xb1\xe7\xcf\x9f\xff\
\xc5\x8b2\xb0L\xd4\xaa\x8f\xc4\xa6\xfa3\x02\xb3\x00#\
\xc1L\xc7*\x8d\x04c\x99\x91#E\xd3\x97\x18\xc4\x14\
\xbd\xd5K\x8e1\xd9\x1d\x1c\x1c8\xb8'$$\x0c\x0b\
'Fod4b\xcf\xf6\x84\x87\x87\x0f\xb2\xb6\xcf#\
\xb7^\xe3;\x11\x11\x11\x83\x81\x81\x81=\xa6o\x89\x91\
X\xe2G\xea0\xb1\xa7N\x1d\xe7\xaf\x83\xbd\xbd=\x7f\
\x82{\x84\x85\x85\xddgCm\xd9\xb2\x85?\xa3\x96\x0a\
[\xe5\xec\xec|A\xfcLo1\xa5w\xb4\xb1\xc6\xcd\
NNN\x17\xf8.}\xb6n\xdd\xaa\x22\x06\xb1\x889\
e\xca\x14b\x8f\x7f\xd9\xd9\xd9qX\xe7\xe8\xe8xB\
\x8c3\xad\x9bT\xd8\xa2+W\xae<\xb2\x16\xc19\xd7\
\xf8lT\x93\xcd$\x06\xb1\x88\xf9\xba\xae\xf7bbb\
\xbaX\x1a6\x15\x8ds\xae\xf1\xd9\x847p\xd9\x07\x07\
\x07k\xb9\x03\xf8\xb3\x9b\xc69\xd7\xf8\xec\x7fg\x9f4\
i\x92\x8d\xe9,\xdf)\xcc\x87\xc69\xd7\xf8]yU\
\xbc\x7f\x01\x06A\xf8\xf9\xb2\x87\xf2i\x00\x00\x00\x00I\
END\xaeB`\x82\
\x00\x00\x05\x08\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -1360,6 +1483,144 @@ $&\x93\xc8\x7f\xb3\xe7\xcc*\xc2w\xf1nHG\x09\
\xf9\x5c\x8a\xda\xed\x0c\xf6t\xf6\xe1\xf9-'\x97\x16\xdc\
5\x03\xef$\x18\xdea\xfc\x07;\x00\xdf\x09\xb1\xdb|\
\x84\x00\x00\x00\x00IEND\xaeB`\x82\
\x00\x00\x08{\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\
\x00\x00\x00\x09pHYs\x00\x00\x03v\x00\x00\x03v\
\x01}\xd5\x82\xcc\x00\x00\x00\x19tEXtSof\
tware\x00www.inksca\
pe.org\x9b\xee<\x1a\x00\x00\x07\xf8ID\
ATx\xda\xb5W\x0bX\xcdi\x1a\xff,\xd5\xa2\xc1\
\x8e\x8ad<\xe52.\xab\xc9\x0e\xe5t*a\x0c!\
\x95\xa2\x9abZ\x97U\xf3<\x96M3I5\x83\xd5\
E24\x88RjY\xc3&\x0c]\xb6D\xd3\x95t\
t\x8fD[\x9d\xee\xb7\xa3\x9e\xd2)\xba\xfd\xf6\xfdR\
\xf3\xa4Q\x1d\xb3\xcf~\xcf\xf3{\xce9\xdf\xff\xfd\xfe\
\xef\xef{\xef\x87\x01\xf8\xbf\x80\xd6(\x82\x02\xe1\x03\xc2\
x\xc2\x18\xbe7X\xae_x\xf4)\x7f\xff\xd5\xf49\
\x8e\x0b\xfd\x8f\x8a\x7f\xa7\xac\xac\xac\xe8\xe5\xe5e\x1b\x15\
\x1d\xf5$;;\xbb;//\xefUVvV\xd1\xd5\
\xabWWr]o\xcb\xd3\xc6\x95+W\xdc_\xbe|\
\x89R\xb1\xf8UXx\xf8~\xbe\xf7[o=g\xce\
\x1c\xe5\x90\x90\x90\xc2\xfc\xc7\xf9(+/Cuu5\
jkk!\x91HPRZ\xd2q\xe8\xd0\xa1\xf5\x03\
/\xc9\xd7\xa4\xe2\xe2\xe2\xd7\xaf;:P]S\x83\xb4\
\x87\x0f{\xec\xec\xec\xcc\xf8M~\x03\x81\xdf\xef\xdd\xbb\
\xf7\x87\xb4\x87i\xa8\xac\xaaD\x83\xa4\x01\x12Btt\
\xf4kWW\xd7\xeaE\x8b\x16]TTT\x9c\xcf\xdd\
\xf1\x16\x813g\xce\xdc\xae\xac\xac\x84\xb8\xbc\x1c\xa2G\
\x8f`\xef\xe0\xd0\xbax\xf1\xe2\x19\xef\xeb\x0eZ\x93\x8f\
\x1f\xf7-\xab\xa8(\xefU\xdc\xda\xda\xca-\xd0\xad\xa4\
\xa4\xf4\x1d=[F\x98\xceI\x0e\xb6\xc0\x18\xc2\xbc\x8b\
\x97.\x15\xdf\x89\x8b\xebIJNF[[\x1b6[\
Z\xa5\xd3\xfe\x04YIp9\x15\x15\x95YQQ\x91\
\xdd\x9c@SS\x13\xda\xdb\xdbPZZ\xfa\x8a\xbf\x9f\
\xa00\x5c\x10\xf2\x87\x0bW\xafYs\xd6v\xeb\x97\xad\
\x05\x05\x05\x90\x12\x89\x8d\xe6\x16Q\xea\xea\xea\x93d!\
AK\xce\xd2\xd2\xd2.%%\x19999\x90\xbc\x90\
\xf4\x92\xa8\xab\xaf\x83\xa1\xa1\xe1\x12\xee\xd2\x01\xd91\xa6\
?+\xdez\x01a\x9a\xbc\xbc\xbc\xf9\xe7\xab\xd7\x14<\
\x7f\xfe\xbc\xd7\x84\xebML\x13\x96-[\xa6<\x12\x09\
Z\x8a\xbbw\xef\xfe\xd7\xdd{q\xc8\xc8\xc8@Uu\
\x15\xaa\x09\x15\x15\x15\xb8r\xf5\xc7V\x1b\x1b\x9b5\x5c\
\xe6\xb0\xc7\xe1\x8d\xc1\xc1A\x05_\xd1\xe2:\x7f\x95B\
\x84\x89\xdc_\x02]afIi)\x9a\x9b\x9bac\
\xfbe\xba\x85\x85\xc5\xd4\xe1\x02\x93\xd6\x94s\x01\xe7Z\
\x92\x92\x93\xb8\xd9\xf1 \xed\x01\x0a\x0b\x0b\xf1\xecY!\
\x92S\x92p\xd2\xef\xfb\xce\xd8;\xb1\x92\xd4\xd4T\xf8\
\xfa\x1ek\xd7\xd1\xd1\xb1\xe1\xf10T\x01\x19O\x10\x0a\
\x04\xba\xa9t\x032c=\x1c\xbe\xda\x9d\xbfk\xd7\xae\
\x19\xef\x22\xc1\xd3\xd6\xca\xca\xca\xfa\xe6O7\x10p>\
\x08!\xff\x08\x81\xe7QO\xf8\xf8\xfa\x22+;\x93\x02\
;\x1d!\xa1!\xe0\xca\xc3\xc2\xc2(\xc8\xed\xcb\xe9\xcc\
,\xfe\xae\xe1\xaa\xd88\x82\xb6\xbe\xbe\xc1\xdd\xba\xbaz\
TP\x968\xefw)rvv\x9e3\x98\x04\x0f\xd6\
\xc3\x87\x0f%\xe4\xe5\xe5b\x87\xbd=\x5c\x5c\xbf\x81(\
\xe3!\xf4\xf4\x84\xa4\x5c\x84;q\xb1\x08%R\x91\x91\
\x11\xf0\xf4\xf4\xe8\xa6Z\xf15?\xc3\xcf\x8eTJ\xc7\
\x12\xfedh\xb8<\xb2\xb1\xb1\xb1\xa7\xb8\xa4\x04\x01\x81\
\x81\xe5TL\x16\x0e\x08*y\xf2\xfd\x0e\xba]\x17\xcf\
\x9e\xa0\xe0\x0b\xd8ha\x86\xfc\xc7\xb9ptr\xc4\x8d\
\x9b7@\xae\x81\xb7\xb7\x17\xdc\xbfu\xeb\xd6\xd4\xd4\xfc\
\x91\xceh\xf4\x17;Y\xeb\xb9\xe6\x8a\x15+\xc3ZZ\
Z\xba\x9f\x17\x15!]$\xaa;q\xe2\xc4\x12n%\
r\x8b3/\xb7\xe1\xe1\xd7\x90\x90\x90\x80\xb3g\xcf\xc2\
\xda\xfa\x0bxy{\x83d\xe0\xe6\xee\xd6\xe3\xe8\xf8\xb7\
\xd7\xdb\xb6o\xab\xa7\x9b\x1f\xa33\xdc\x82r\xfd:d\
-0\xf2\x84\x05\xe6\xe6\xe6\x17\xa5Ri\xd7\xe3'O\
@\xf5\xbd\xe9\xe0\xc1\x83\x17sss{\xbc\xbc<\xb1\
b\xe5r\x1c8\xe0\x82\xd3\xa7O\xc3\xc5\xc5\xa5\xd5\xd6\
\xd6\xf6\xa9\x96\x96\xd69:gM\xd0%\xcc$|\xd8\
_\x05e%08M\xe7n\xd9\xb2%\x80\xfaFg\
Nn.'\x01\x0fO\x0f,_n\x88\xcd\x9b7\xe1\
\xf2\xe5\xcb \xc5i$\xb7\x81\xf01aJ_7\x94\
\x1f*\x8d\x07m\xc8d\x89\x85\xdb\xb7o\x7f\xc2\x8bL\
Ff\x16\xe5|&v\xd9\xef\x02u:888\xe4\
\xd3s\x01W*k/y\xdf\xfe\xae\xe8\xed\xed}\xf3\
\xde\xbd{\xe4g+P\xff\xa0xx\xd4K\xc2\xc7\xc7\
\xa7!1\xd4\xf2N\x84\xbb\xfaw\x03\xeb\xfdH\x90Y\
\xf9$Z\xd4\xb4\xe2\xf2\xf3\xf3\xb1u\xab-\x96\x0a\xb4\
\xa1o\xa0\xcf\x8b\x0e\xa5Z\x06\xca\xf2\xfcz\xa4e!\
(\xba\xa0\x88\xa0\x1dl_\x7f\xa0\x8d\x04\x99\x06\x0c\x0a\
&\x15\xea\xf1\xf7333a\xfd\x85\x15\x0c\x0c\xf5\xa1\
\xa5\xf5\x09\x8e\x1d;\x06rG\x9b\xf8\xd1\xdf;:\xcb\
>D\xdb\xd3y\x90\xde\x1c\x0f\x91\x1fk\xf8h2S\
\x93\xc5\x0a#*\x17\x08\x04j\xd7\xae]\xcbNII\
\xc1\xa6M\xe6tk=\xcc\x9f?\xbf7\xc5N\x9e<\
\xd9\xeaj16\x5c|\x95u7\xddg\xe8\x8egx\
\x95\xccP\x13\xcb\xf0\xef#,\xf2\xaf\x9f\xb1\x05\xdc\x12\
\xc3\x11\x19V\xf9\xaaU\xabf\xd20\xf1466\x96\
:\xa3\x19\xf4\xf4u1k\xf6LP\xfa!((\xa8\
\x9d\x88\xec\xfb\x5c\x8b9\xdc\xfe\x9a\xb5\xd7]fh\x89\
c\xe8,a\x90\x1604g04\x8a\x14\xba\x13}\
\x14\xc3\x94\x95\x99\xe2P$\x86Tnjj\xba >\
>\xbe4<<\x1c\xa6f&\x10\xea\x090c\xc6G\
\xd8\xb3g\x0fO\xb7\x0e===\x1elj\x04\xd5y\
j\xcc\xde\xc7\x96\x15\xfd\xec3\xaaK\x92B$\xc4\x0c\
]\xe5\xa3\xd1.6AQ(C\xe4>\x16MM\xfd\
\x9dm\x9d\xbd\xab\xb1P_\xff\x94JkuH\xc8\x05\
l05\x86@\xb8\x14jj\xd3(\xf8\xb6\xe2\xfa\xf5\
\xeb]FFF\xa7Hn\x06\x97\xed\xeb\xa0\xfc\xe5\x9a\
\x1fOc.\x0fN\xb2\xce\x17\x0f\x88D\xc5\x04t\xd4\
\xedEC\xb2\x12\x8a\x7f\xea%\xf1d\xc9\x5c\xa64\x98\
\xc4\xaf\x94S\xa11\x10\x89D\x12\xbf\x1f\xfc`l\xb2\
\x9e\xa2]\x07SU\xa7\xc2\xd8\xd8\x18QQQ=D\
\xee2\xefd\x83+\x1a\xffM\x98\xe9m\xcd\xd2\x8b\xc9\
\x1d\x0d\x89\x14\x0f\xc5c\xd1Qk\x0bi\x8e\x12\xc41\
\x14\x17\xfbX\xa9\xd9\xa7Lu`\x8dxK9\xd5\xf5\
\xd5T\xd7\x9b=<<\xb0~\xc3:\xe8,]\xd2\xab\
\xdc\xc0\xc0\x00111=;w\xee\x8cf\xecM`\
\x0d5\x94N\x99\xc8V\x86\xda\xb3\x17\xff\xf9'C9\
\xdd\x5c\x92\x22\x87\x8ej#H\xb3'\xa3\xe2.C\xcc\
7\xac\xc6Z\xc0\xd4\xfbI\xfc\xa2\x9c\xa6Y3\x1a\xc5\
Z\x1f\xd1Pz\xfc\xfb\xe3\x10\x0au1e\x8a\x0ah\
\x92\x05\x0fB''\xa7\xfb\xbc3\x12\x14F(V\x13\
\xc7)0\x93\xf3;Y}m\xfc\x1b\x12\xf5?\x8fF\
G\x956\xa4Y\x7f@u\x02\xc3\xdd\x03\xec\x85\xfb\x06\
\xf6GN\xa2\xf7\x90\x9a\x9a\xdat\xba\xb9\xd4\xd3\xd3\xf3\
\xd9\xbau\xeb\x22)\xbd\x1a]\x5c\xf6\x83\xba\x17\x22\x22\
\x22@\xed\x97\x97X\xa1\x0c\x7f\x5c\xfaILR\x18\xc3\
V\xfb\xff\x99U\xd5\x10\x89\xca\xdb\x94\x9a1\xa3\xd0Y\
9\x1b\xd2LE\xd4\xa52dy\xb3\x12^Y\x09L\
\xde\xcd\xcd\xcd\x8e\x22\xbe\x89\xbe/'\xcc^\xbbv\xad\
3\x99\x9c\xfb\x9c\xe7{)\xed}F\xf8\xe0}&\xe4\
\xbe\x89z\x85\xdf\x16VR\x15K$\x22\xde\xa0\xb3\x5c\
\x09\xcd)\x0a\x90\x5cb\xed}\xcd\x8a\x8d\xa5?\x0d\xdf\
\xc6\xc5\xc5\xb5ihh\xcc\xe5&vtt\xdc\x96\x98\
\x98\xd8\x13\x18\x18X\xab\xa0\xa0`\xc2\xd8\xe0\x14\x92\xbd\
w\x10\xf4\x8fnf\x85\xe2(\x86\xaah\x06\xa9\x88\xa1\
=i4\xe2\x8f\xb2\x87\xf4L\xa9\xd7\x02rrr\xda\
4\xab\xb5\xdf\xbau\xab\x90>\x03(\xd5jN\x9d:\
\xd58a\xc2\x04[.$cg\x1bn\xbe\x14\x1c1\
g9\xf9~\x0c\xe2\xf3\xac'\xdd\x8fU\xd0\xdeb\xae\
\xbb_HE(\x14\xfa\x04\x07\x07\xb7\xf8\xfb\xfb\xb7\x91\
\xcf\xc5\xaa\xaa\xaa\x7f\xe1&\x92Q\xb9,\xa3\x9d\xa6\xb6\
\x06s\xb2\xd6cG\xe8\xfb'}{\xa3~\xc9\x02\xc2\
t\x82\x11a\x13a)a\xb2L\xcae'!\xc7]\
\xc91\xb0?\xfc\x17\xb0l\xc1\xc4\x9c\xd3\xd1\xfc\x00\x00\
\x00\x00IEND\xaeB`\x82\
\x00\x00\x04+\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -2271,6 +2532,10 @@ qt_resource_name = b"\
\x00z\
\x00o\x00o\x00m\x00-\x00o\x00r\x00i\x00g\x00i\x00n\x00a\x00l\x00.\x00p\x00n\x00g\
\
\x00\x0d\
\x0f\xa5%\xc7\
\x00r\
\x00u\x00n\x00-\x00b\x00u\x00i\x00l\x00d\x00.\x00p\x00n\x00g\
\x00\x0f\
\x0bb\xb8g\
\x00d\
@@ -2327,6 +2592,11 @@ qt_resource_name = b"\
\x00e\
\x00d\x00i\x00t\x00-\x00r\x00e\x00d\x00o\x00.\x00p\x00n\x00g\
\x00\x16\
\x01p\xe1\x87\
\x00p\
\x00r\x00e\x00f\x00e\x00r\x00e\x00n\x00c\x00e\x00s\x00-\x00s\x00y\x00s\x00t\x00e\
\x00m\x00.\x00p\x00n\x00g\
\x00\x16\
\x0c\xb7\x89\xc7\
\x00d\
\x00r\x00a\x00w\x00-\x00b\x00e\x00z\x00i\x00e\x00r\x00-\x00c\x00u\x00r\x00v\x00e\
@@ -2383,59 +2653,63 @@ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1a\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1c\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x01\xde\x00\x00\x00\x00\x00\x01\x00\x00G\x5c\
\x00\x00\x01\xfe\x00\x00\x00\x00\x00\x01\x00\x00N\xe8\
\x00\x00\x01\x9f{C\xf1'\
\x00\x00\x02\x9a\x00\x00\x00\x00\x00\x01\x00\x00dK\
\x00\x00\x02>\x00\x00\x00\x00\x00\x01\x00\x00Z\x1a\
\x00\x00\x01\x9f\x7f\xa8\xa8)\
\x00\x00\x02\xec\x00\x00\x00\x00\x00\x01\x00\x00tV\
\x00\x00\x01\x9f{0\xc99\
\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x00<\xa6\
\x00\x00\x01\xc4\x00\x00\x00\x00\x00\x01\x00\x00D2\
\x00\x00\x01\x9f\x7f&\x83\xcd\
\x00\x00\x01\x84\x00\x00\x00\x00\x00\x01\x00\x004\xbe\
\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x00<J\
\x00\x00\x01\x9f{C\xf1\x18\
\x00\x00\x03R\x00\x00\x00\x00\x00\x01\x00\x00}\x9f\
\x00\x00\x03\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x8d\xaa\
\x00\x00\x01\x9f\x7fY\xceg\
\x00\x00\x02P\x00\x00\x00\x00\x00\x01\x00\x00V\xbd\
\x00\x00\x02\xa2\x00\x00\x00\x00\x00\x01\x00\x00f\xc8\
\x00\x00\x01\x9f\x7fV\xd5\xc0\
\x00\x00\x03\x08\x00\x00\x00\x00\x00\x01\x00\x00t\xba\
\x00\x00\x03Z\x00\x00\x00\x00\x00\x01\x00\x00\x84\xc5\
\x00\x00\x01\x9f\x7f&\x83r\
\x00\x00\x02|\x00\x00\x00\x00\x00\x01\x00\x00]P\
\x00\x00\x02\xce\x00\x00\x00\x00\x00\x01\x00\x00m[\
\x00\x00\x01\x9f\x7f&\x83\xb1\
\x00\x00\x00\xfc\x00\x00\x00\x00\x00\x01\x00\x00$|\
\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x00,\x08\
\x00\x00\x01\x9f\x7fY\xce\x82\
\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x00C\xdc\
\x00\x00\x01\xe0\x00\x00\x00\x00\x00\x01\x00\x00Kh\
\x00\x00\x01\x9f{C\xf1.\
\x00\x00\x01d\x00\x00\x00\x00\x00\x01\x00\x00/\x10\
\x00\x00\x01\x84\x00\x00\x00\x00\x00\x01\x00\x006\x9c\
\x00\x00\x01\x9f{{\xa5\xd5\
\x00\x00\x03x\x00\x00\x00\x00\x00\x01\x00\x00\x81\xdf\
\x00\x00\x03\xca\x00\x00\x00\x00\x00\x01\x00\x00\x91\xea\
\x00\x00\x01\x9f\x7f&\x83\x10\
\x00\x00\x00\xde\x00\x00\x00\x00\x00\x01\x00\x00 \xd9\
\x00\x00\x00\xfe\x00\x00\x00\x00\x00\x01\x00\x00(e\
\x00\x00\x01\x9f\x7f&\x83~\
\x00\x00\x02\xe4\x00\x00\x00\x00\x00\x01\x00\x00p\xd7\
\x00\x00\x036\x00\x00\x00\x00\x00\x01\x00\x00\x80\xe2\
\x00\x00\x01\x9f\x7f&\x83T\
\x00\x00\x00^\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xc1\
\x00\x00\x00~\x00\x00\x00\x00\x00\x01\x00\x00\x14M\
\x00\x00\x01\x9f\x7fY\xce^\
\x00\x00\x00\x82\x00\x00\x00\x00\x00\x01\x00\x00\x11\xcd\
\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x19Y\
\x00\x00\x01\x9f{0\xc9B\
\x00\x00\x01D\x00\x00\x00\x00\x00\x01\x00\x00,\xb6\
\x00\x00\x01d\x00\x00\x00\x00\x00\x01\x00\x004B\
\x00\x00\x01\x9f\x7f&\x83)\
\x00\x00\x02\xc2\x00\x00\x00\x00\x00\x01\x00\x00kU\
\x00\x00\x03\x14\x00\x00\x00\x00\x00\x01\x00\x00{`\
\x00\x00\x01\x9f{C\xf1N\
\x00\x00\x02\x1e\x00\x00\x00\x00\x00\x01\x00\x00R\x8e\
\x00\x00\x02p\x00\x00\x00\x00\x00\x01\x00\x00b\x99\
\x00\x00\x01\x9f\x7f&\x82\xf2\
\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x1a9\
\x00\x00\x00\xd0\x00\x00\x00\x00\x00\x01\x00\x00!\xc5\
\x00\x00\x01\x9f{\x8d\xf34\
\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x9f{0\xc9+\
\x00\x00\x01\xfe\x00\x00\x00\x00\x00\x01\x00\x00J\xbc\
\x00\x00\x02\x1e\x00\x00\x00\x00\x00\x01\x00\x00RH\
\x00\x00\x01\x9f{C\xf1=\
\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x05\x86\
\x00\x00\x01\x9f\x7f&\x83\x99\
\x00\x00\x03\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x84u\
\x00\x00\x03\xf4\x00\x00\x00\x00\x00\x01\x00\x00\x94\x80\
\x00\x00\x01\x9f{{\xa5\xe3\
\x00\x00\x01$\x00\x00\x00\x00\x00\x01\x00\x00'\xef\
\x00\x00\x00^\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xc1\
\x00\x00\x01\x9f\x7f\xac\xf2\xc6\
\x00\x00\x01D\x00\x00\x00\x00\x00\x01\x00\x00/{\
\x00\x00\x01\x9f\x7fV\xd5\xe5\
\x00\x00\x03*\x00\x00\x00\x00\x00\x01\x00\x00x\xac\
\x00\x00\x03|\x00\x00\x00\x00\x00\x01\x00\x00\x88\xb7\
\x00\x00\x01\x9f{0\xc9R\
"

View File

@@ -18,9 +18,9 @@ from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QTransform)
from PySide6.QtWidgets import (QApplication, QDockWidget, QFrame, QHBoxLayout,
QHeaderView, QLabel, QMainWindow, QMenu,
QMenuBar, QSizePolicy, QSpacerItem, QSplitter,
QStackedWidget, QToolBar, QToolButton, QTreeView,
QVBoxLayout, QWidget)
QMenuBar, QPlainTextEdit, QSizePolicy, QSpacerItem,
QSplitter, QStackedWidget, QTabWidget, QToolBar,
QToolButton, QTreeView, QVBoxLayout, QWidget)
from bedit.gui.editors.text_definition import TextDefinitionEditor
from bedit.gui.graphics.workspace import GraphWorkspaceView
@@ -31,77 +31,87 @@ class Ui_MainWindow(object):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(1209, 777)
self.actionSimulationSettings = QAction(MainWindow)
self.actionSimulationSettings.setObjectName(u"actionSimulationSettings")
icon = QIcon()
icon.addFile(u":/icons/icons/preferences-system.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionSimulationSettings.setIcon(icon)
self.actionCompile = QAction(MainWindow)
self.actionCompile.setObjectName(u"actionCompile")
icon1 = QIcon()
icon1.addFile(u":/icons/icons/run-build.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCompile.setIcon(icon1)
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)
icon2 = QIcon()
icon2.addFile(u":/icons/icons/document-new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionNew.setIcon(icon2)
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)
icon3 = QIcon()
icon3.addFile(u":/icons/icons/transform-rotate.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionRotateClockwise.setIcon(icon3)
self.actionZoomIn = QAction(MainWindow)
self.actionZoomIn.setObjectName(u"actionZoomIn")
icon2 = QIcon()
icon2.addFile(u":/icons/icons/zoom-in.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionZoomIn.setIcon(icon2)
icon4 = QIcon()
icon4.addFile(u":/icons/icons/zoom-in.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionZoomIn.setIcon(icon4)
self.actionZoomOut = QAction(MainWindow)
self.actionZoomOut.setObjectName(u"actionZoomOut")
icon3 = QIcon()
icon3.addFile(u":/icons/icons/zoom-out.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionZoomOut.setIcon(icon3)
icon5 = QIcon()
icon5.addFile(u":/icons/icons/zoom-out.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionZoomOut.setIcon(icon5)
self.actionCenterView = QAction(MainWindow)
self.actionCenterView.setObjectName(u"actionCenterView")
icon4 = QIcon()
icon4.addFile(u":/icons/icons/zoom-original.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCenterView.setIcon(icon4)
icon6 = QIcon()
icon6.addFile(u":/icons/icons/zoom-original.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCenterView.setIcon(icon6)
self.actionOpen = QAction(MainWindow)
self.actionOpen.setObjectName(u"actionOpen")
icon5 = QIcon()
icon5.addFile(u":/icons/icons/document-open.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionOpen.setIcon(icon5)
icon7 = QIcon()
icon7.addFile(u":/icons/icons/document-open.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionOpen.setIcon(icon7)
self.actionReloadLibraries = QAction(MainWindow)
self.actionReloadLibraries.setObjectName(u"actionReloadLibraries")
self.actionSave = QAction(MainWindow)
self.actionSave.setObjectName(u"actionSave")
icon6 = QIcon()
icon6.addFile(u":/icons/icons/document-save.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionSave.setIcon(icon6)
icon8 = QIcon()
icon8.addFile(u":/icons/icons/document-save.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionSave.setIcon(icon8)
self.actionSaveAs = QAction(MainWindow)
self.actionSaveAs.setObjectName(u"actionSaveAs")
icon7 = QIcon()
icon7.addFile(u":/icons/icons/document-save-as.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionSaveAs.setIcon(icon7)
icon9 = QIcon()
icon9.addFile(u":/icons/icons/document-save-as.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionSaveAs.setIcon(icon9)
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")
icon8 = QIcon()
icon8.addFile(u":/icons/icons/edit-undo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionUndo.setIcon(icon8)
icon10 = QIcon()
icon10.addFile(u":/icons/icons/edit-undo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionUndo.setIcon(icon10)
self.actionRedo = QAction(MainWindow)
self.actionRedo.setObjectName(u"actionRedo")
icon9 = QIcon()
icon9.addFile(u":/icons/icons/edit-redo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionRedo.setIcon(icon9)
icon11 = QIcon()
icon11.addFile(u":/icons/icons/edit-redo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionRedo.setIcon(icon11)
self.actionCut = QAction(MainWindow)
self.actionCut.setObjectName(u"actionCut")
icon10 = QIcon()
icon10.addFile(u":/icons/icons/edit-cut.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCut.setIcon(icon10)
icon12 = QIcon()
icon12.addFile(u":/icons/icons/edit-cut.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCut.setIcon(icon12)
self.actionCopy = QAction(MainWindow)
self.actionCopy.setObjectName(u"actionCopy")
icon11 = QIcon()
icon11.addFile(u":/icons/icons/edit-copy.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCopy.setIcon(icon11)
icon13 = QIcon()
icon13.addFile(u":/icons/icons/edit-copy.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionCopy.setIcon(icon13)
self.actionPaste = QAction(MainWindow)
self.actionPaste.setObjectName(u"actionPaste")
icon12 = QIcon()
icon12.addFile(u":/icons/icons/edit-paste.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionPaste.setIcon(icon12)
icon14 = QIcon()
icon14.addFile(u":/icons/icons/edit-paste.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionPaste.setIcon(icon14)
self.actionSelectAll = QAction(MainWindow)
self.actionSelectAll.setObjectName(u"actionSelectAll")
self.actionDelete = QAction(MainWindow)
@@ -191,18 +201,18 @@ class Ui_MainWindow(object):
self.workspaceHeaderLayout.setContentsMargins(6, 2, 6, 2)
self.navigateUpButton = QToolButton(self.workspaceHeader)
self.navigateUpButton.setObjectName(u"navigateUpButton")
icon13 = QIcon()
icon13.addFile(u":/icons/icons/arrow-up.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.navigateUpButton.setIcon(icon13)
icon15 = QIcon()
icon15.addFile(u":/icons/icons/arrow-up.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.navigateUpButton.setIcon(icon15)
self.workspaceHeaderLayout.addWidget(self.navigateUpButton)
self.navigateDownButton = QToolButton(self.workspaceHeader)
self.navigateDownButton.setObjectName(u"navigateDownButton")
self.navigateDownButton.setEnabled(False)
icon14 = QIcon()
icon14.addFile(u":/icons/icons/arrow-down.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.navigateDownButton.setIcon(icon14)
icon16 = QIcon()
icon16.addFile(u":/icons/icons/arrow-down.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.navigateDownButton.setIcon(icon16)
self.workspaceHeaderLayout.addWidget(self.navigateDownButton)
@@ -222,9 +232,9 @@ class Ui_MainWindow(object):
self.pointerToolButton = QToolButton(self.workspaceHeader)
self.pointerToolButton.setObjectName(u"pointerToolButton")
icon15 = QIcon()
icon15.addFile(u":/icons/icons/edit-select.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.pointerToolButton.setIcon(icon15)
icon17 = QIcon()
icon17.addFile(u":/icons/icons/edit-select.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.pointerToolButton.setIcon(icon17)
self.pointerToolButton.setCheckable(True)
self.pointerToolButton.setChecked(True)
@@ -232,50 +242,54 @@ class Ui_MainWindow(object):
self.connectToolButton = QToolButton(self.workspaceHeader)
self.connectToolButton.setObjectName(u"connectToolButton")
icon16 = QIcon()
icon16.addFile(u":/icons/icons/network-connect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.connectToolButton.setIcon(icon16)
icon18 = QIcon()
icon18.addFile(u":/icons/icons/network-connect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.connectToolButton.setIcon(icon18)
self.connectToolButton.setCheckable(True)
self.workspaceHeaderLayout.addWidget(self.connectToolButton)
self.boxToolButton = QToolButton(self.workspaceHeader)
self.boxToolButton.setObjectName(u"boxToolButton")
icon17 = QIcon()
icon17.addFile(u":/icons/icons/draw-rectangle.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.boxToolButton.setIcon(icon17)
icon19 = QIcon()
icon19.addFile(u":/icons/icons/draw-rectangle.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.boxToolButton.setIcon(icon19)
self.boxToolButton.setCheckable(True)
self.workspaceHeaderLayout.addWidget(self.boxToolButton)
self.lineToolButton = QToolButton(self.workspaceHeader)
self.lineToolButton.setObjectName(u"lineToolButton")
icon18 = QIcon()
icon18.addFile(u":/icons/icons/draw-bezier-curves.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.lineToolButton.setIcon(icon18)
icon20 = QIcon()
icon20.addFile(u":/icons/icons/draw-bezier-curves.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.lineToolButton.setIcon(icon20)
self.lineToolButton.setCheckable(True)
self.workspaceHeaderLayout.addWidget(self.lineToolButton)
self.textToolButton = QToolButton(self.workspaceHeader)
self.textToolButton.setObjectName(u"textToolButton")
icon19 = QIcon()
icon19.addFile(u":/icons/icons/draw-text.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.textToolButton.setIcon(icon19)
icon21 = QIcon()
icon21.addFile(u":/icons/icons/draw-text.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.textToolButton.setIcon(icon21)
self.textToolButton.setCheckable(True)
self.workspaceHeaderLayout.addWidget(self.textToolButton)
self.rotateToolButton = QToolButton(self.workspaceHeader)
self.rotateToolButton.setObjectName(u"rotateToolButton")
self.rotateToolButton.setIcon(icon1)
self.rotateToolButton.setIcon(icon3)
self.workspaceHeaderLayout.addWidget(self.rotateToolButton)
self.workspaceEditorLayout.addWidget(self.workspaceHeader)
self.workspaceStack = QStackedWidget(self.workspace)
self.workspaceVerticalSplitter = QSplitter(self.workspace)
self.workspaceVerticalSplitter.setObjectName(u"workspaceVerticalSplitter")
self.workspaceVerticalSplitter.setOrientation(Qt.Orientation.Vertical)
self.workspaceVerticalSplitter.setChildrenCollapsible(False)
self.workspaceStack = QStackedWidget(self.workspaceVerticalSplitter)
self.workspaceStack.setObjectName(u"workspaceStack")
self.graphPage = QWidget()
self.graphPage.setObjectName(u"graphPage")
@@ -312,8 +326,26 @@ class Ui_MainWindow(object):
self.emptyPageLayout.addWidget(self.emptyWorkspaceLabel)
self.workspaceStack.addWidget(self.emptyPage)
self.workspaceVerticalSplitter.addWidget(self.workspaceStack)
self.outputTabs = QTabWidget(self.workspaceVerticalSplitter)
self.outputTabs.setObjectName(u"outputTabs")
self.outputTabs.setMinimumSize(QSize(0, 100))
self.logTab = QWidget()
self.logTab.setObjectName(u"logTab")
self.logTabLayout = QVBoxLayout(self.logTab)
self.logTabLayout.setObjectName(u"logTabLayout")
self.logTabLayout.setContentsMargins(0, 0, 0, 0)
self.logOutput = QPlainTextEdit(self.logTab)
self.logOutput.setObjectName(u"logOutput")
self.logOutput.setReadOnly(True)
self.logOutput.setMaximumBlockCount(5000)
self.workspaceEditorLayout.addWidget(self.workspaceStack)
self.logTabLayout.addWidget(self.logOutput)
self.outputTabs.addTab(self.logTab, "")
self.workspaceVerticalSplitter.addWidget(self.outputTabs)
self.workspaceEditorLayout.addWidget(self.workspaceVerticalSplitter)
self.workspaceSplitter.addWidget(self.workspace)
@@ -353,6 +385,10 @@ class Ui_MainWindow(object):
self.cameraToolbar = QToolBar(MainWindow)
self.cameraToolbar.setObjectName(u"cameraToolbar")
MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.cameraToolbar)
self.simulationToolbar = QToolBar(MainWindow)
self.simulationToolbar.setObjectName(u"simulationToolbar")
self.simulationToolbar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.simulationToolbar)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuEdit.menuAction())
@@ -393,6 +429,8 @@ class Ui_MainWindow(object):
self.cameraToolbar.addAction(self.actionZoomIn)
self.cameraToolbar.addAction(self.actionZoomOut)
self.cameraToolbar.addAction(self.actionCenterView)
self.simulationToolbar.addAction(self.actionSimulationSettings)
self.simulationToolbar.addAction(self.actionCompile)
self.retranslateUi(MainWindow)
@@ -404,6 +442,14 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"BEdit", None))
self.actionSimulationSettings.setText(QCoreApplication.translate("MainWindow", u"Simulation Settings", None))
#if QT_CONFIG(statustip)
self.actionSimulationSettings.setStatusTip(QCoreApplication.translate("MainWindow", u"Edit settings stored in the active graph", None))
#endif // QT_CONFIG(statustip)
self.actionCompile.setText(QCoreApplication.translate("MainWindow", u"Compile", None))
#if QT_CONFIG(statustip)
self.actionCompile.setStatusTip(QCoreApplication.translate("MainWindow", u"Compile the active graph for simulation", None))
#endif // QT_CONFIG(statustip)
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))
@@ -528,6 +574,8 @@ class Ui_MainWindow(object):
#endif // QT_CONFIG(tooltip)
self.rotateToolButton.setText(QCoreApplication.translate("MainWindow", u"Rotate", None))
self.emptyWorkspaceLabel.setText(QCoreApplication.translate("MainWindow", u"No document open", None))
self.logOutput.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Application messages appear here.", None))
self.outputTabs.setTabText(self.outputTabs.indexOf(self.logTab), QCoreApplication.translate("MainWindow", u"Log", 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))
@@ -537,5 +585,6 @@ class Ui_MainWindow(object):
self.fileToolbar.setWindowTitle(QCoreApplication.translate("MainWindow", u"File", None))
self.editToolbar.setWindowTitle(QCoreApplication.translate("MainWindow", u"Edit", None))
self.cameraToolbar.setWindowTitle(QCoreApplication.translate("MainWindow", u"Camera", None))
self.simulationToolbar.setWindowTitle(QCoreApplication.translate("MainWindow", u"Simulation", None))
# retranslateUi

View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'simulation_settings_dialog.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 (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QCheckBox, QDialog,
QDialogButtonBox, QGroupBox, QSizePolicy, QSpacerItem,
QVBoxLayout, QWidget)
class Ui_SimulationSettingsDialog(object):
def setupUi(self, SimulationSettingsDialog):
if not SimulationSettingsDialog.objectName():
SimulationSettingsDialog.setObjectName(u"SimulationSettingsDialog")
SimulationSettingsDialog.resize(420, 180)
self.dialogLayout = QVBoxLayout(SimulationSettingsDialog)
self.dialogLayout.setObjectName(u"dialogLayout")
self.settingsGroup = QGroupBox(SimulationSettingsDialog)
self.settingsGroup.setObjectName(u"settingsGroup")
self.settingsLayout = QVBoxLayout(self.settingsGroup)
self.settingsLayout.setObjectName(u"settingsLayout")
self.placeholderCheckBox = QCheckBox(self.settingsGroup)
self.placeholderCheckBox.setObjectName(u"placeholderCheckBox")
self.settingsLayout.addWidget(self.placeholderCheckBox)
self.settingsSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.settingsLayout.addItem(self.settingsSpacer)
self.dialogLayout.addWidget(self.settingsGroup)
self.buttonBox = QDialogButtonBox(SimulationSettingsDialog)
self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.dialogLayout.addWidget(self.buttonBox)
self.retranslateUi(SimulationSettingsDialog)
self.buttonBox.accepted.connect(SimulationSettingsDialog.accept)
self.buttonBox.rejected.connect(SimulationSettingsDialog.reject)
QMetaObject.connectSlotsByName(SimulationSettingsDialog)
# setupUi
def retranslateUi(self, SimulationSettingsDialog):
SimulationSettingsDialog.setWindowTitle(QCoreApplication.translate("SimulationSettingsDialog", u"Simulation Settings", None))
self.settingsGroup.setTitle(QCoreApplication.translate("SimulationSettingsDialog", u"Settings", None))
self.placeholderCheckBox.setText(QCoreApplication.translate("SimulationSettingsDialog", u"Enable simulation option", None))
# retranslateUi

View File

@@ -13,7 +13,9 @@ from PySide6.QtWidgets import (
)
from bedit.core.model import Component, Parameter
from bedit.core.application_log import get_logger
from bedit.core.serializer import JsonDocumentSerializer
from bedit.core.simulation import Simulation
from bedit.gui.controllers.document import DocumentController
from bedit.gui.dialogs.component_options import ComponentOptionsDialog
from bedit.gui.dialogs.item_options import ItemOptionsDialog
@@ -27,8 +29,10 @@ from bedit.gui.models.library_tree import (
)
from bedit.gui.dialogs.settings import SettingsDialog
from bedit.gui.dialogs.port_options import PortOptionsDialog
from bedit.gui.dialogs.simulation_settings import SimulationSettingsDialog
from bedit.gui.preferences import application_settings
from bedit.gui.generated.ui_main_window import Ui_MainWindow
from bedit.gui.application_log import ApplicationLogHandler
class MainWindow(QMainWindow):
@@ -38,11 +42,19 @@ class MainWindow(QMainWindow):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.application_logger = get_logger()
self.log = get_logger("ui")
self.log_handler = ApplicationLogHandler(self.ui.logOutput)
self.application_logger.addHandler(self.log_handler)
self.application_logger.setLevel("INFO")
self.ui.workspaceVerticalSplitter.setSizes([580, 160])
self.log.info("BEdit started")
self.settings = application_settings()
self._applying_text_definition = False
self.libraries = LibraryRepository(self)
self.document_controller = DocumentController(self)
self.simulation = Simulation()
self.document_controller = DocumentController(self, simulation=self.simulation)
self.library_tree_model = LibraryTreeModel(
self.libraries,
self.document_controller,
@@ -141,6 +153,10 @@ class MainWindow(QMainWindow):
self.ui.actionZoomIn.triggered.connect(self.ui.graphView.zoom_in)
self.ui.actionZoomOut.triggered.connect(self.ui.graphView.zoom_out)
self.ui.actionCenterView.triggered.connect(self.ui.graphView.center_workspace)
self.ui.actionSimulationSettings.triggered.connect(
self.show_simulation_settings
)
self.ui.actionCompile.triggered.connect(self.compile_active_graph)
self.document_controller.undo_stack.canUndoChanged.connect(self.ui.actionUndo.setEnabled)
self.document_controller.undo_stack.canRedoChanged.connect(self.ui.actionRedo.setEnabled)
self.document_controller.modifiedChanged.connect(lambda _modified: self._update_title())
@@ -158,19 +174,40 @@ class MainWindow(QMainWindow):
self.ui.fileToolbar,
self.ui.editToolbar,
self.ui.cameraToolbar,
self.ui.simulationToolbar,
):
self.ui.menuToolbars.addAction(toolbar.toggleViewAction())
def reload_libraries(self) -> None:
self.libraries.load_paths(SettingsDialog.library_paths(self.settings))
self.ui.treeView.expandAll()
self.log.info("Reloaded %d libraries", len(self.libraries.libraries))
if self.libraries.load_warnings:
for warning in self.libraries.load_warnings:
self.log.warning("Library load failed: %s", warning)
QMessageBox.warning(
self,
"Some libraries could not be loaded",
"\n".join(self.libraries.load_warnings),
)
@Slot()
def show_simulation_settings(self) -> None:
component = self.document_controller.active_component
if component is None or component.implementation_kind != "graph":
return
dialog = SimulationSettingsDialog(component.graph.simulation_settings, self)
if dialog.exec() == dialog.DialogCode.Accepted:
self.document_controller.edit_simulation_settings(dialog.settings)
@Slot()
def compile_active_graph(self) -> None:
try:
self.document_controller.compile_active_graph()
except ValueError as error:
self.log.error("Compile failed: %s", error)
QMessageBox.warning(self, "Cannot compile", str(error))
def _restore_window_geometry(self) -> None:
geometry = self.settings.value("window/geometry")
if geometry is not None:
@@ -196,6 +233,8 @@ class MainWindow(QMainWindow):
self.ui.workspaceModeLabel.setText("")
self.ui.workspaceStack.setCurrentWidget(self.ui.emptyPage)
self._set_graph_controls_visible(False)
self.ui.actionSimulationSettings.setEnabled(False)
self.ui.actionCompile.setEnabled(False)
self._update_edit_actions()
return
self.ui.graphBreadcrumbLabel.setText(" ".join(self.document_controller.breadcrumb()))
@@ -203,6 +242,8 @@ class MainWindow(QMainWindow):
self.document_controller.document.find_parent(component.id) is not None
)
is_graph = component.implementation_kind == "graph"
self.ui.actionSimulationSettings.setEnabled(is_graph)
self.ui.actionCompile.setEnabled(is_graph)
self.ui.workspaceModeLabel.setText("Graph" if is_graph else "Text")
self.ui.workspaceStack.setCurrentWidget(
self.ui.graphPage if is_graph else self.ui.textPage
@@ -314,6 +355,7 @@ class MainWindow(QMainWindow):
finally:
self._applying_text_definition = False
except (TypeError, ValueError) as error:
self.log.error("Text component update failed: %s", error)
QMessageBox.critical(self, "Invalid text component", str(error))
self._load_text_definition()
return False
@@ -351,11 +393,14 @@ class MainWindow(QMainWindow):
def new_document(self) -> None:
if self._resolve_source_edits() and self._maybe_save():
self.document_controller.new_document()
self.log.info("Created a new document")
@Slot()
def close_document(self) -> None:
if self._resolve_source_edits() and self._maybe_save():
path = self.document_controller.file_path
self.document_controller.close_document()
self.log.info("Closed document%s", f" {path}" if path else "")
@Slot()
def open_document(self) -> None:
@@ -368,7 +413,9 @@ class MainWindow(QMainWindow):
return
try:
self.document_controller.load(Path(filename))
self.log.info("Opened document %s", filename)
except (OSError, ValueError) as error:
self.log.error("Could not open document %s: %s", filename, error)
QMessageBox.critical(self, "Could not open graph", str(error))
@Slot()
@@ -378,8 +425,10 @@ class MainWindow(QMainWindow):
if self.document_controller.file_path is None:
return self.save_document_as()
try:
self.document_controller.save()
path = self.document_controller.save()
self.log.info("Saved document %s", path)
except OSError as error:
self.log.error("Could not save document: %s", error)
QMessageBox.critical(self, "Could not save graph", str(error))
return False
return True
@@ -397,8 +446,10 @@ class MainWindow(QMainWindow):
if not filename:
return False
try:
self.document_controller.save(Path(filename))
path = self.document_controller.save(Path(filename))
self.log.info("Saved document %s", path)
except OSError as error:
self.log.error("Could not save document %s: %s", filename, error)
QMessageBox.critical(self, "Could not save graph", str(error))
return False
return True
@@ -509,6 +560,7 @@ class MainWindow(QMainWindow):
JsonDocumentSerializer.save(library.document, Path(library.source_path))
except (OSError, ValueError) as error:
component.inputs, component.outputs = old_inputs, old_outputs
self.log.error("Could not change library ports: %s", error)
QMessageBox.warning(self, "Cannot change library ports", str(error))
self.library_tree_model.rebuild()
@@ -526,6 +578,7 @@ class MainWindow(QMainWindow):
component_id, dialog.inputs, dialog.outputs
)
except ValueError as error:
self.log.error("Could not change component ports: %s", error)
QMessageBox.warning(self, "Cannot change ports", str(error))
@Slot(str)
@@ -547,6 +600,7 @@ class MainWindow(QMainWindow):
dialog.ui.showNameCheckBox.isChecked(),
)
except ValueError as error:
self.log.error("Could not rename component: %s", error)
QMessageBox.warning(self, "Cannot rename component", str(error))
@Slot(str, str)
@@ -595,4 +649,6 @@ class MainWindow(QMainWindow):
event.ignore()
return
self.settings.setValue("window/geometry", self.saveGeometry())
self.log.info("BEdit closed")
self.application_logger.removeHandler(self.log_handler)
event.accept()

View File

@@ -364,6 +364,9 @@
</widget>
</item>
<item>
<widget class="QSplitter" name="workspaceVerticalSplitter">
<property name="orientation"><enum>Qt::Orientation::Vertical</enum></property>
<property name="childrenCollapsible"><bool>false</bool></property>
<widget class="QStackedWidget" name="workspaceStack">
<property name="currentIndex">
<number>0</number>
@@ -427,6 +430,20 @@
</layout>
</widget>
</widget>
<widget class="QTabWidget" name="outputTabs">
<property name="minimumSize"><size><width>0</width><height>100</height></size></property>
<widget class="QWidget" name="logTab">
<attribute name="title"><string>Log</string></attribute>
<layout class="QVBoxLayout" name="logTabLayout">
<property name="leftMargin"><number>0</number></property>
<property name="topMargin"><number>0</number></property>
<property name="rightMargin"><number>0</number></property>
<property name="bottomMargin"><number>0</number></property>
<item><widget class="QPlainTextEdit" name="logOutput"><property name="readOnly"><bool>true</bool></property><property name="maximumBlockCount"><number>5000</number></property><property name="placeholderText"><string>Application messages appear here.</string></property></widget></item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
@@ -570,6 +587,46 @@
<addaction name="actionZoomOut"/>
<addaction name="actionCenterView"/>
</widget>
<widget class="QToolBar" name="simulationToolbar">
<property name="windowTitle">
<string>Simulation</string>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonStyle::ToolButtonIconOnly</enum>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionSimulationSettings"/>
<addaction name="actionCompile"/>
</widget>
<action name="actionSimulationSettings">
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/icons/preferences-system.png</normaloff>:/icons/icons/preferences-system.png</iconset>
</property>
<property name="text">
<string>Simulation Settings</string>
</property>
<property name="statusTip">
<string>Edit settings stored in the active graph</string>
</property>
</action>
<action name="actionCompile">
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/icons/run-build.png</normaloff>:/icons/icons/run-build.png</iconset>
</property>
<property name="text">
<string>Compile</string>
</property>
<property name="statusTip">
<string>Compile the active graph for simulation</string>
</property>
</action>
<action name="actionNew">
<property name="icon">
<iconset resource="../resources/resources.qrc">

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SimulationSettingsDialog</class>
<widget class="QDialog" name="SimulationSettingsDialog">
<property name="geometry"><rect><x>0</x><y>0</y><width>420</width><height>180</height></rect></property>
<property name="windowTitle"><string>Simulation Settings</string></property>
<layout class="QVBoxLayout" name="dialogLayout">
<item><widget class="QGroupBox" name="settingsGroup"><property name="title"><string>Settings</string></property><layout class="QVBoxLayout" name="settingsLayout"><item><widget class="QCheckBox" name="placeholderCheckBox"><property name="text"><string>Enable simulation option</string></property></widget></item><item><spacer name="settingsSpacer"><property name="orientation"><enum>Qt::Orientation::Vertical</enum></property><property name="sizeHint" stdset="0"><size><width>20</width><height>40</height></size></property></spacer></item></layout></widget></item>
<item><widget class="QDialogButtonBox" name="buttonBox"><property name="standardButtons"><set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set></property></widget></item>
</layout>
</widget>
<resources/>
<connections>
<connection><sender>buttonBox</sender><signal>accepted()</signal><receiver>SimulationSettingsDialog</receiver><slot>accept()</slot><hints/></connection>
<connection><sender>buttonBox</sender><signal>rejected()</signal><receiver>SimulationSettingsDialog</receiver><slot>reject()</slot><hints/></connection>
</connections>
</ui>

View File

@@ -908,7 +908,10 @@
},
"type": "signal"
}
]
],
"simulation": {
"enabled": true
}
}
}
}