From 9ed8e371ef5fc3503204e40f8e5a37fdbe11dd77 Mon Sep 17 00:00:00 2001 From: Joppe Blondel Date: Mon, 20 Jul 2026 15:26:18 +0200 Subject: [PATCH] added stub simulation entry and logs --- BEdit/AGENTS.md | 9 +- BEdit/resources/icons/preferences-system.png | Bin 0 -> 2171 bytes BEdit/resources/icons/run-build.png | Bin 0 -> 1928 bytes BEdit/resources/resources.qrc | 2 + BEdit/resources/resources_rc.py | 324 ++++++++++++++++-- BEdit/src/bedit/core/application_log.py | 9 + BEdit/src/bedit/core/model.py | 4 + BEdit/src/bedit/core/simulation/__init__.py | 3 + BEdit/src/bedit/core/simulation/service.py | 18 + BEdit/src/bedit/gui/application_log.py | 26 ++ BEdit/src/bedit/gui/controllers/commands.py | 13 + BEdit/src/bedit/gui/controllers/document.py | 28 +- .../bedit/gui/dialogs/simulation_settings.py | 22 ++ BEdit/src/bedit/gui/generated/resources_rc.py | 324 ++++++++++++++++-- .../src/bedit/gui/generated/ui_main_window.py | 181 ++++++---- .../ui_simulation_settings_dialog.py | 64 ++++ BEdit/src/bedit/gui/main_window.py | 62 +++- BEdit/ui/main_window.ui | 59 +++- BEdit/ui/simulation_settings_dialog.ui | 17 + BEdit/untitled.bedit.json | 5 +- 20 files changed, 1047 insertions(+), 123 deletions(-) create mode 100644 BEdit/resources/icons/preferences-system.png create mode 100644 BEdit/resources/icons/run-build.png create mode 100644 BEdit/src/bedit/core/application_log.py create mode 100644 BEdit/src/bedit/core/simulation/__init__.py create mode 100644 BEdit/src/bedit/core/simulation/service.py create mode 100644 BEdit/src/bedit/gui/application_log.py create mode 100644 BEdit/src/bedit/gui/dialogs/simulation_settings.py create mode 100644 BEdit/src/bedit/gui/generated/ui_simulation_settings_dialog.py create mode 100644 BEdit/ui/simulation_settings_dialog.ui diff --git a/BEdit/AGENTS.md b/BEdit/AGENTS.md index f0ea503..930acc5 100644 --- a/BEdit/AGENTS.md +++ b/BEdit/AGENTS.md @@ -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 diff --git a/BEdit/resources/icons/preferences-system.png b/BEdit/resources/icons/preferences-system.png new file mode 100644 index 0000000000000000000000000000000000000000..2afbd23ccc523e9ff83d22d5dfa13b46db40b604 GIT binary patch literal 2171 zcmV->2!!{EP)hXMG%fYvC20^tL~c)}RIH(0J;c=RcM|JC$4jtl*dihrl9tgPte z;u-Jj4qB6sANMV*s3`AM+}hg8Ezqr`r1TvXT$4K}*ik($X#^B^fz6Igq@N^pdEn zuY;kX;S%oB0ogI8KP%pP0b!cFyuA76tJf4ffBwAJgzHUA6P7JoraTe}T8g`O@BY`_ zdvVCf$Us$f6{-mp6&1L0^~Y8l8=Ey;=CH@%jlsb|1z*wPI)9Zygo)f=0$pKdNogsX zo10-{`(?U?g~in20+ZI1a{=dCl9H3S+3_Il0SXHX@$6Y4l2ej!((n7u*gLV3)YMe? z`W|c7(b2Kt@i0^Y8BY)j1iGno12SVh9KPC}ziZd78Sf&&)7Hw$%KGL{HxO_>2qFIn zf#*?A`1txFD?5_{I~^gRA>hix!on!X?aS%REdH+Hs@OP!w!Xf>-MYGZR8W>U?{_J3 zc6OdKm;?{j!-o$gapxID74v9YJPqyzzhf#p<877hq1dGh@Zx?LB~o}l0h*l1yf{KvU)baKRv zn>RqNfw#98+`o0}*3{JekLqiiw>n) z2FmjMdZc9`D7v7CpdD;qf1r&7nFb6BCKc-O_cJpyVf}>_3=H+5rS%Dp9XkecN1OQ3 z-#a?XK1AEod63>550M{h_-Haylf?=nDi;U@YS*q^%T7s2!KO{-bZb4#pFh7h#ZI0) z+3IRBHe7s_bvNB-=#FQ2mCR5R%kb|bEb_0Pu?5@`!x0%)`Knc`X3{Y6G&VMtI-?o% z^mQ?7_DoRvAt)%QorvuFT#Gr}{+hMdU1n&BW9TelkQOjBXD~F16uJ|A6vLF2nc`3+ zhE#59YPujkKE5s>}hMyE(_2AA#Yp#0^UA5UX04^Fbq7Y~R;#RPQJ?L|Yf5{iH7CGlj*T&6Ub#IKZ7 zp)nANL=wLr{9tUdo}yg`Q&p$J*w`3RQBgfa?lSLWgDV>UO_=Fz&C-j>9Sw;T55;5A zQELlnjuON%6c_tgsoAGYbzqHnOF7l`)l}D;Jv=}25z|{Lm{GyzuSu+~N zS|8C+di?w25DIlMWr_-Dk_8V!Cnu--yfYFMMkrPj$0;zA^E+96ZTuS&^*@hBN0m0D zS${xv0>j+{?1lRV_D4>l_mG;J+H|_P)YH@R*}8S>A}PHZU0n8qx)&lMB0%kzmsp_; ze_Z-WOi~!bR{Q^%Rn-uQ%G;EhF{98~IUCYUMbxD-WO=g^UReoD-p$R;jv~H^zAq=t zUb}X!Gnt(GoacKf?R`f0;C&|KzKME;-xgL<6-y){-k`Hw3C$@AkX&Nzea(uEr9Tbd z#>K_8X=rH7w zC*6-#(B5LMyI)8}C=~hx2M4$K`}?<%&&8^$s(;}ol2N%Vqn)O<26M93V@K%wCw+UP xe#rvDbb=9K6JaS~vP{ZlCn3jO$uY1${1>on!Ni=?(fj}a002ovPDHLkV1h5bE#?3K literal 0 HcmV?d00001 diff --git a/BEdit/resources/icons/run-build.png b/BEdit/resources/icons/run-build.png new file mode 100644 index 0000000000000000000000000000000000000000..85a12e87f9158bf9b95b0358789c830d970fe5c2 GIT binary patch literal 1928 zcmV;32Y2|1P)Px#32;bRa{vGh*8l(w*8xH(n|J^K00(qQO+^RU2nzr$AG5Z6od5s;24YJ`L;(K) z{{a7>y{D4^00#(3L_t(|+Pzn4OjK7GmD-?CFKT0&j zC^4-~Y}FzvXe@|J&|)j56VW?&eWVIPKJ*pz({RFI%{PVae{nM|mN zCf1jHx%b`ozH`pE-1}xGyy_JY5Wr!gun*U-U%z!uo}Qi)-o*8mOeWJj zdh`fGLqn2ro}Qkr=Cd~;U}|DwV&BNf2!@A;CFA_YjT^mu_Bx5|moH!b$#d2}$jrvjHQu~=v-`Dc*V6Cb zzmLJeK}!iZ@9XPBTwGkbySw{0d{>xFNJz+^&z?O)RaKRam@iB2l$)E|LC}GL0VC@h z6&01MP$(K1E!Q7DeE3}Bv&WAgL#b3Y#K*_WIVVPce?N3OT@SIxt?$j7H*4t7+uKWH zWD>YoGcs~voR2oHTbof3Oa47QJ?QN0M0a;LL}1K>!NdQt4n(C?%lg+X=yPwH8sJ_&28I@GBC}{%d3#i zt*xyTLLiF>Mp{~03YAKQva&K53CzAdHCf!oosnWF#~KH#9T|Gf<2)mltM1-zc)u)`olc?n&!)b#*v$ zWWUIh_XI%CT8#e4RrIrAKq?wU33JNhr-h|Z5Sj4CPhJ>`UNW2{dtyW8NcDpoJ z70Xep(@Lc(Dk?%{WhKO!uzfuKi6>8Q$~=w~ zBLz`gTMKfC`zE8&IL!$ewY!ni zya$RlA1K>>kb8F@vh+V8Jbw|+MeZdI_dK$*vq{bXh)HbXQ3Uey^C7rDrhtt?4eQKQ z@ddL83b2T(sn){(cMqIb%tUC>G9+lfMpC^8k_;aBTj!3j(p5O8bb{}>wNMqPap%q* zGQBJZ6SV4b;JI& z1@O=K2;Sk-a5Cs9`-w(!`dK0;HaW;8INQiQ!PeH+h7(~cnUX_?4jHJ@Tn?sCd7UjjNtQW7Gf!Vr8mNZ4t_T#iO= zo{Xg2a@;*^VAh?bU?9fO(9j3Oei;F_-rn9CHVQkH#$A#rPsT09RouRv&NTCclNwdp zvK3YAbT*s$_-6X~%+e1B4jj-BdpvTR{rmTyVZC^WkeG)QQ^>lOYD%CtXc-~F&5g=4 zgU2>MTPCs=;(Mtj5Dp9RM?^`SFSv=7Qs2!_-s^@%_Ih~i21s%_hMpVx>y=E6^(PQ z@!5noaM=e32Wz + icons/run-build.png + icons/preferences-system.png icons/draw-triangle.png icons/draw-ellipse.png icons/draw-circle.png diff --git a/BEdit/resources/resources_rc.py b/BEdit/resources/resources_rc.py index 276b470..311a8cf 100644 --- a/BEdit/resources/resources_rc.py +++ b/BEdit/resources/resources_rc.py @@ -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\ +}\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{\ +\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 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}") diff --git a/BEdit/src/bedit/core/model.py b/BEdit/src/bedit/core/model.py index 3e8f761..0b8c1d4 100644 --- a/BEdit/src/bedit/core/model.py +++ b/BEdit/src/bedit/core/model.py @@ -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()), diff --git a/BEdit/src/bedit/core/simulation/__init__.py b/BEdit/src/bedit/core/simulation/__init__.py new file mode 100644 index 0000000..41515e1 --- /dev/null +++ b/BEdit/src/bedit/core/simulation/__init__.py @@ -0,0 +1,3 @@ +from bedit.core.simulation.service import Simulation + +__all__ = ["Simulation"] diff --git a/BEdit/src/bedit/core/simulation/service.py b/BEdit/src/bedit/core/simulation/service.py new file mode 100644 index 0000000..348e2e6 --- /dev/null +++ b/BEdit/src/bedit/core/simulation/service.py @@ -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) diff --git a/BEdit/src/bedit/gui/application_log.py b/BEdit/src/bedit/gui/application_log.py new file mode 100644 index 0000000..9869971 --- /dev/null +++ b/BEdit/src/bedit/gui/application_log.py @@ -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) diff --git a/BEdit/src/bedit/gui/controllers/commands.py b/BEdit/src/bedit/gui/controllers/commands.py index b856f9d..97c8a08 100644 --- a/BEdit/src/bedit/gui/controllers/commands.py +++ b/BEdit/src/bedit/gui/controllers/commands.py @@ -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") diff --git a/BEdit/src/bedit/gui/controllers/document.py b/BEdit/src/bedit/gui/controllers/document.py index c0ca3e7..8af72ab 100644 --- a/BEdit/src/bedit/gui/controllers/document.py +++ b/BEdit/src/bedit/gui/controllers/document.py @@ -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 diff --git a/BEdit/src/bedit/gui/dialogs/simulation_settings.py b/BEdit/src/bedit/gui/dialogs/simulation_settings.py new file mode 100644 index 0000000..00552d7 --- /dev/null +++ b/BEdit/src/bedit/gui/dialogs/simulation_settings.py @@ -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 diff --git a/BEdit/src/bedit/gui/generated/resources_rc.py b/BEdit/src/bedit/gui/generated/resources_rc.py index 276b470..311a8cf 100644 --- a/BEdit/src/bedit/gui/generated/resources_rc.py +++ b/BEdit/src/bedit/gui/generated/resources_rc.py @@ -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\ +}\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{\ +\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 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() diff --git a/BEdit/ui/main_window.ui b/BEdit/ui/main_window.ui index 3abb5a1..4ba5f72 100644 --- a/BEdit/ui/main_window.ui +++ b/BEdit/ui/main_window.ui @@ -364,7 +364,10 @@ - + + Qt::Orientation::Vertical + false + 0 @@ -426,6 +429,20 @@ + + + 0100 + + Log + + 0 + 0 + 0 + 0 + true5000Application messages appear here. + + + @@ -570,6 +587,46 @@ + + + Simulation + + + Qt::ToolButtonStyle::ToolButtonIconOnly + + + TopToolBarArea + + + false + + + + + + + + :/icons/icons/preferences-system.png:/icons/icons/preferences-system.png + + + Simulation Settings + + + Edit settings stored in the active graph + + + + + + :/icons/icons/run-build.png:/icons/icons/run-build.png + + + Compile + + + Compile the active graph for simulation + + diff --git a/BEdit/ui/simulation_settings_dialog.ui b/BEdit/ui/simulation_settings_dialog.ui new file mode 100644 index 0000000..a646fc9 --- /dev/null +++ b/BEdit/ui/simulation_settings_dialog.ui @@ -0,0 +1,17 @@ + + + SimulationSettingsDialog + + 00420180 + Simulation Settings + + SettingsEnable simulation optionQt::Orientation::Vertical2040 + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + + + buttonBoxaccepted()SimulationSettingsDialogaccept() + buttonBoxrejected()SimulationSettingsDialogreject() + + diff --git a/BEdit/untitled.bedit.json b/BEdit/untitled.bedit.json index ee96c86..6c56f8c 100644 --- a/BEdit/untitled.bedit.json +++ b/BEdit/untitled.bedit.json @@ -908,7 +908,10 @@ }, "type": "signal" } - ] + ], + "simulation": { + "enabled": true + } } } }