meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7020__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/__init__.py +1 -1
- meerk40t/balormk/balor_params.py +167 -167
- meerk40t/balormk/clone_loader.py +457 -457
- meerk40t/balormk/controller.py +1566 -1512
- meerk40t/balormk/cylindermod.py +64 -0
- meerk40t/balormk/device.py +966 -1959
- meerk40t/balormk/driver.py +778 -591
- meerk40t/balormk/galvo_commands.py +1194 -0
- meerk40t/balormk/gui/balorconfig.py +237 -111
- meerk40t/balormk/gui/balorcontroller.py +191 -184
- meerk40t/balormk/gui/baloroperationproperties.py +116 -115
- meerk40t/balormk/gui/corscene.py +845 -0
- meerk40t/balormk/gui/gui.py +179 -147
- meerk40t/balormk/livelightjob.py +466 -382
- meerk40t/balormk/mock_connection.py +131 -109
- meerk40t/balormk/plugin.py +133 -135
- meerk40t/balormk/usb_connection.py +306 -301
- meerk40t/camera/__init__.py +1 -1
- meerk40t/camera/camera.py +514 -397
- meerk40t/camera/gui/camerapanel.py +1241 -1095
- meerk40t/camera/gui/gui.py +58 -58
- meerk40t/camera/plugin.py +441 -399
- meerk40t/ch341/__init__.py +27 -27
- meerk40t/ch341/ch341device.py +628 -628
- meerk40t/ch341/libusb.py +595 -589
- meerk40t/ch341/mock.py +171 -171
- meerk40t/ch341/windriver.py +157 -157
- meerk40t/constants.py +13 -0
- meerk40t/core/__init__.py +1 -1
- meerk40t/core/bindalias.py +550 -539
- meerk40t/core/core.py +47 -47
- meerk40t/core/cutcode/cubiccut.py +73 -73
- meerk40t/core/cutcode/cutcode.py +315 -312
- meerk40t/core/cutcode/cutgroup.py +141 -137
- meerk40t/core/cutcode/cutobject.py +192 -185
- meerk40t/core/cutcode/dwellcut.py +37 -37
- meerk40t/core/cutcode/gotocut.py +29 -29
- meerk40t/core/cutcode/homecut.py +29 -29
- meerk40t/core/cutcode/inputcut.py +34 -34
- meerk40t/core/cutcode/linecut.py +33 -33
- meerk40t/core/cutcode/outputcut.py +34 -34
- meerk40t/core/cutcode/plotcut.py +335 -335
- meerk40t/core/cutcode/quadcut.py +61 -61
- meerk40t/core/cutcode/rastercut.py +168 -148
- meerk40t/core/cutcode/waitcut.py +34 -34
- meerk40t/core/cutplan.py +1843 -1316
- meerk40t/core/drivers.py +330 -329
- meerk40t/core/elements/align.py +801 -669
- meerk40t/core/elements/branches.py +1858 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4595 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4315 -3617
- meerk40t/core/elements/files.py +117 -64
- meerk40t/core/elements/geometry.py +473 -224
- meerk40t/core/elements/grid.py +467 -316
- meerk40t/core/elements/materials.py +158 -94
- meerk40t/core/elements/notes.py +50 -38
- meerk40t/core/elements/offset_clpr.py +934 -912
- meerk40t/core/elements/offset_mk.py +963 -955
- meerk40t/core/elements/penbox.py +339 -267
- meerk40t/core/elements/placements.py +300 -83
- meerk40t/core/elements/render.py +785 -687
- meerk40t/core/elements/shapes.py +2618 -2092
- meerk40t/core/elements/testcases.py +105 -0
- meerk40t/core/elements/trace.py +651 -563
- meerk40t/core/elements/tree_commands.py +415 -409
- meerk40t/core/elements/undo_redo.py +116 -58
- meerk40t/core/elements/wordlist.py +319 -200
- meerk40t/core/exceptions.py +9 -9
- meerk40t/core/laserjob.py +220 -220
- meerk40t/core/logging.py +63 -63
- meerk40t/core/node/blobnode.py +83 -86
- meerk40t/core/node/bootstrap.py +105 -103
- meerk40t/core/node/branch_elems.py +40 -31
- meerk40t/core/node/branch_ops.py +45 -38
- meerk40t/core/node/branch_regmark.py +48 -41
- meerk40t/core/node/cutnode.py +29 -32
- meerk40t/core/node/effect_hatch.py +375 -257
- meerk40t/core/node/effect_warp.py +398 -0
- meerk40t/core/node/effect_wobble.py +441 -309
- meerk40t/core/node/elem_ellipse.py +404 -309
- meerk40t/core/node/elem_image.py +1082 -801
- meerk40t/core/node/elem_line.py +358 -292
- meerk40t/core/node/elem_path.py +259 -201
- meerk40t/core/node/elem_point.py +129 -102
- meerk40t/core/node/elem_polyline.py +310 -246
- meerk40t/core/node/elem_rect.py +376 -286
- meerk40t/core/node/elem_text.py +445 -418
- meerk40t/core/node/filenode.py +59 -40
- meerk40t/core/node/groupnode.py +138 -74
- meerk40t/core/node/image_processed.py +777 -766
- meerk40t/core/node/image_raster.py +156 -113
- meerk40t/core/node/layernode.py +31 -31
- meerk40t/core/node/mixins.py +135 -107
- meerk40t/core/node/node.py +1427 -1304
- meerk40t/core/node/nutils.py +117 -114
- meerk40t/core/node/op_cut.py +463 -335
- meerk40t/core/node/op_dots.py +296 -251
- meerk40t/core/node/op_engrave.py +414 -311
- meerk40t/core/node/op_image.py +755 -369
- meerk40t/core/node/op_raster.py +787 -522
- meerk40t/core/node/place_current.py +37 -40
- meerk40t/core/node/place_point.py +329 -126
- meerk40t/core/node/refnode.py +58 -47
- meerk40t/core/node/rootnode.py +225 -219
- meerk40t/core/node/util_console.py +48 -48
- meerk40t/core/node/util_goto.py +84 -65
- meerk40t/core/node/util_home.py +61 -61
- meerk40t/core/node/util_input.py +102 -102
- meerk40t/core/node/util_output.py +102 -102
- meerk40t/core/node/util_wait.py +65 -65
- meerk40t/core/parameters.py +709 -707
- meerk40t/core/planner.py +875 -785
- meerk40t/core/plotplanner.py +656 -652
- meerk40t/core/space.py +120 -113
- meerk40t/core/spoolers.py +706 -705
- meerk40t/core/svg_io.py +1836 -1549
- meerk40t/core/treeop.py +534 -445
- meerk40t/core/undos.py +278 -124
- meerk40t/core/units.py +784 -680
- meerk40t/core/view.py +393 -322
- meerk40t/core/webhelp.py +62 -62
- meerk40t/core/wordlist.py +513 -504
- meerk40t/cylinder/cylinder.py +247 -0
- meerk40t/cylinder/gui/cylindersettings.py +41 -0
- meerk40t/cylinder/gui/gui.py +24 -0
- meerk40t/device/__init__.py +1 -1
- meerk40t/device/basedevice.py +322 -123
- meerk40t/device/devicechoices.py +50 -0
- meerk40t/device/dummydevice.py +163 -128
- meerk40t/device/gui/defaultactions.py +618 -602
- meerk40t/device/gui/effectspanel.py +114 -0
- meerk40t/device/gui/formatterpanel.py +253 -290
- meerk40t/device/gui/warningpanel.py +337 -260
- meerk40t/device/mixins.py +13 -13
- meerk40t/dxf/__init__.py +1 -1
- meerk40t/dxf/dxf_io.py +766 -554
- meerk40t/dxf/plugin.py +47 -35
- meerk40t/external_plugins.py +79 -79
- meerk40t/external_plugins_build.py +28 -28
- meerk40t/extra/cag.py +112 -116
- meerk40t/extra/coolant.py +403 -0
- meerk40t/extra/encode_detect.py +204 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +834 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +628 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +283 -0
- meerk40t/extra/param_functions.py +1542 -1556
- meerk40t/extra/potrace.py +257 -253
- meerk40t/extra/serial_exchange.py +118 -0
- meerk40t/extra/updater.py +602 -453
- meerk40t/extra/vectrace.py +147 -146
- meerk40t/extra/winsleep.py +83 -83
- meerk40t/extra/xcs_reader.py +597 -0
- meerk40t/fill/fills.py +781 -335
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +614 -567
- meerk40t/grbl/control.py +87 -87
- meerk40t/grbl/controller.py +990 -903
- meerk40t/grbl/device.py +1084 -768
- meerk40t/grbl/driver.py +989 -771
- meerk40t/grbl/emulator.py +532 -497
- meerk40t/grbl/gcodejob.py +783 -767
- meerk40t/grbl/gui/grblconfiguration.py +373 -298
- meerk40t/grbl/gui/grblcontroller.py +485 -271
- meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
- meerk40t/grbl/gui/grbloperationconfig.py +105 -0
- meerk40t/grbl/gui/gui.py +147 -116
- meerk40t/grbl/interpreter.py +44 -44
- meerk40t/grbl/loader.py +22 -22
- meerk40t/grbl/mock_connection.py +56 -56
- meerk40t/grbl/plugin.py +294 -264
- meerk40t/grbl/serial_connection.py +93 -88
- meerk40t/grbl/tcp_connection.py +81 -79
- meerk40t/grbl/ws_connection.py +112 -0
- meerk40t/gui/__init__.py +1 -1
- meerk40t/gui/about.py +2042 -296
- meerk40t/gui/alignment.py +1644 -1608
- meerk40t/gui/autoexec.py +199 -0
- meerk40t/gui/basicops.py +791 -670
- meerk40t/gui/bufferview.py +77 -71
- meerk40t/gui/busy.py +232 -133
- meerk40t/gui/choicepropertypanel.py +1662 -1469
- meerk40t/gui/consolepanel.py +706 -542
- meerk40t/gui/devicepanel.py +687 -581
- meerk40t/gui/dialogoptions.py +110 -107
- meerk40t/gui/executejob.py +316 -306
- meerk40t/gui/fonts.py +90 -90
- meerk40t/gui/functionwrapper.py +252 -0
- meerk40t/gui/gui_mixins.py +729 -0
- meerk40t/gui/guicolors.py +205 -182
- meerk40t/gui/help_assets/help_assets.py +218 -201
- meerk40t/gui/helper.py +154 -0
- meerk40t/gui/hersheymanager.py +1440 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +897 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2944 -0
- meerk40t/gui/materialtest.py +1722 -1694
- meerk40t/gui/mkdebug.py +646 -359
- meerk40t/gui/mwindow.py +163 -140
- meerk40t/gui/navigationpanels.py +2605 -2467
- meerk40t/gui/notes.py +143 -142
- meerk40t/gui/opassignment.py +414 -410
- meerk40t/gui/operation_info.py +310 -299
- meerk40t/gui/plugin.py +500 -328
- meerk40t/gui/position.py +714 -669
- meerk40t/gui/preferences.py +901 -650
- meerk40t/gui/propertypanels/attributes.py +1461 -1131
- meerk40t/gui/propertypanels/blobproperty.py +117 -114
- meerk40t/gui/propertypanels/consoleproperty.py +83 -80
- meerk40t/gui/propertypanels/gotoproperty.py +77 -0
- meerk40t/gui/propertypanels/groupproperties.py +223 -217
- meerk40t/gui/propertypanels/hatchproperty.py +489 -469
- meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
- meerk40t/gui/propertypanels/inputproperty.py +59 -58
- meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
- meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
- meerk40t/gui/propertypanels/outputproperty.py +59 -58
- meerk40t/gui/propertypanels/pathproperty.py +389 -380
- meerk40t/gui/propertypanels/placementproperty.py +1214 -383
- meerk40t/gui/propertypanels/pointproperty.py +140 -136
- meerk40t/gui/propertypanels/propertywindow.py +313 -181
- meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
- meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
- meerk40t/gui/propertypanels/textproperty.py +770 -755
- meerk40t/gui/propertypanels/waitproperty.py +56 -55
- meerk40t/gui/propertypanels/warpproperty.py +121 -0
- meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
- meerk40t/gui/ribbon.py +2471 -2210
- meerk40t/gui/scene/scene.py +1100 -1051
- meerk40t/gui/scene/sceneconst.py +22 -22
- meerk40t/gui/scene/scenepanel.py +439 -349
- meerk40t/gui/scene/scenespacewidget.py +365 -365
- meerk40t/gui/scene/widget.py +518 -505
- meerk40t/gui/scenewidgets/affinemover.py +215 -215
- meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
- meerk40t/gui/scenewidgets/bedwidget.py +120 -97
- meerk40t/gui/scenewidgets/elementswidget.py +137 -107
- meerk40t/gui/scenewidgets/gridwidget.py +785 -745
- meerk40t/gui/scenewidgets/guidewidget.py +765 -765
- meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
- meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
- meerk40t/gui/scenewidgets/nodeselector.py +28 -28
- meerk40t/gui/scenewidgets/rectselectwidget.py +592 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2958 -2756
- meerk40t/gui/simpleui.py +362 -333
- meerk40t/gui/simulation.py +2451 -2094
- meerk40t/gui/snapoptions.py +208 -203
- meerk40t/gui/spoolerpanel.py +1227 -1180
- meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
- meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
- meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
- meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
- meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
- meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
- meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
- meerk40t/gui/themes.py +200 -78
- meerk40t/gui/tips.py +590 -0
- meerk40t/gui/toolwidgets/circlebrush.py +35 -35
- meerk40t/gui/toolwidgets/toolcircle.py +248 -242
- meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
- meerk40t/gui/toolwidgets/tooldraw.py +97 -90
- meerk40t/gui/toolwidgets/toolellipse.py +219 -212
- meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
- meerk40t/gui/toolwidgets/toolline.py +39 -144
- meerk40t/gui/toolwidgets/toollinetext.py +79 -236
- meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
- meerk40t/gui/toolwidgets/toolmeasure.py +163 -216
- meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
- meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
- meerk40t/gui/toolwidgets/toolparameter.py +754 -668
- meerk40t/gui/toolwidgets/toolplacement.py +108 -108
- meerk40t/gui/toolwidgets/toolpoint.py +68 -59
- meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
- meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
- meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
- meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
- meerk40t/gui/toolwidgets/toolrect.py +211 -207
- meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
- meerk40t/gui/toolwidgets/toolribbon.py +598 -113
- meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
- meerk40t/gui/toolwidgets/tooltext.py +98 -89
- meerk40t/gui/toolwidgets/toolvector.py +213 -204
- meerk40t/gui/toolwidgets/toolwidget.py +39 -39
- meerk40t/gui/usbconnect.py +98 -91
- meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
- meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
- meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
- meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
- meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
- meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
- meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
- meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
- meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
- meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
- meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
- meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
- meerk40t/gui/wordlisteditor.py +985 -931
- meerk40t/gui/wxmeerk40t.py +1447 -1169
- meerk40t/gui/wxmmain.py +5644 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1631 -1453
- meerk40t/gui/wxmtree.py +2416 -2089
- meerk40t/gui/wxutils.py +1769 -1099
- meerk40t/gui/zmatrix.py +102 -102
- meerk40t/image/__init__.py +1 -1
- meerk40t/image/dither.py +429 -0
- meerk40t/image/imagetools.py +2793 -2269
- meerk40t/internal_plugins.py +150 -130
- meerk40t/kernel/__init__.py +63 -12
- meerk40t/kernel/channel.py +259 -212
- meerk40t/kernel/context.py +538 -538
- meerk40t/kernel/exceptions.py +41 -41
- meerk40t/kernel/functions.py +463 -414
- meerk40t/kernel/jobs.py +100 -100
- meerk40t/kernel/kernel.py +3828 -3571
- meerk40t/kernel/lifecycles.py +71 -71
- meerk40t/kernel/module.py +49 -49
- meerk40t/kernel/service.py +147 -147
- meerk40t/kernel/settings.py +383 -343
- meerk40t/lihuiyu/controller.py +883 -876
- meerk40t/lihuiyu/device.py +1181 -1069
- meerk40t/lihuiyu/driver.py +1466 -1372
- meerk40t/lihuiyu/gui/gui.py +127 -106
- meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
- meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
- meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
- meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
- meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
- meerk40t/lihuiyu/interpreter.py +53 -53
- meerk40t/lihuiyu/laserspeed.py +450 -450
- meerk40t/lihuiyu/loader.py +90 -90
- meerk40t/lihuiyu/parser.py +404 -404
- meerk40t/lihuiyu/plugin.py +101 -102
- meerk40t/lihuiyu/tcp_connection.py +111 -109
- meerk40t/main.py +231 -165
- meerk40t/moshi/builder.py +788 -781
- meerk40t/moshi/controller.py +505 -499
- meerk40t/moshi/device.py +495 -442
- meerk40t/moshi/driver.py +862 -696
- meerk40t/moshi/gui/gui.py +78 -76
- meerk40t/moshi/gui/moshicontrollergui.py +538 -522
- meerk40t/moshi/gui/moshidrivergui.py +87 -75
- meerk40t/moshi/plugin.py +43 -43
- meerk40t/network/console_server.py +140 -57
- meerk40t/network/kernelserver.py +10 -9
- meerk40t/network/tcp_server.py +142 -140
- meerk40t/network/udp_server.py +103 -77
- meerk40t/network/web_server.py +404 -0
- meerk40t/newly/controller.py +1158 -1144
- meerk40t/newly/device.py +874 -732
- meerk40t/newly/driver.py +540 -412
- meerk40t/newly/gui/gui.py +219 -188
- meerk40t/newly/gui/newlyconfig.py +116 -101
- meerk40t/newly/gui/newlycontroller.py +193 -186
- meerk40t/newly/gui/operationproperties.py +51 -51
- meerk40t/newly/mock_connection.py +82 -82
- meerk40t/newly/newly_params.py +56 -56
- meerk40t/newly/plugin.py +1214 -1246
- meerk40t/newly/usb_connection.py +322 -322
- meerk40t/rotary/gui/gui.py +52 -46
- meerk40t/rotary/gui/rotarysettings.py +240 -232
- meerk40t/rotary/rotary.py +202 -98
- meerk40t/ruida/control.py +291 -91
- meerk40t/ruida/controller.py +138 -1088
- meerk40t/ruida/device.py +676 -231
- meerk40t/ruida/driver.py +534 -472
- meerk40t/ruida/emulator.py +1494 -1491
- meerk40t/ruida/exceptions.py +4 -4
- meerk40t/ruida/gui/gui.py +71 -76
- meerk40t/ruida/gui/ruidaconfig.py +239 -72
- meerk40t/ruida/gui/ruidacontroller.py +187 -184
- meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
- meerk40t/ruida/loader.py +54 -52
- meerk40t/ruida/mock_connection.py +57 -109
- meerk40t/ruida/plugin.py +124 -87
- meerk40t/ruida/rdjob.py +2084 -945
- meerk40t/ruida/serial_connection.py +116 -0
- meerk40t/ruida/tcp_connection.py +146 -0
- meerk40t/ruida/udp_connection.py +73 -0
- meerk40t/svgelements.py +9671 -9669
- meerk40t/tools/driver_to_path.py +584 -579
- meerk40t/tools/geomstr.py +5583 -4680
- meerk40t/tools/jhfparser.py +357 -292
- meerk40t/tools/kerftest.py +904 -890
- meerk40t/tools/livinghinges.py +1168 -1033
- meerk40t/tools/pathtools.py +987 -949
- meerk40t/tools/pmatrix.py +234 -0
- meerk40t/tools/pointfinder.py +942 -942
- meerk40t/tools/polybool.py +941 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +1047 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +150 -139
- meerk40t-0.9.7020.dist-info/RECORD +446 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +1 -1
- meerk40t/balormk/elementlightjob.py +0 -159
- meerk40t-0.9.3001.dist-info/RECORD +0 -437
- test/bootstrap.py +0 -63
- test/test_cli.py +0 -12
- test/test_core_cutcode.py +0 -418
- test/test_core_elements.py +0 -144
- test/test_core_plotplanner.py +0 -397
- test/test_core_viewports.py +0 -312
- test/test_drivers_grbl.py +0 -108
- test/test_drivers_lihuiyu.py +0 -443
- test/test_drivers_newly.py +0 -113
- test/test_element_degenerate_points.py +0 -43
- test/test_elements_classify.py +0 -97
- test/test_elements_penbox.py +0 -22
- test/test_file_svg.py +0 -176
- test/test_fill.py +0 -155
- test/test_geomstr.py +0 -1523
- test/test_geomstr_nodes.py +0 -18
- test/test_imagetools_actualize.py +0 -306
- test/test_imagetools_wizard.py +0 -258
- test/test_kernel.py +0 -200
- test/test_laser_speeds.py +0 -3303
- test/test_length.py +0 -57
- test/test_lifecycle.py +0 -66
- test/test_operations.py +0 -251
- test/test_operations_hatch.py +0 -57
- test/test_ruida.py +0 -19
- test/test_spooler.py +0 -22
- test/test_tools_rasterplotter.py +0 -29
- test/test_wobble.py +0 -133
- test/test_zingl.py +0 -124
- {test → meerk40t/cylinder}/__init__.py +0 -0
- /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
meerk40t/gui/wxmtree.py
CHANGED
@@ -1,2089 +1,2416 @@
|
|
1
|
-
import wx
|
2
|
-
from wx import aui
|
3
|
-
|
4
|
-
from meerk40t.core.elements.element_types import op_nodes
|
5
|
-
|
6
|
-
from ..core.units import Length
|
7
|
-
from ..kernel import signal_listener
|
8
|
-
from ..svgelements import Color
|
9
|
-
from .basicops import BasicOpPanel
|
10
|
-
from .icons import (
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
icon_effect_hatch,
|
18
|
-
icon_effect_wobble,
|
19
|
-
icon_external,
|
20
|
-
icon_internal,
|
21
|
-
icon_line,
|
22
|
-
icon_meerk40t,
|
23
|
-
icon_mk_ellipse,
|
24
|
-
icon_mk_polyline,
|
25
|
-
icon_mk_rectangular,
|
26
|
-
icon_path,
|
27
|
-
icon_points,
|
28
|
-
icon_regmarks,
|
29
|
-
icon_return,
|
30
|
-
icon_round_stop,
|
31
|
-
icon_timer,
|
32
|
-
icon_tree,
|
33
|
-
icon_warning,
|
34
|
-
icons8_direction,
|
35
|
-
icons8_file,
|
36
|
-
icons8_ghost,
|
37
|
-
icons8_group_objects,
|
38
|
-
icons8_home_filled,
|
39
|
-
icons8_image,
|
40
|
-
icons8_laser_beam,
|
41
|
-
icons8_laserbeam_weak,
|
42
|
-
icons8_lock,
|
43
|
-
icons8_r_white,
|
44
|
-
)
|
45
|
-
from .laserrender import DRAW_MODE_ICONS, LaserRender, swizzlecolor
|
46
|
-
from .mwindow import MWindow
|
47
|
-
from .wxutils import (
|
48
|
-
StaticBoxSizer,
|
49
|
-
create_menu,
|
50
|
-
dip_size,
|
51
|
-
get_key_name,
|
52
|
-
is_navigation_key,
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
.
|
88
|
-
.
|
89
|
-
.
|
90
|
-
.
|
91
|
-
.
|
92
|
-
.
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
self.
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
def
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
self.warn_panel
|
208
|
-
self.
|
209
|
-
self.
|
210
|
-
self.
|
211
|
-
self.
|
212
|
-
self.
|
213
|
-
self.
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
self.
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
self.
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
self.
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
)
|
245
|
-
self.
|
246
|
-
|
247
|
-
|
248
|
-
self.
|
249
|
-
|
250
|
-
|
251
|
-
self.
|
252
|
-
|
253
|
-
self.
|
254
|
-
self.
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
self.
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
""
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
@signal_listener("
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
""
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
#
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
"""
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
@signal_listener("
|
436
|
-
def
|
437
|
-
"""
|
438
|
-
Called by 'rebuild_tree' signal.
|
439
|
-
|
440
|
-
@param origin: the path of the originating signal
|
441
|
-
@param
|
442
|
-
@param args:
|
443
|
-
@return:
|
444
|
-
"""
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
self.shadow_tree.
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
self.
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
self.
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
)
|
596
|
-
|
597
|
-
self.
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
self.
|
678
|
-
|
679
|
-
self.
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
"""
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
self.
|
712
|
-
self.
|
713
|
-
|
714
|
-
def
|
715
|
-
"""
|
716
|
-
Notified that this node
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
if
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
self.
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
self.
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
self.
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
self.
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
"""
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
if
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
self.
|
1108
|
-
self.
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
self.
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
)
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
)
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
)
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
)
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
self.wxtree.
|
1169
|
-
|
1170
|
-
self.
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
self.
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
)
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
""
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
#
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
if
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
if
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
if
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
|
1687
|
-
|
1688
|
-
|
1689
|
-
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
if
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
if
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
-
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
#
|
1777
|
-
#
|
1778
|
-
#
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
1802
|
-
|
1803
|
-
|
1804
|
-
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1811
|
-
|
1812
|
-
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
return
|
1818
|
-
|
1819
|
-
|
1820
|
-
if
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
|
1836
|
-
|
1837
|
-
|
1838
|
-
|
1839
|
-
|
1840
|
-
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
|
1863
|
-
|
1864
|
-
|
1865
|
-
|
1866
|
-
|
1867
|
-
|
1868
|
-
if node
|
1869
|
-
|
1870
|
-
|
1871
|
-
|
1872
|
-
|
1873
|
-
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
)
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
)
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
)
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
|
1917
|
-
|
1918
|
-
|
1919
|
-
|
1920
|
-
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1925
|
-
|
1926
|
-
|
1927
|
-
|
1928
|
-
|
1929
|
-
|
1930
|
-
|
1931
|
-
|
1932
|
-
|
1933
|
-
|
1934
|
-
|
1935
|
-
|
1936
|
-
|
1937
|
-
|
1938
|
-
|
1939
|
-
|
1940
|
-
|
1941
|
-
|
1942
|
-
|
1943
|
-
|
1944
|
-
|
1945
|
-
|
1946
|
-
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
|
1951
|
-
|
1952
|
-
|
1953
|
-
|
1954
|
-
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1958
|
-
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
|
1965
|
-
|
1966
|
-
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
|
1977
|
-
|
1978
|
-
|
1979
|
-
|
1980
|
-
|
1981
|
-
|
1982
|
-
|
1983
|
-
|
1984
|
-
|
1985
|
-
|
1986
|
-
|
1987
|
-
|
1988
|
-
|
1989
|
-
|
1990
|
-
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
|
1997
|
-
|
1998
|
-
|
1999
|
-
|
2000
|
-
|
2001
|
-
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
|
2009
|
-
|
2010
|
-
|
2011
|
-
|
2012
|
-
|
2013
|
-
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
2017
|
-
|
2018
|
-
|
2019
|
-
|
2020
|
-
|
2021
|
-
|
2022
|
-
|
2023
|
-
"""
|
2024
|
-
|
2025
|
-
|
2026
|
-
|
2027
|
-
|
2028
|
-
|
2029
|
-
if
|
2030
|
-
|
2031
|
-
|
2032
|
-
|
2033
|
-
|
2034
|
-
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
|
2042
|
-
|
2043
|
-
|
2044
|
-
|
2045
|
-
|
2046
|
-
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
|
2054
|
-
|
2055
|
-
|
2056
|
-
|
2057
|
-
|
2058
|
-
|
2059
|
-
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
self.
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
1
|
+
import wx
|
2
|
+
from wx import aui
|
3
|
+
|
4
|
+
from meerk40t.core.elements.element_types import op_nodes, elem_nodes
|
5
|
+
|
6
|
+
from ..core.units import Length
|
7
|
+
from ..kernel import signal_listener
|
8
|
+
from ..svgelements import Color
|
9
|
+
from .basicops import BasicOpPanel
|
10
|
+
from .icons import (
|
11
|
+
icon_bell,
|
12
|
+
icon_bmap_text,
|
13
|
+
icon_canvas,
|
14
|
+
icon_close_window,
|
15
|
+
icon_console,
|
16
|
+
icon_distort,
|
17
|
+
icon_effect_hatch,
|
18
|
+
icon_effect_wobble,
|
19
|
+
icon_external,
|
20
|
+
icon_internal,
|
21
|
+
icon_line,
|
22
|
+
icon_meerk40t,
|
23
|
+
icon_mk_ellipse,
|
24
|
+
icon_mk_polyline,
|
25
|
+
icon_mk_rectangular,
|
26
|
+
icon_path,
|
27
|
+
icon_points,
|
28
|
+
icon_regmarks,
|
29
|
+
icon_return,
|
30
|
+
icon_round_stop,
|
31
|
+
icon_timer,
|
32
|
+
icon_tree,
|
33
|
+
icon_warning,
|
34
|
+
icons8_direction,
|
35
|
+
icons8_file,
|
36
|
+
icons8_ghost,
|
37
|
+
icons8_group_objects,
|
38
|
+
icons8_home_filled,
|
39
|
+
icons8_image,
|
40
|
+
icons8_laser_beam,
|
41
|
+
icons8_laserbeam_weak,
|
42
|
+
icons8_lock,
|
43
|
+
icons8_r_white,
|
44
|
+
)
|
45
|
+
from .laserrender import DRAW_MODE_ICONS, LaserRender, swizzlecolor
|
46
|
+
from .mwindow import MWindow
|
47
|
+
from .wxutils import (
|
48
|
+
StaticBoxSizer,
|
49
|
+
create_menu,
|
50
|
+
dip_size,
|
51
|
+
get_key_name,
|
52
|
+
is_navigation_key,
|
53
|
+
wxButton,
|
54
|
+
wxTreeCtrl,
|
55
|
+
)
|
56
|
+
|
57
|
+
_ = wx.GetTranslation
|
58
|
+
|
59
|
+
|
60
|
+
def register_panel_tree(window, context):
|
61
|
+
lastpage = context.root.setting(int, "tree_panel_page", 1)
|
62
|
+
if lastpage is None or lastpage < 0 or lastpage > 2:
|
63
|
+
lastpage = 0
|
64
|
+
|
65
|
+
basic_op = BasicOpPanel(window, wx.ID_ANY, context=context)
|
66
|
+
wxtree = TreePanel(window, wx.ID_ANY, context=context)
|
67
|
+
|
68
|
+
def on_panel_change(context):
|
69
|
+
def handler(event):
|
70
|
+
mycontext.root.setting(int, "tree_panel_page", 1)
|
71
|
+
pagenum = notetab.GetSelection()
|
72
|
+
setattr(mycontext.root, "tree_panel_page", pagenum)
|
73
|
+
if pagenum == 0:
|
74
|
+
basic_op.pane_show()
|
75
|
+
wxtree.pane_hide()
|
76
|
+
else:
|
77
|
+
basic_op.pane_hide()
|
78
|
+
wxtree.pane_show()
|
79
|
+
|
80
|
+
mycontext = context
|
81
|
+
return handler
|
82
|
+
|
83
|
+
# ARGGH, the color setting via the ArtProvider does only work
|
84
|
+
# if you set the tabs to the bottom! wx.aui.AUI_NB_BOTTOM
|
85
|
+
notetab = wx.aui.AuiNotebook(
|
86
|
+
window,
|
87
|
+
wx.ID_ANY,
|
88
|
+
style=wx.aui.AUI_NB_TAB_EXTERNAL_MOVE
|
89
|
+
| wx.aui.AUI_NB_SCROLL_BUTTONS
|
90
|
+
| wx.aui.AUI_NB_TAB_SPLIT
|
91
|
+
| wx.aui.AUI_NB_TAB_MOVE
|
92
|
+
| wx.aui.AUI_NB_BOTTOM,
|
93
|
+
)
|
94
|
+
context.themes.set_window_colors(notetab)
|
95
|
+
bg_std = context.themes.get("win_bg")
|
96
|
+
bg_active = context.themes.get("highlight")
|
97
|
+
notetab.GetArtProvider().SetColour(bg_std)
|
98
|
+
notetab.GetArtProvider().SetActiveColour(bg_active)
|
99
|
+
|
100
|
+
pane = (
|
101
|
+
aui.AuiPaneInfo()
|
102
|
+
.Name("tree")
|
103
|
+
.Right()
|
104
|
+
.MinSize(200, 180)
|
105
|
+
.BestSize(300, 270)
|
106
|
+
.FloatingSize(300, 270)
|
107
|
+
.LeftDockable()
|
108
|
+
.RightDockable()
|
109
|
+
.BottomDockable(False)
|
110
|
+
.Caption(_("Tree"))
|
111
|
+
.CaptionVisible(not context.pane_lock)
|
112
|
+
.TopDockable(False)
|
113
|
+
)
|
114
|
+
pane.helptext = _("Tree containing all objects")
|
115
|
+
notetab.AddPage(basic_op, _("Burn-Operation"))
|
116
|
+
notetab.AddPage(wxtree, _("Details"))
|
117
|
+
notetab.SetSelection(lastpage)
|
118
|
+
notetab.Bind(aui.EVT_AUINOTEBOOK_PAGE_CHANGED, on_panel_change(context))
|
119
|
+
pane.dock_proportion = 500
|
120
|
+
pane.control = notetab
|
121
|
+
window.on_pane_create(pane)
|
122
|
+
context.register("pane/tree", pane)
|
123
|
+
|
124
|
+
|
125
|
+
class TreePanel(wx.Panel):
|
126
|
+
def __init__(self, *args, context=None, **kwds):
|
127
|
+
kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
|
128
|
+
wx.Panel.__init__(self, *args, **kwds)
|
129
|
+
self.context = context
|
130
|
+
self.context.themes.set_window_colors(self)
|
131
|
+
# Define Tree
|
132
|
+
self.wxtree = wxTreeCtrl(
|
133
|
+
self,
|
134
|
+
wx.ID_ANY,
|
135
|
+
style=wx.TR_MULTIPLE
|
136
|
+
| wx.TR_HAS_BUTTONS
|
137
|
+
| wx.TR_HIDE_ROOT
|
138
|
+
| wx.TR_LINES_AT_ROOT,
|
139
|
+
)
|
140
|
+
# try:
|
141
|
+
# res = wx.SystemSettings().GetAppearance().IsDark()
|
142
|
+
# except AttributeError:
|
143
|
+
# res = wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOW)[0] < 127
|
144
|
+
self.SetHelpText(
|
145
|
+
"tree"
|
146
|
+
) # That will be used for all controls in this window, unless stated differently
|
147
|
+
|
148
|
+
self.setup_warn_panel()
|
149
|
+
|
150
|
+
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
151
|
+
main_sizer.Add(self.wxtree, 1, wx.EXPAND, 0)
|
152
|
+
main_sizer.Add(self.warn_panel, 0, wx.EXPAND, 0)
|
153
|
+
self.SetSizer(main_sizer)
|
154
|
+
self.__set_tree()
|
155
|
+
self.wxtree.Bind(wx.EVT_KEY_UP, self.on_key_up)
|
156
|
+
self.wxtree.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
|
157
|
+
self._keybind_channel = self.context.channel("keybinds")
|
158
|
+
|
159
|
+
self.context.signal("rebuild_tree", "all")
|
160
|
+
|
161
|
+
def setup_warn_panel(self):
|
162
|
+
def fix_unassigned_create(event):
|
163
|
+
previous = self.context.elements.classify_autogenerate
|
164
|
+
self.context.elements.classify_autogenerate = True
|
165
|
+
target_list = list(self.context.elements.unassigned_elements())
|
166
|
+
self.context.elements.classify(target_list)
|
167
|
+
self.context.elements.classify_autogenerate = previous
|
168
|
+
self.context.elements.signal("refresh_tree")
|
169
|
+
|
170
|
+
def fix_unassigned_used(event):
|
171
|
+
previous = self.context.elements.classify_autogenerate
|
172
|
+
self.context.elements.classify_autogenerate = False
|
173
|
+
target_list = list(self.context.elements.unassigned_elements())
|
174
|
+
self.context.elements.classify(target_list)
|
175
|
+
self.context.elements.classify_autogenerate = previous
|
176
|
+
self.context.elements.signal("refresh_tree")
|
177
|
+
|
178
|
+
def fix_unburnt(event):
|
179
|
+
to_reload = []
|
180
|
+
for node in self.context.elements.elems():
|
181
|
+
will_be_burnt = False
|
182
|
+
first_op = None
|
183
|
+
for refnode in node._references:
|
184
|
+
op = refnode.parent
|
185
|
+
if op is not None:
|
186
|
+
try:
|
187
|
+
if op.output:
|
188
|
+
will_be_burnt = True
|
189
|
+
break
|
190
|
+
else:
|
191
|
+
if first_op is None:
|
192
|
+
first_op = op
|
193
|
+
except AttributeError:
|
194
|
+
pass
|
195
|
+
if not will_be_burnt and first_op is not None:
|
196
|
+
try:
|
197
|
+
first_op.output = True
|
198
|
+
to_reload.append(first_op)
|
199
|
+
except AttributeError:
|
200
|
+
pass
|
201
|
+
if to_reload:
|
202
|
+
self.context.elements.signal(
|
203
|
+
"element_property_reload", to_reload
|
204
|
+
)
|
205
|
+
self.context.elements.signal("warn_state_update")
|
206
|
+
|
207
|
+
self.warn_panel = wx.BoxSizer(wx.HORIZONTAL)
|
208
|
+
unassigned_frame = StaticBoxSizer(self, wx.ID_ANY, "Unassigned", wx.HORIZONTAL)
|
209
|
+
unburnt_frame = StaticBoxSizer(self, wx.ID_ANY, "Non-burnt", wx.HORIZONTAL)
|
210
|
+
self.btn_fix_assign_create = wxButton(self, wx.ID_ANY, "Assign (+new)")
|
211
|
+
self.btn_fix_assign_existing = wxButton(self, wx.ID_ANY, "Assign")
|
212
|
+
self.btn_fix_unburnt = wxButton(self, wx.ID_ANY, "Enable")
|
213
|
+
self.btn_fix_assign_create.SetToolTip(
|
214
|
+
_("Classify unassigned elements and create operations if necessary")
|
215
|
+
)
|
216
|
+
self.btn_fix_assign_existing.SetToolTip(
|
217
|
+
_("Classify unassigned elements and use only existing operations")
|
218
|
+
)
|
219
|
+
self.btn_fix_unburnt.SetToolTip(
|
220
|
+
_("Reactivate disabled operations that prevent elements from being burnt")
|
221
|
+
)
|
222
|
+
|
223
|
+
unassigned_frame.Add(self.btn_fix_assign_create, 0, wx.EXPAND, 0)
|
224
|
+
unassigned_frame.Add(self.btn_fix_assign_existing, 0, wx.EXPAND, 0)
|
225
|
+
unburnt_frame.Add(self.btn_fix_unburnt, 0, wx.EXPAND, 0)
|
226
|
+
self.warn_panel.Add(unassigned_frame, 1, wx.EXPAND, 0)
|
227
|
+
self.warn_panel.Add(unburnt_frame, 1, wx.EXPAND, 0)
|
228
|
+
self._last_issue = None
|
229
|
+
self.warn_panel.Show(False)
|
230
|
+
self.warn_panel.ShowItems(False)
|
231
|
+
self.Bind(wx.EVT_BUTTON, fix_unassigned_create, self.btn_fix_assign_create)
|
232
|
+
self.Bind(wx.EVT_BUTTON, fix_unassigned_used, self.btn_fix_assign_existing)
|
233
|
+
self.Bind(wx.EVT_BUTTON, fix_unburnt, self.btn_fix_unburnt)
|
234
|
+
# self.Show(False)
|
235
|
+
|
236
|
+
def check_for_issues(self):
|
237
|
+
needs_showing = False
|
238
|
+
non_assigned, non_burn = self.context.elements.have_unburnable_elements()
|
239
|
+
warn_level = self.context.setting(int, "concern_level", 1)
|
240
|
+
if non_assigned and warn_level <= 2:
|
241
|
+
needs_showing = True
|
242
|
+
if non_burn and warn_level <= 1:
|
243
|
+
needs_showing = True
|
244
|
+
self.btn_fix_assign_create.Enable(non_assigned)
|
245
|
+
self.btn_fix_assign_existing.Enable(non_assigned)
|
246
|
+
self.btn_fix_unburnt.Enable(non_burn)
|
247
|
+
new_issue = non_assigned or non_burn
|
248
|
+
if (self._last_issue == new_issue) and (needs_showing == self.btn_fix_unburnt.IsShown()):
|
249
|
+
# no changes
|
250
|
+
return
|
251
|
+
self._last_issue = new_issue
|
252
|
+
if new_issue and needs_showing:
|
253
|
+
self.warn_panel.Show(True)
|
254
|
+
self.warn_panel.ShowItems(True)
|
255
|
+
else:
|
256
|
+
self.warn_panel.Show(False)
|
257
|
+
self.warn_panel.ShowItems(False)
|
258
|
+
self.Layout()
|
259
|
+
|
260
|
+
def __set_tree(self):
|
261
|
+
self.shadow_tree = ShadowTree(
|
262
|
+
self.context.elements, self.GetParent(), self.wxtree, self.context
|
263
|
+
)
|
264
|
+
|
265
|
+
# self.Bind(
|
266
|
+
# wx.EVT_TREE_BEGIN_DRAG, self.shadow_tree.on_drag_begin_handler, self.wxtree
|
267
|
+
# )
|
268
|
+
self.shadow_tree.wxtree.Bind(
|
269
|
+
wx.EVT_TREE_BEGIN_DRAG, self.shadow_tree.on_drag_begin_handler
|
270
|
+
)
|
271
|
+
self.Bind(
|
272
|
+
wx.EVT_TREE_END_DRAG, self.shadow_tree.on_drag_end_handler, self.wxtree
|
273
|
+
)
|
274
|
+
self.Bind(
|
275
|
+
wx.EVT_TREE_ITEM_ACTIVATED, self.shadow_tree.on_item_activated, self.wxtree
|
276
|
+
)
|
277
|
+
self.Bind(
|
278
|
+
wx.EVT_TREE_SEL_CHANGED,
|
279
|
+
self.shadow_tree.on_item_selection_changed,
|
280
|
+
self.wxtree,
|
281
|
+
)
|
282
|
+
self.Bind(
|
283
|
+
wx.EVT_TREE_ITEM_RIGHT_CLICK,
|
284
|
+
self.shadow_tree.on_item_right_click,
|
285
|
+
self.wxtree,
|
286
|
+
)
|
287
|
+
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.shadow_tree.on_collapse, self.wxtree)
|
288
|
+
self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.shadow_tree.on_expand, self.wxtree)
|
289
|
+
self.Bind(wx.EVT_TREE_STATE_IMAGE_CLICK, self.shadow_tree.on_state_icon, self.wxtree)
|
290
|
+
|
291
|
+
self.wxtree.Bind(wx.EVT_MOTION, self.shadow_tree.on_mouse_over)
|
292
|
+
self.wxtree.Bind(wx.EVT_LEAVE_WINDOW, self.on_lost_focus, self.wxtree)
|
293
|
+
|
294
|
+
def on_lost_focus(self, event):
|
295
|
+
self.wxtree.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
|
296
|
+
|
297
|
+
def on_key_down(self, event):
|
298
|
+
"""
|
299
|
+
Keydown for the tree does not execute navigation keys. These are only executed by the scene since they do
|
300
|
+
useful work for the tree.
|
301
|
+
|
302
|
+
Make sure the treectl can work on standard keys...
|
303
|
+
|
304
|
+
@param event:
|
305
|
+
@return:
|
306
|
+
"""
|
307
|
+
event.Skip()
|
308
|
+
keyvalue = get_key_name(event)
|
309
|
+
if is_navigation_key(keyvalue):
|
310
|
+
if self._keybind_channel:
|
311
|
+
self._keybind_channel(
|
312
|
+
f"Tree key_down: {keyvalue} is a navigation key. Not processed."
|
313
|
+
)
|
314
|
+
return
|
315
|
+
if self.context.bind.trigger(keyvalue):
|
316
|
+
if self._keybind_channel:
|
317
|
+
self._keybind_channel(f"Tree key_down: {keyvalue} is executed.")
|
318
|
+
else:
|
319
|
+
if self._keybind_channel:
|
320
|
+
self._keybind_channel(f"Tree key_down: {keyvalue} was unbound.")
|
321
|
+
|
322
|
+
def on_key_up(self, event):
|
323
|
+
"""
|
324
|
+
Keyup for the tree does not execute navigation keys. These are only executed by the scene.
|
325
|
+
|
326
|
+
Make sure the treectl can work on standard keys...
|
327
|
+
|
328
|
+
@param event:
|
329
|
+
@return:
|
330
|
+
"""
|
331
|
+
keyvalue = get_key_name(event)
|
332
|
+
# There is a menu entry in wxmain that should catch all 'delete' keys
|
333
|
+
# but that is not consistently so, every other key seems to slip through
|
334
|
+
# probably there is an issue there, but we use the opportunity not
|
335
|
+
# only to catch these but to establish a forced delete via ctrl-delete as well
|
336
|
+
|
337
|
+
if keyvalue == "delete":
|
338
|
+
self.context("tree selected delete\n")
|
339
|
+
return
|
340
|
+
if keyvalue == "ctrl+delete":
|
341
|
+
self.context("tree selected remove\n")
|
342
|
+
return
|
343
|
+
event.Skip()
|
344
|
+
if is_navigation_key(keyvalue):
|
345
|
+
if self._keybind_channel:
|
346
|
+
self._keybind_channel(
|
347
|
+
f"Tree key_up: {keyvalue} is a navigation key. Not processed."
|
348
|
+
)
|
349
|
+
return
|
350
|
+
if self.context.bind.untrigger(keyvalue):
|
351
|
+
if self._keybind_channel:
|
352
|
+
self._keybind_channel(f"Tree key_up: {keyvalue} is executed.")
|
353
|
+
else:
|
354
|
+
if self._keybind_channel:
|
355
|
+
self._keybind_channel(f"Tree key_up: {keyvalue} was unbound.")
|
356
|
+
|
357
|
+
def pane_show(self):
|
358
|
+
pass
|
359
|
+
|
360
|
+
def pane_hide(self):
|
361
|
+
pass
|
362
|
+
|
363
|
+
@signal_listener("updateop_tree")
|
364
|
+
@signal_listener("warn_state_update")
|
365
|
+
def on_warn_state_update(self, origin, *args):
|
366
|
+
# Updates the warning state, using signal to avoid unnecessary calls
|
367
|
+
self.shadow_tree.update_warn_sign()
|
368
|
+
self.check_for_issues()
|
369
|
+
|
370
|
+
@signal_listener("update_group_labels")
|
371
|
+
def on_group_update(self, origin, *args):
|
372
|
+
self.shadow_tree.update_group_labels("signal")
|
373
|
+
|
374
|
+
@signal_listener("select_emphasized_tree")
|
375
|
+
def on_shadow_select_emphasized_tree(self, origin, *args):
|
376
|
+
self.shadow_tree.select_in_tree_by_emphasis(origin, *args)
|
377
|
+
|
378
|
+
@signal_listener("activate_selected_nodes")
|
379
|
+
def on_shadow_select_activate_tree(self, origin, *args):
|
380
|
+
self.shadow_tree.activate_selected_node(origin, *args)
|
381
|
+
|
382
|
+
@signal_listener("activate_single_node")
|
383
|
+
def on_shadow_select_activate_single_tree(self, origin, node=None, *args):
|
384
|
+
if node is not None:
|
385
|
+
node.selected = True
|
386
|
+
# self.shadow_tree.activate_selected_node(origin, *args)
|
387
|
+
|
388
|
+
@signal_listener("element_property_update")
|
389
|
+
def on_element_update(self, origin, *args):
|
390
|
+
"""
|
391
|
+
Called by 'element_property_update' when the properties of an element are changed.
|
392
|
+
|
393
|
+
@param origin: the path of the originating signal
|
394
|
+
@param args:
|
395
|
+
@return:
|
396
|
+
"""
|
397
|
+
if self.shadow_tree is not None:
|
398
|
+
stop_updates = not self.shadow_tree._freeze
|
399
|
+
if stop_updates:
|
400
|
+
self.shadow_tree.freeze_tree(True)
|
401
|
+
self.shadow_tree.on_element_update(*args)
|
402
|
+
if stop_updates:
|
403
|
+
self.shadow_tree.freeze_tree(False)
|
404
|
+
|
405
|
+
@signal_listener("element_property_reload")
|
406
|
+
def on_force_element_update(self, origin, *args):
|
407
|
+
"""
|
408
|
+
Called by 'element_property_reload' when the properties of an element are changed.
|
409
|
+
|
410
|
+
@param origin: the path of the originating signal
|
411
|
+
@param args:
|
412
|
+
@return:
|
413
|
+
"""
|
414
|
+
if self.shadow_tree is not None:
|
415
|
+
stop_updates = not self.shadow_tree._freeze
|
416
|
+
if stop_updates:
|
417
|
+
self.shadow_tree.freeze_tree(True)
|
418
|
+
self.shadow_tree.on_force_element_update(*args)
|
419
|
+
if stop_updates:
|
420
|
+
self.shadow_tree.freeze_tree(False)
|
421
|
+
|
422
|
+
@signal_listener("activate;device")
|
423
|
+
def on_activate_device(self, origin, target=None, *args):
|
424
|
+
self.shadow_tree.reset_formatter_cache()
|
425
|
+
self.shadow_tree.refresh_tree(source="device")
|
426
|
+
|
427
|
+
@signal_listener("reset_formatter")
|
428
|
+
def on_reset_formatter(self, origin, target=None, *args):
|
429
|
+
self.shadow_tree.reset_formatter_cache()
|
430
|
+
|
431
|
+
@signal_listener("sync_expansion")
|
432
|
+
def on_sync_expansion(self, origin, target=None, *args):
|
433
|
+
self.shadow_tree.sync_expansion()
|
434
|
+
|
435
|
+
@signal_listener("rebuild_tree")
|
436
|
+
def on_rebuild_tree_signal(self, origin, target=None, *args):
|
437
|
+
"""
|
438
|
+
Called by 'rebuild_tree' signal. To rebuild the tree directly
|
439
|
+
|
440
|
+
@param origin: the path of the originating signal
|
441
|
+
@param target: target device
|
442
|
+
@param args:
|
443
|
+
@return:
|
444
|
+
"""
|
445
|
+
# if target is not None:
|
446
|
+
# if target == "elements":
|
447
|
+
# startnode = self.shadow_tree.elements.get(type="branch elems").item
|
448
|
+
# elif target == "operations":
|
449
|
+
# startnode = self.shadow_tree.elements.get(type="branch ops").item
|
450
|
+
# elif target == "regmarks":
|
451
|
+
# startnode = self.shadow_tree.elements.get(type="branch reg").item
|
452
|
+
# print ("Current content of branch %s" % target)
|
453
|
+
# idx = 0
|
454
|
+
# child, cookie = self.shadow_tree.wxtree.GetFirstChild(startnode)
|
455
|
+
# while child.IsOk():
|
456
|
+
# # child_node = self.wxtree.GetItemData(child)
|
457
|
+
# lbl = self.shadow_tree.wxtree.GetItemText(child)
|
458
|
+
# print ("Node #%d - content: %s" % (idx, lbl))
|
459
|
+
# child, cookie = self.shadow_tree.wxtree.GetNextChild(startnode, cookie)
|
460
|
+
# idx += 1
|
461
|
+
# self.shadow_tree.wxtree.Expand(startnode)
|
462
|
+
# else:
|
463
|
+
# self.shadow_tree.rebuild_tree()
|
464
|
+
if target is None:
|
465
|
+
target = "all"
|
466
|
+
self.shadow_tree.rebuild_tree(source="signal", target=target)
|
467
|
+
|
468
|
+
@signal_listener("refresh_tree")
|
469
|
+
def on_refresh_tree_signal(self, origin, nodes=None, *args):
|
470
|
+
"""
|
471
|
+
Called by 'refresh_tree' signal. To refresh tree directly
|
472
|
+
|
473
|
+
@param origin: the path of the originating signal
|
474
|
+
@param nodes: which nodes were added.
|
475
|
+
@param args:
|
476
|
+
@return:
|
477
|
+
"""
|
478
|
+
self.shadow_tree.cache_hits = 0
|
479
|
+
self.shadow_tree.cache_requests = 0
|
480
|
+
self.shadow_tree.refresh_tree(source=f"signal_{origin}")
|
481
|
+
if nodes is not None:
|
482
|
+
if isinstance(nodes, (tuple, list)):
|
483
|
+
# All Standard nodes first
|
484
|
+
for node in nodes:
|
485
|
+
if node is not None and node._item is not None and node.type.startswith("elem "):
|
486
|
+
self.shadow_tree.set_icon(node, force=True)
|
487
|
+
# Then all others
|
488
|
+
for node in nodes:
|
489
|
+
if node is not None and node._item is not None and not node.type.startswith("elem "):
|
490
|
+
self.shadow_tree.set_icon(node, force=True)
|
491
|
+
# Show the first node, but if that's the root node then ignore stuff
|
492
|
+
node = nodes[0] if len(nodes) > 0 else None
|
493
|
+
else:
|
494
|
+
node = nodes
|
495
|
+
self.shadow_tree.set_icon(node, force=True)
|
496
|
+
rootitem = self.shadow_tree.wxtree.GetRootItem()
|
497
|
+
if (
|
498
|
+
node is not None
|
499
|
+
and node._item is not None
|
500
|
+
and node._item != rootitem
|
501
|
+
):
|
502
|
+
self.shadow_tree.wxtree.EnsureVisible(node._item)
|
503
|
+
|
504
|
+
@signal_listener("freeze_tree")
|
505
|
+
def on_freeze_tree_signal(self, origin, status=None, *args):
|
506
|
+
"""
|
507
|
+
Called by 'rebuild_tree' signal. Halts any updates like set_decorations and others
|
508
|
+
|
509
|
+
@param origin: the path of the originating signal
|
510
|
+
@param status: true, false (evident what they do), None: to toggle
|
511
|
+
@param args:
|
512
|
+
@return:
|
513
|
+
"""
|
514
|
+
self.shadow_tree.freeze_tree(status)
|
515
|
+
|
516
|
+
@signal_listener("updateop_tree")
|
517
|
+
def on_update_op_labels_tree(self, origin, *args):
|
518
|
+
stop_updates = not self.shadow_tree._freeze
|
519
|
+
if stop_updates:
|
520
|
+
self.shadow_tree.freeze_tree(True)
|
521
|
+
self.shadow_tree.update_op_labels()
|
522
|
+
opitem = self.context.elements.get(type="branch ops")._item
|
523
|
+
if opitem is None:
|
524
|
+
return
|
525
|
+
tree = self.shadow_tree.wxtree
|
526
|
+
tree.Expand(opitem)
|
527
|
+
if stop_updates:
|
528
|
+
self.shadow_tree.freeze_tree(False)
|
529
|
+
|
530
|
+
@signal_listener("updateelem_tree")
|
531
|
+
def on_update_elem_tree(self, origin, *args):
|
532
|
+
elitem = self.context.elements.get(type="branch elems")._item
|
533
|
+
if elitem is None:
|
534
|
+
return
|
535
|
+
tree = self.shadow_tree.wxtree
|
536
|
+
tree.Expand(elitem)
|
537
|
+
|
538
|
+
|
539
|
+
class ElementsTree(MWindow):
|
540
|
+
def __init__(self, *args, **kwds):
|
541
|
+
super().__init__(423, 131, *args, **kwds)
|
542
|
+
|
543
|
+
self.panel = TreePanel(self, wx.ID_ANY, context=self.context)
|
544
|
+
self.sizer.Add(self.panel, 1, wx.EXPAND, 0)
|
545
|
+
self.add_module_delegate(self.panel)
|
546
|
+
_icon = wx.NullIcon
|
547
|
+
_icon.CopyFromBitmap(icon_tree.GetBitmap())
|
548
|
+
self.SetIcon(_icon)
|
549
|
+
self.SetTitle(_("Tree"))
|
550
|
+
self.restore_aspect()
|
551
|
+
|
552
|
+
def window_open(self):
|
553
|
+
try:
|
554
|
+
self.panel.pane_show()
|
555
|
+
except AttributeError:
|
556
|
+
pass
|
557
|
+
|
558
|
+
def window_close(self):
|
559
|
+
try:
|
560
|
+
self.panel.pane_hide()
|
561
|
+
except AttributeError:
|
562
|
+
pass
|
563
|
+
|
564
|
+
|
565
|
+
class ShadowTree:
|
566
|
+
"""
|
567
|
+
The shadowTree creates a 'wx.Tree' structure from the 'elements.tree' structure. It listens to updates to the
|
568
|
+
elements tree and updates the GUI version accordingly. This tree does not permit alterations to it, rather it sends
|
569
|
+
any requested alterations to the 'elements.tree' or the 'elements.elements' or 'elements.operations' and when those
|
570
|
+
are reflected in the tree, the shadow tree is updated accordingly.
|
571
|
+
"""
|
572
|
+
|
573
|
+
def __init__(self, service, gui, wxtree, context):
|
574
|
+
self.elements = service
|
575
|
+
self.context = context
|
576
|
+
self.gui = gui
|
577
|
+
self.wxtree = wxtree
|
578
|
+
self.renderer = LaserRender(service.root)
|
579
|
+
self.dragging_nodes = None
|
580
|
+
self.tree_images = None
|
581
|
+
self.name = "Project"
|
582
|
+
self._freeze = False
|
583
|
+
testsize = dip_size(self.wxtree, 20, 20)
|
584
|
+
self.iconsize = testsize[1]
|
585
|
+
self.iconstates = {}
|
586
|
+
self.last_call = 0
|
587
|
+
self._nodes_to_expand = []
|
588
|
+
|
589
|
+
# fact = get_default_scale_factor()
|
590
|
+
# if fact > 1.0:
|
591
|
+
# self.iconsize = int(self.iconsize * fact)
|
592
|
+
|
593
|
+
self.do_not_select = False
|
594
|
+
self.was_already_expanded = []
|
595
|
+
service.add_service_delegate(self)
|
596
|
+
self.setup_state_images()
|
597
|
+
self.default_images = {
|
598
|
+
"console home -f": icons8_home_filled,
|
599
|
+
"console move_abs": icon_return,
|
600
|
+
"console beep": icon_bell,
|
601
|
+
"console interrupt": icon_round_stop,
|
602
|
+
"console quit": icon_close_window,
|
603
|
+
"util wait": icon_timer,
|
604
|
+
"util home": icons8_home_filled,
|
605
|
+
"util goto": icon_return,
|
606
|
+
"util output": icon_external,
|
607
|
+
"util input": icon_internal,
|
608
|
+
"util console": icon_console,
|
609
|
+
"op engrave": icons8_laserbeam_weak,
|
610
|
+
"op cut": icons8_laser_beam,
|
611
|
+
"op image": icons8_image,
|
612
|
+
"op raster": icons8_direction,
|
613
|
+
"op dots": icon_points,
|
614
|
+
"effect hatch": icon_effect_hatch,
|
615
|
+
"effect wobble": icon_effect_wobble,
|
616
|
+
"effect warp": icon_distort,
|
617
|
+
"place current": icons8_home_filled,
|
618
|
+
"place point": icons8_home_filled,
|
619
|
+
"elem point": icon_points,
|
620
|
+
"file": icons8_file,
|
621
|
+
"group": icons8_group_objects,
|
622
|
+
"elem rect": icon_mk_rectangular,
|
623
|
+
"elem ellipse": icon_mk_ellipse,
|
624
|
+
"elem image": icons8_image,
|
625
|
+
"elem path": icon_path,
|
626
|
+
"elem line": icon_line,
|
627
|
+
"elem polyline": icon_mk_polyline,
|
628
|
+
"elem text": icon_bmap_text,
|
629
|
+
"image raster": icons8_image,
|
630
|
+
"blob": icons8_file,
|
631
|
+
}
|
632
|
+
self.image_cache = []
|
633
|
+
self.cache_hits = 0
|
634
|
+
self.cache_requests = 0
|
635
|
+
self.color_cache = {}
|
636
|
+
self.formatter_cache = {}
|
637
|
+
self._too_big = False
|
638
|
+
self.refresh_tree_counter = 0
|
639
|
+
self._last_hover_item = None
|
640
|
+
|
641
|
+
def service_attach(self, *args):
|
642
|
+
self.elements.listen_tree(self)
|
643
|
+
|
644
|
+
def service_detach(self, *args):
|
645
|
+
self.elements.unlisten_tree(self)
|
646
|
+
|
647
|
+
def setup_state_images(self):
|
648
|
+
self.state_images = wx.ImageList()
|
649
|
+
self.iconstates = {}
|
650
|
+
self.state_images.Create(width=self.iconsize, height=self.iconsize)
|
651
|
+
image = icons8_lock.GetBitmap(
|
652
|
+
resize=(self.iconsize, self.iconsize),
|
653
|
+
noadjustment=True,
|
654
|
+
buffer=1,
|
655
|
+
)
|
656
|
+
image_id = self.state_images.Add(bitmap=image)
|
657
|
+
self.iconstates["lock"] = image_id
|
658
|
+
image = icons8_r_white.GetBitmap(
|
659
|
+
resize=(self.iconsize, self.iconsize),
|
660
|
+
noadjustment=True,
|
661
|
+
buffer=1,
|
662
|
+
)
|
663
|
+
image_id = self.state_images.Add(bitmap=image)
|
664
|
+
self.iconstates["refobject"] = image_id
|
665
|
+
image = icon_warning.GetBitmap(
|
666
|
+
resize=(self.iconsize, self.iconsize),
|
667
|
+
noadjustment=True,
|
668
|
+
buffer=1,
|
669
|
+
)
|
670
|
+
image_id = self.state_images.Add(bitmap=image)
|
671
|
+
self.iconstates["warning"] = image_id
|
672
|
+
image = icons8_ghost.GetBitmap(
|
673
|
+
resize=(self.iconsize, self.iconsize),
|
674
|
+
noadjustment=True,
|
675
|
+
buffer=1,
|
676
|
+
)
|
677
|
+
image_id = self.state_images.Add(bitmap=image)
|
678
|
+
self.iconstates["ghost"] = image_id
|
679
|
+
self.wxtree.SetStateImageList(self.state_images)
|
680
|
+
|
681
|
+
def node_created(self, node, **kwargs):
|
682
|
+
"""
|
683
|
+
Notified that this node has been created.
|
684
|
+
@param node: Node that was created.
|
685
|
+
@param kwargs:
|
686
|
+
@return:
|
687
|
+
"""
|
688
|
+
if self._freeze or self.context.elements.suppress_updates:
|
689
|
+
return
|
690
|
+
self.elements.signal("modified")
|
691
|
+
|
692
|
+
def node_destroyed(self, node, **kwargs):
|
693
|
+
"""
|
694
|
+
Notified that this node has been destroyed.
|
695
|
+
@param node: Node that was destroyed.
|
696
|
+
@param kwargs:
|
697
|
+
@return:
|
698
|
+
"""
|
699
|
+
if self._freeze or self.context.elements.suppress_updates:
|
700
|
+
return
|
701
|
+
self.elements.signal("modified")
|
702
|
+
self.elements.signal("warn_state_update")
|
703
|
+
|
704
|
+
def node_detached(self, node, **kwargs):
|
705
|
+
"""
|
706
|
+
Notified that this node has been detached from the tree.
|
707
|
+
@param node: Node that was detached.
|
708
|
+
@param kwargs:
|
709
|
+
@return:
|
710
|
+
"""
|
711
|
+
self.unregister_children(node)
|
712
|
+
self.node_unregister(node, **kwargs)
|
713
|
+
|
714
|
+
def node_attached(self, node, **kwargs):
|
715
|
+
"""
|
716
|
+
Notified that this node has been attached to the tree.
|
717
|
+
@param node: Node that was attached.
|
718
|
+
@param kwargs:
|
719
|
+
@return:
|
720
|
+
"""
|
721
|
+
self.node_register(node, **kwargs)
|
722
|
+
self.register_children(node)
|
723
|
+
if node.expanded:
|
724
|
+
# Needs to be done later...
|
725
|
+
self._nodes_to_expand.append(node)
|
726
|
+
if not self.context.elements.suppress_signalling:
|
727
|
+
self.context.elements.signal("sync_expansion")
|
728
|
+
|
729
|
+
def sync_expansion(self):
|
730
|
+
for node in self._nodes_to_expand:
|
731
|
+
item = node._item
|
732
|
+
if item is None or not item.IsOk():
|
733
|
+
continue
|
734
|
+
if node.expanded:
|
735
|
+
self.wxtree.Expand(item)
|
736
|
+
else:
|
737
|
+
self.wxtree.Collapse(item)
|
738
|
+
self._nodes_to_expand.clear()
|
739
|
+
|
740
|
+
def node_changed(self, node):
|
741
|
+
"""
|
742
|
+
Notified that this node has been changed.
|
743
|
+
@param node: Node that was changed.
|
744
|
+
@return:
|
745
|
+
"""
|
746
|
+
if self._freeze or self.context.elements.suppress_updates:
|
747
|
+
return
|
748
|
+
item = node._item
|
749
|
+
self.check_validity(item)
|
750
|
+
try:
|
751
|
+
self.update_decorations(node, force=True)
|
752
|
+
except RuntimeError:
|
753
|
+
# A timer can update after the tree closes.
|
754
|
+
return
|
755
|
+
|
756
|
+
def check_validity(self, item):
|
757
|
+
if item is None or not item.IsOk():
|
758
|
+
# raise ValueError("Bad Item")
|
759
|
+
self.rebuild_tree(source="validity", target="all")
|
760
|
+
self.elements.signal("refresh_scene", "Scene")
|
761
|
+
return False
|
762
|
+
return True
|
763
|
+
|
764
|
+
def selected(self, node):
|
765
|
+
"""
|
766
|
+
Notified that this node was selected.
|
767
|
+
|
768
|
+
Directly selected within the tree, specifically selected within the treectrl
|
769
|
+
@param node:
|
770
|
+
@return:
|
771
|
+
"""
|
772
|
+
if self._freeze or self.context.elements.suppress_updates:
|
773
|
+
return
|
774
|
+
item = node._item
|
775
|
+
self.check_validity(item)
|
776
|
+
# self.update_decorations(node)
|
777
|
+
self.set_enhancements(node)
|
778
|
+
if not self.context.elements.suppress_signalling:
|
779
|
+
self.elements.signal("selected", node)
|
780
|
+
|
781
|
+
def emphasized(self, node):
|
782
|
+
"""
|
783
|
+
Notified that this node was emphasized.
|
784
|
+
|
785
|
+
Item is selected by being emphasized this is treated like a soft selection throughout
|
786
|
+
@param node:
|
787
|
+
@return:
|
788
|
+
"""
|
789
|
+
if self._freeze or self.context.elements.suppress_updates:
|
790
|
+
return
|
791
|
+
item = node._item
|
792
|
+
self.check_validity(item)
|
793
|
+
# self.update_decorations(node)
|
794
|
+
self.set_enhancements(node)
|
795
|
+
if not self.context.elements.suppress_signalling:
|
796
|
+
self.elements.signal("emphasized", node)
|
797
|
+
|
798
|
+
def targeted(self, node):
|
799
|
+
"""
|
800
|
+
Notified that this node was targeted.
|
801
|
+
|
802
|
+
If any element is emphasized, all operations containing that element are targeted.
|
803
|
+
@param node:
|
804
|
+
@return:
|
805
|
+
"""
|
806
|
+
if self._freeze or self.context.elements.suppress_updates:
|
807
|
+
return
|
808
|
+
item = node._item
|
809
|
+
self.check_validity(item)
|
810
|
+
self.update_decorations(node)
|
811
|
+
self.set_enhancements(node)
|
812
|
+
if not self.context.elements.suppress_signalling:
|
813
|
+
self.elements.signal("targeted", node)
|
814
|
+
|
815
|
+
def highlighted(self, node):
|
816
|
+
"""
|
817
|
+
Notified that this node was highlighted.
|
818
|
+
|
819
|
+
If any operation is selected, all sub-operations are highlighted.
|
820
|
+
If any element is emphasized, all copies are highlighted.
|
821
|
+
@param node:
|
822
|
+
@return:
|
823
|
+
"""
|
824
|
+
if self._freeze or self.context.elements.suppress_updates:
|
825
|
+
return
|
826
|
+
item = node._item
|
827
|
+
self.check_validity(item)
|
828
|
+
# self.update_decorations(node)
|
829
|
+
self.set_enhancements(node)
|
830
|
+
if not self.context.elements.suppress_signalling:
|
831
|
+
self.elements.signal("highlighted", node)
|
832
|
+
|
833
|
+
def translated(self, node, dx=0, dy=0, interim=False, *args):
|
834
|
+
"""
|
835
|
+
This node was moved
|
836
|
+
"""
|
837
|
+
return
|
838
|
+
|
839
|
+
def scaled(self, node, sx=1, sy=1, ox=0, oy=0, interim=False, *args):
|
840
|
+
"""
|
841
|
+
This node was scaled
|
842
|
+
"""
|
843
|
+
return
|
844
|
+
|
845
|
+
def modified(self, node):
|
846
|
+
"""
|
847
|
+
Notified that this node was modified.
|
848
|
+
This node position values were changed, but nothing about the core data was altered.
|
849
|
+
@param node:
|
850
|
+
@return:
|
851
|
+
"""
|
852
|
+
if self._freeze or self.context.elements.suppress_updates:
|
853
|
+
return
|
854
|
+
|
855
|
+
if node is None or not hasattr(node, "_item"):
|
856
|
+
return
|
857
|
+
|
858
|
+
item = node._item
|
859
|
+
if item is None or not item.IsOk():
|
860
|
+
return
|
861
|
+
|
862
|
+
try:
|
863
|
+
self.update_decorations(node, force=True)
|
864
|
+
except RuntimeError:
|
865
|
+
# A timer can update after the tree closes.
|
866
|
+
return
|
867
|
+
|
868
|
+
try:
|
869
|
+
c = node.color
|
870
|
+
self.set_color(node, c)
|
871
|
+
except AttributeError:
|
872
|
+
pass
|
873
|
+
self.elements.signal("modified", node)
|
874
|
+
|
875
|
+
def altered(self, node, *args, **kwargs):
|
876
|
+
"""
|
877
|
+
Notified that this node was altered.
|
878
|
+
This node was changed in fundamental ways and nothing about this node remains trusted.
|
879
|
+
@param node:
|
880
|
+
@return:
|
881
|
+
"""
|
882
|
+
if self._freeze or self.context.elements.suppress_updates:
|
883
|
+
return
|
884
|
+
item = node._item
|
885
|
+
self.check_validity(item)
|
886
|
+
try:
|
887
|
+
self.update_decorations(node, force=True)
|
888
|
+
except RuntimeError:
|
889
|
+
# A timer can update after the tree closes.
|
890
|
+
return
|
891
|
+
try:
|
892
|
+
c = node.color
|
893
|
+
self.set_color(node, c)
|
894
|
+
except AttributeError:
|
895
|
+
pass
|
896
|
+
self.elements.signal("altered", node)
|
897
|
+
|
898
|
+
def expand(self, node):
|
899
|
+
"""
|
900
|
+
Notified that this node was expanded.
|
901
|
+
|
902
|
+
@param node:
|
903
|
+
@return:
|
904
|
+
"""
|
905
|
+
if self._freeze or self.context.elements.suppress_updates:
|
906
|
+
return
|
907
|
+
node.expanded = True
|
908
|
+
item = node._item
|
909
|
+
self.check_validity(item)
|
910
|
+
self.wxtree.ExpandAllChildren(item)
|
911
|
+
self.set_expanded(item, 1)
|
912
|
+
|
913
|
+
def collapse_within(self, node):
|
914
|
+
# Tries to collapse children first, if there were any open,
|
915
|
+
# return TRUE, if all were already collapsed, return FALSE
|
916
|
+
result = False
|
917
|
+
startnode = node._item
|
918
|
+
try:
|
919
|
+
pnode, cookie = self.wxtree.GetFirstChild(startnode)
|
920
|
+
except:
|
921
|
+
return
|
922
|
+
were_expanded = []
|
923
|
+
while pnode.IsOk():
|
924
|
+
if self.wxtree.IsExpanded(pnode):
|
925
|
+
result = True
|
926
|
+
were_expanded.append(pnode)
|
927
|
+
pnode, cookie = self.wxtree.GetNextChild(startnode, cookie)
|
928
|
+
for pnode in were_expanded:
|
929
|
+
cnode = self.wxtree.GetItemData(pnode)
|
930
|
+
cnode.notify_collapse()
|
931
|
+
return result
|
932
|
+
|
933
|
+
def collapse(self, node):
|
934
|
+
"""
|
935
|
+
Notified that this node was collapsed.
|
936
|
+
|
937
|
+
@param node:
|
938
|
+
@return:
|
939
|
+
"""
|
940
|
+
if node is None:
|
941
|
+
return
|
942
|
+
node.expanded = False
|
943
|
+
item = node._item
|
944
|
+
if item is None:
|
945
|
+
return
|
946
|
+
self.check_validity(item)
|
947
|
+
# Special treatment for branches, they only collapse fully,
|
948
|
+
# if all their childrens were collapsed already
|
949
|
+
if node.type.startswith("branch") and self.collapse_within(node):
|
950
|
+
return
|
951
|
+
self.wxtree.CollapseAllChildren(item)
|
952
|
+
if (
|
953
|
+
item is self.wxtree.GetRootItem()
|
954
|
+
or self.wxtree.GetItemParent(item) is self.wxtree.GetRootItem()
|
955
|
+
):
|
956
|
+
self.wxtree.Expand(self.elements.get(type="branch ops")._item)
|
957
|
+
self.wxtree.Expand(self.elements.get(type="branch elems")._item)
|
958
|
+
self.wxtree.Expand(self.elements.get(type="branch reg")._item)
|
959
|
+
|
960
|
+
def reorder(self, node):
|
961
|
+
"""
|
962
|
+
Notified that this node was reordered.
|
963
|
+
|
964
|
+
Tree is rebuilt.
|
965
|
+
|
966
|
+
@param node:
|
967
|
+
@return:
|
968
|
+
"""
|
969
|
+
target = "all"
|
970
|
+
while node.parent is not None:
|
971
|
+
if node.parent.type == "branch reg":
|
972
|
+
target = "regmarks"
|
973
|
+
break
|
974
|
+
if node.parent.type == "branch elem":
|
975
|
+
target = "elements"
|
976
|
+
break
|
977
|
+
if node.parent.type == "branch ops":
|
978
|
+
target = "operations"
|
979
|
+
break
|
980
|
+
node = node.parent
|
981
|
+
|
982
|
+
self.rebuild_tree("reorder", target=target)
|
983
|
+
|
984
|
+
def update(self, node):
|
985
|
+
"""
|
986
|
+
Notified that this node has been updated.
|
987
|
+
@param node:
|
988
|
+
@return:
|
989
|
+
"""
|
990
|
+
if self._freeze or self.context.elements.suppress_updates:
|
991
|
+
return
|
992
|
+
item = node._item
|
993
|
+
if item is None:
|
994
|
+
# Could be a faulty refresh during an undo.
|
995
|
+
return
|
996
|
+
self.check_validity(item)
|
997
|
+
self.set_icon(node, force=False)
|
998
|
+
self.on_force_element_update(node)
|
999
|
+
|
1000
|
+
def focus(self, node):
|
1001
|
+
"""
|
1002
|
+
Notified that this node has been focused.
|
1003
|
+
|
1004
|
+
It must be seen in the tree.
|
1005
|
+
@param node:
|
1006
|
+
@return:
|
1007
|
+
"""
|
1008
|
+
if self._freeze or self.context.elements.suppress_updates:
|
1009
|
+
return
|
1010
|
+
item = node._item
|
1011
|
+
self.check_validity(item)
|
1012
|
+
self.wxtree.EnsureVisible(item)
|
1013
|
+
self.wxtree.ScrollTo(item)
|
1014
|
+
# self.wxtree.SetFocusedItem(item)
|
1015
|
+
|
1016
|
+
def on_force_element_update(self, *args):
|
1017
|
+
"""
|
1018
|
+
Called by signal "element_property_reload"
|
1019
|
+
@param args:
|
1020
|
+
@return:
|
1021
|
+
"""
|
1022
|
+
element = args[0]
|
1023
|
+
if isinstance(element, (tuple, list)):
|
1024
|
+
for node in element:
|
1025
|
+
if hasattr(node, "node"):
|
1026
|
+
node = node.node
|
1027
|
+
try:
|
1028
|
+
self.update_decorations(node, force=True)
|
1029
|
+
for refnode in node.references:
|
1030
|
+
self.update_decorations(refnode, force=True)
|
1031
|
+
except RuntimeError:
|
1032
|
+
# A timer can update after the tree closes.
|
1033
|
+
return
|
1034
|
+
else:
|
1035
|
+
try:
|
1036
|
+
self.update_decorations(element, force=True)
|
1037
|
+
for refnode in element.references:
|
1038
|
+
self.update_decorations(refnode, force=True)
|
1039
|
+
except RuntimeError:
|
1040
|
+
# A timer can update after the tree closes.
|
1041
|
+
return
|
1042
|
+
|
1043
|
+
def on_element_update(self, *args):
|
1044
|
+
"""
|
1045
|
+
Called by signal "element_property_update"
|
1046
|
+
@param args:
|
1047
|
+
@return:
|
1048
|
+
"""
|
1049
|
+
element = args[0]
|
1050
|
+
if isinstance(element, (tuple, list)):
|
1051
|
+
for node in element:
|
1052
|
+
if hasattr(node, "node"):
|
1053
|
+
node = node.node
|
1054
|
+
try:
|
1055
|
+
self.update_decorations(node, force=True)
|
1056
|
+
except RuntimeError:
|
1057
|
+
# A timer can update after the tree closes.
|
1058
|
+
return
|
1059
|
+
else:
|
1060
|
+
try:
|
1061
|
+
self.update_decorations(element, force=True)
|
1062
|
+
except RuntimeError:
|
1063
|
+
# A timer can update after the tree closes.
|
1064
|
+
return
|
1065
|
+
|
1066
|
+
def refresh_tree(self, node=None, level=0, source=""):
|
1067
|
+
"""
|
1068
|
+
This no longer has any relevance, as the updates are properly done outside...
|
1069
|
+
"""
|
1070
|
+
# if node is None:
|
1071
|
+
# self.context.elements.set_start_time("refresh_tree")
|
1072
|
+
# self.refresh_tree_counter = 0
|
1073
|
+
# elemtree = self.elements._tree
|
1074
|
+
# node = elemtree._item
|
1075
|
+
# level = 0
|
1076
|
+
# else:
|
1077
|
+
# self.refresh_tree_counter += 1
|
1078
|
+
|
1079
|
+
# if node is None:
|
1080
|
+
# return
|
1081
|
+
self.context.elements.set_start_time("refresh_tree")
|
1082
|
+
self.freeze_tree(True)
|
1083
|
+
self.update_op_labels()
|
1084
|
+
if node is not None:
|
1085
|
+
if isinstance(node, (tuple, list)):
|
1086
|
+
for enode in node:
|
1087
|
+
if hasattr(enode, "node"):
|
1088
|
+
enode = enode.node
|
1089
|
+
try:
|
1090
|
+
self.update_decorations(enode, force=True)
|
1091
|
+
except RuntimeError:
|
1092
|
+
# A timer can update after the tree closes.
|
1093
|
+
return
|
1094
|
+
else:
|
1095
|
+
try:
|
1096
|
+
self.update_decorations(node, force=True)
|
1097
|
+
except RuntimeError:
|
1098
|
+
# A timer can update after the tree closes.
|
1099
|
+
return
|
1100
|
+
|
1101
|
+
branch_elems_item = self.elements.get(type="branch elems")._item
|
1102
|
+
if branch_elems_item:
|
1103
|
+
self.wxtree.Expand(branch_elems_item)
|
1104
|
+
branch_reg_item = self.elements.get(type="branch reg")._item
|
1105
|
+
if branch_reg_item:
|
1106
|
+
self.wxtree.Expand(branch_reg_item)
|
1107
|
+
self.context.elements.signal("warn_state_update")
|
1108
|
+
self.freeze_tree(False)
|
1109
|
+
self.context.elements.set_end_time("full_load", display=True, delete=True)
|
1110
|
+
self.context.elements.set_end_time("refresh_tree", display=True)
|
1111
|
+
|
1112
|
+
def update_warn_sign(self):
|
1113
|
+
# from time import perf_counter
|
1114
|
+
# this_call = perf_counter()
|
1115
|
+
# print (f"Update warn was called, time since last: {this_call-self.last_call:.3f}sec")
|
1116
|
+
# self.last_call = this_call
|
1117
|
+
op_node = self.elements.get(type="branch ops")
|
1118
|
+
if op_node is None:
|
1119
|
+
return
|
1120
|
+
op_item = op_node._item
|
1121
|
+
|
1122
|
+
status = ""
|
1123
|
+
if op_item is None:
|
1124
|
+
return
|
1125
|
+
|
1126
|
+
self.wxtree.Expand(op_item)
|
1127
|
+
unassigned, unburnt = self.elements.have_unburnable_elements()
|
1128
|
+
needs_showing = False
|
1129
|
+
warn_level = self.context.setting(int, "concern_level", 1)
|
1130
|
+
messages = []
|
1131
|
+
if unassigned and warn_level <= 2:
|
1132
|
+
needs_showing = True
|
1133
|
+
messages.append( _("You have unassigned elements, that won't be burned") )
|
1134
|
+
if unburnt and warn_level <= 1:
|
1135
|
+
needs_showing = True
|
1136
|
+
messages.append( _("You have elements in disabled operations, that won't be burned") )
|
1137
|
+
|
1138
|
+
if needs_showing:
|
1139
|
+
self.wxtree.SetItemState(op_item, self.iconstates["warning"])
|
1140
|
+
status = "\n".join(messages)
|
1141
|
+
else:
|
1142
|
+
self.wxtree.SetItemState(op_item, wx.TREE_ITEMSTATE_NONE)
|
1143
|
+
status = ""
|
1144
|
+
op_node._tooltip = status
|
1145
|
+
op_node._tooltip_translated = True
|
1146
|
+
|
1147
|
+
def freeze_tree(self, status=None):
|
1148
|
+
if status is None:
|
1149
|
+
status = not self._freeze
|
1150
|
+
if self._freeze != status:
|
1151
|
+
self._freeze = status
|
1152
|
+
self.wxtree.Enable(not self._freeze)
|
1153
|
+
if status:
|
1154
|
+
self.wxtree.Freeze()
|
1155
|
+
else:
|
1156
|
+
self.wxtree.Thaw()
|
1157
|
+
self.wxtree.Refresh()
|
1158
|
+
|
1159
|
+
def frozen(self, status):
|
1160
|
+
self.wxtree.Enable(not status)
|
1161
|
+
if status:
|
1162
|
+
self.wxtree.Freeze()
|
1163
|
+
else:
|
1164
|
+
self.wxtree.Thaw()
|
1165
|
+
self.wxtree.Refresh()
|
1166
|
+
|
1167
|
+
def was_expanded(self, node, level):
|
1168
|
+
txt = self.wxtree.GetItemText(node)
|
1169
|
+
chk = f"{level}-{txt}"
|
1170
|
+
return any(chk == elem for elem in self.was_already_expanded)
|
1171
|
+
|
1172
|
+
def set_expanded(self, node, level):
|
1173
|
+
txt = self.wxtree.GetItemText(node)
|
1174
|
+
chk = f"{level}-{txt}"
|
1175
|
+
result = self.was_expanded(node, level)
|
1176
|
+
if not result:
|
1177
|
+
self.was_already_expanded.append(chk)
|
1178
|
+
|
1179
|
+
# These routines were supposed to save and restore the expanded state of the tree
|
1180
|
+
# But that did not work out as intended....
|
1181
|
+
#
|
1182
|
+
# def parse_tree(self, startnode, level):
|
1183
|
+
# if startnode is None:
|
1184
|
+
# return
|
1185
|
+
# cookie = 0
|
1186
|
+
# try:
|
1187
|
+
# pnode, cookie = self.wxtree.GetFirstChild(startnode)
|
1188
|
+
# except:
|
1189
|
+
# return
|
1190
|
+
# while pnode.IsOk():
|
1191
|
+
# txt = self.wxtree.GetItemText(pnode)
|
1192
|
+
# # That is not working as advertised...
|
1193
|
+
# state = self.wxtree.IsExpanded(pnode)
|
1194
|
+
# if state:
|
1195
|
+
# self.was_already_expanded.append(f"{level}-{txt}")
|
1196
|
+
# self.parse_tree(pnode, level + 1)
|
1197
|
+
# pnode, cookie = self.wxtree.GetNextChild(startnode, cookie)
|
1198
|
+
|
1199
|
+
# def restore_tree(self, startnode, level):
|
1200
|
+
# if startnode is None:
|
1201
|
+
# return
|
1202
|
+
# cookie = 0
|
1203
|
+
# try:
|
1204
|
+
# pnode, cookie = self.wxtree.GetFirstChild(startnode)
|
1205
|
+
# except:
|
1206
|
+
# return
|
1207
|
+
# while pnode.IsOk():
|
1208
|
+
# txt = self.wxtree.GetItemText(pnode)
|
1209
|
+
# chk = f"{level}-{txt}"
|
1210
|
+
# for elem in self.was_already_expanded:
|
1211
|
+
# if chk == elem:
|
1212
|
+
# self.wxtree.ExpandAllChildren(pnode)
|
1213
|
+
# break
|
1214
|
+
# self.parse_tree(pnode, level + 1)
|
1215
|
+
# pnode, cookie = self.wxtree.GetNextChild(startnode, cookie)
|
1216
|
+
#
|
1217
|
+
# def reset_expanded(self):
|
1218
|
+
# self.was_already_expanded = []
|
1219
|
+
|
1220
|
+
def reset_dragging(self):
|
1221
|
+
self.dragging_nodes = None
|
1222
|
+
self.wxtree.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
|
1223
|
+
|
1224
|
+
def rebuild_tree(self, source:str, target:str="all" ):
|
1225
|
+
"""
|
1226
|
+
Tree requires being deleted and completely rebuilt.
|
1227
|
+
|
1228
|
+
@return:
|
1229
|
+
"""
|
1230
|
+
# print (f"Rebuild called from {source}")
|
1231
|
+
# let's try to remember which branches were expanded:
|
1232
|
+
busy = wx.BusyCursor()
|
1233
|
+
self.context.elements.set_start_time(f"rebuild_tree_{target}")
|
1234
|
+
self.freeze_tree(True)
|
1235
|
+
|
1236
|
+
# self.reset_expanded()
|
1237
|
+
|
1238
|
+
# Safety net - if we have too many elements it will
|
1239
|
+
# take too long to create all preview icons...
|
1240
|
+
count = self.elements.count_elems() + self.elements.count_op()
|
1241
|
+
self._too_big = count > 1000
|
1242
|
+
# print(f"Was too big?! {count} -> {self._too_big}")
|
1243
|
+
|
1244
|
+
# self.parse_tree(self.wxtree.GetRootItem(), 0)
|
1245
|
+
# Rebuild tree destroys the emphasis, so let's store it...
|
1246
|
+
def delete_items(target):
|
1247
|
+
if target == "regmarks":
|
1248
|
+
node = self.elements.reg_branch
|
1249
|
+
item = node._item
|
1250
|
+
if item is not None:
|
1251
|
+
self.wxtree.DeleteChildren(item)
|
1252
|
+
elif target == "operations":
|
1253
|
+
node = self.elements.op_branch
|
1254
|
+
item = node._item
|
1255
|
+
if item is not None:
|
1256
|
+
self.wxtree.DeleteChildren(item)
|
1257
|
+
elif target == "elements":
|
1258
|
+
node = self.elements.elem_branch
|
1259
|
+
item = node._item
|
1260
|
+
if item is not None:
|
1261
|
+
self.wxtree.DeleteChildren(item)
|
1262
|
+
else:
|
1263
|
+
self.wxtree.DeleteAllItems()
|
1264
|
+
|
1265
|
+
def rebuild_items(target):
|
1266
|
+
if target == "all":
|
1267
|
+
if self.tree_images is not None:
|
1268
|
+
self.tree_images.Destroy()
|
1269
|
+
self.image_cache = []
|
1270
|
+
self.tree_images = wx.ImageList()
|
1271
|
+
self.tree_images.Create(width=self.iconsize, height=self.iconsize)
|
1272
|
+
|
1273
|
+
self.wxtree.SetImageList(self.tree_images)
|
1274
|
+
if target == "regmarks":
|
1275
|
+
elemtree = self.elements.reg_branch
|
1276
|
+
elif target == "operations":
|
1277
|
+
elemtree = self.elements.op_branch
|
1278
|
+
elif target == "elements":
|
1279
|
+
elemtree = self.elements.elem_branch
|
1280
|
+
else:
|
1281
|
+
elemtree = self.elements._tree
|
1282
|
+
elemtree._item = self.wxtree.AddRoot(self.name)
|
1283
|
+
self.wxtree.SetItemData(elemtree._item, elemtree)
|
1284
|
+
self.set_icon(
|
1285
|
+
elemtree,
|
1286
|
+
icon_meerk40t.GetBitmap(
|
1287
|
+
False,
|
1288
|
+
resize=(self.iconsize, self.iconsize),
|
1289
|
+
noadjustment=True,
|
1290
|
+
buffer=1,
|
1291
|
+
),
|
1292
|
+
)
|
1293
|
+
self.register_children(elemtree)
|
1294
|
+
branch_list = (
|
1295
|
+
("branch ops", "operations", icons8_laser_beam),
|
1296
|
+
("branch reg", "regmarks", icon_regmarks),
|
1297
|
+
("branch elems", "elements", icon_canvas),
|
1298
|
+
)
|
1299
|
+
for branch_name, branch_type, icon in branch_list:
|
1300
|
+
if target not in ("all", branch_type):
|
1301
|
+
continue
|
1302
|
+
node_branch = elemtree.get(type=branch_name)
|
1303
|
+
self.set_icon(
|
1304
|
+
node_branch,
|
1305
|
+
icon.GetBitmap(
|
1306
|
+
resize=(self.iconsize, self.iconsize),
|
1307
|
+
noadjustment=True,
|
1308
|
+
buffer=1,
|
1309
|
+
),
|
1310
|
+
)
|
1311
|
+
for n in node_branch.children:
|
1312
|
+
self.set_icon(n, force=True)
|
1313
|
+
if target in {"all", "operations"}:
|
1314
|
+
self.update_op_labels()
|
1315
|
+
if target in {"all", "elements"}:
|
1316
|
+
self.update_group_labels("rebuild_tree")
|
1317
|
+
|
1318
|
+
emphasized_list = list(self.elements.elems(emphasized=True))
|
1319
|
+
|
1320
|
+
delete_items(target)
|
1321
|
+
rebuild_items(target)
|
1322
|
+
|
1323
|
+
# Expand Ops, Element, and Regmarks nodes only
|
1324
|
+
# self.wxtree.CollapseAll()
|
1325
|
+
self.wxtree.Expand(self.elements.op_branch._item)
|
1326
|
+
self.wxtree.Expand(self.elements.elem_branch._item)
|
1327
|
+
self.wxtree.Expand(self.elements.reg_branch._item)
|
1328
|
+
startnode = self.elements._tree._item
|
1329
|
+
|
1330
|
+
def expand_leaf(snode):
|
1331
|
+
child, cookie = self.wxtree.GetFirstChild(snode)
|
1332
|
+
while child.IsOk():
|
1333
|
+
node = self.wxtree.GetItemData(child)
|
1334
|
+
if node.expanded:
|
1335
|
+
self.wxtree.Expand(child)
|
1336
|
+
expand_leaf(child)
|
1337
|
+
child, cookie = self.wxtree.GetNextChild(snode, cookie)
|
1338
|
+
|
1339
|
+
expand_leaf(startnode)
|
1340
|
+
self.elements.signal("warn_state_update")
|
1341
|
+
|
1342
|
+
# Restore emphasis
|
1343
|
+
for e in emphasized_list:
|
1344
|
+
e.emphasized = True
|
1345
|
+
# self.restore_tree(self.wxtree.GetRootItem(), 0)
|
1346
|
+
self.freeze_tree(False)
|
1347
|
+
self.context.elements.set_end_time(f"rebuild_tree_{target}", display=True)
|
1348
|
+
# print(f"Rebuild done for {source}")
|
1349
|
+
del busy
|
1350
|
+
|
1351
|
+
def register_children(self, node):
|
1352
|
+
"""
|
1353
|
+
All children of this node are registered.
|
1354
|
+
|
1355
|
+
@param node:
|
1356
|
+
@return:
|
1357
|
+
"""
|
1358
|
+
for child in node.children:
|
1359
|
+
self.node_register(child)
|
1360
|
+
self.register_children(child)
|
1361
|
+
if node.type in ("group", "file"):
|
1362
|
+
self.update_decorations(node, force=True)
|
1363
|
+
|
1364
|
+
def unregister_children(self, node):
|
1365
|
+
"""
|
1366
|
+
All children of this node are unregistered.
|
1367
|
+
@param node:
|
1368
|
+
@return:
|
1369
|
+
"""
|
1370
|
+
for child in node.children:
|
1371
|
+
self.unregister_children(child)
|
1372
|
+
self.node_unregister(child)
|
1373
|
+
|
1374
|
+
def node_unregister(self, node, **kwargs):
|
1375
|
+
"""
|
1376
|
+
Node object is unregistered and item is deleted.
|
1377
|
+
|
1378
|
+
@param node:
|
1379
|
+
@param kwargs:
|
1380
|
+
@return:
|
1381
|
+
"""
|
1382
|
+
self.do_not_select = True
|
1383
|
+
|
1384
|
+
item = node._item
|
1385
|
+
if item is None:
|
1386
|
+
raise ValueError(f"Item was None for node {repr(node)}")
|
1387
|
+
self.check_validity(item)
|
1388
|
+
# We might need to update the decorations for all parent objects
|
1389
|
+
informed = []
|
1390
|
+
if not self._freeze:
|
1391
|
+
parent = node._parent
|
1392
|
+
while parent is not None and not parent.type.startswith("branch "):
|
1393
|
+
informed.append(parent)
|
1394
|
+
parent = parent._parent
|
1395
|
+
|
1396
|
+
node.unregister_object()
|
1397
|
+
self.wxtree.Delete(node._item)
|
1398
|
+
if informed:
|
1399
|
+
self.context.signal("element_property_update", informed)
|
1400
|
+
for i in self.wxtree.GetSelections():
|
1401
|
+
self.wxtree.SelectItem(i, False)
|
1402
|
+
|
1403
|
+
self.do_not_select = False
|
1404
|
+
|
1405
|
+
def safe_color(self, color_to_set):
|
1406
|
+
_hash = str(color_to_set)
|
1407
|
+
if _hash not in self.color_cache:
|
1408
|
+
back_color = self.wxtree.GetBackgroundColour()
|
1409
|
+
rgb = back_color.Get()
|
1410
|
+
default_color = wx.Colour(
|
1411
|
+
red=255 - rgb[0], green=255 - rgb[1], blue=255 - rgb[2], alpha=128
|
1412
|
+
)
|
1413
|
+
if color_to_set is not None and color_to_set.argb is not None:
|
1414
|
+
mycolor = wx.Colour(swizzlecolor(color_to_set.argb))
|
1415
|
+
if mycolor.Get() == rgb:
|
1416
|
+
mycolor = default_color
|
1417
|
+
else:
|
1418
|
+
mycolor = default_color
|
1419
|
+
self.color_cache[_hash] = mycolor
|
1420
|
+
else:
|
1421
|
+
mycolor = self.color_cache[_hash]
|
1422
|
+
return mycolor
|
1423
|
+
|
1424
|
+
def node_register(self, node, pos=None, **kwargs):
|
1425
|
+
"""
|
1426
|
+
Node.item is added/inserted. Label is updated and values are set. Icon is set.
|
1427
|
+
|
1428
|
+
@param node:
|
1429
|
+
@param pos:
|
1430
|
+
@param kwargs:
|
1431
|
+
@return:
|
1432
|
+
"""
|
1433
|
+
try:
|
1434
|
+
parent = node.parent
|
1435
|
+
parent_item = parent._item
|
1436
|
+
if parent_item is None:
|
1437
|
+
# We are appending items in tree before registration.
|
1438
|
+
return
|
1439
|
+
tree = self.wxtree
|
1440
|
+
if pos is None:
|
1441
|
+
node._item = tree.AppendItem(parent_item, self.name)
|
1442
|
+
else:
|
1443
|
+
node._item = tree.InsertItem(parent_item, pos, self.name)
|
1444
|
+
tree.SetItemData(node._item, node)
|
1445
|
+
except Exception as e:
|
1446
|
+
# Invalid tree?
|
1447
|
+
self.context.signal("rebuild_tree", "all")
|
1448
|
+
print (f"We encountered an error at node registration: {e}")
|
1449
|
+
return
|
1450
|
+
self.update_decorations(node, False)
|
1451
|
+
wxcolor = self.wxtree.GetForegroundColour()
|
1452
|
+
attribute_to_try = "fill" if node.type == "elem text" else "stroke"
|
1453
|
+
if hasattr(node, attribute_to_try):
|
1454
|
+
wxcolor = self.safe_color(getattr(node, attribute_to_try))
|
1455
|
+
elif hasattr(node, "color"):
|
1456
|
+
wxcolor = self.safe_color(node.color)
|
1457
|
+
else:
|
1458
|
+
back_color = self.wxtree.GetBackgroundColour()
|
1459
|
+
rgb = back_color.Get()
|
1460
|
+
background = Color(rgb[0], rgb[1], rgb[2])
|
1461
|
+
if background is not None:
|
1462
|
+
c1 = Color("Black")
|
1463
|
+
c2 = Color("White")
|
1464
|
+
if Color.distance(background, c1) > Color.distance(background, c2):
|
1465
|
+
textcolor = c1
|
1466
|
+
else:
|
1467
|
+
textcolor = c2
|
1468
|
+
wxcolor = wx.Colour(swizzlecolor(textcolor))
|
1469
|
+
if self.context.root.tree_colored:
|
1470
|
+
try:
|
1471
|
+
tree.SetItemTextColour(node._item, wxcolor)
|
1472
|
+
except (AttributeError, KeyError, TypeError):
|
1473
|
+
pass
|
1474
|
+
# We might need to update the decorations for all parent objects
|
1475
|
+
if not self._freeze:
|
1476
|
+
informed = []
|
1477
|
+
parent = node._parent
|
1478
|
+
while parent is not None and not parent.type.startswith("branch "):
|
1479
|
+
informed.append(parent)
|
1480
|
+
parent = parent._parent
|
1481
|
+
if informed:
|
1482
|
+
self.context.signal("element_property_update", informed)
|
1483
|
+
|
1484
|
+
# self.context.signal("update_group_labels")
|
1485
|
+
|
1486
|
+
def set_enhancements(self, node):
|
1487
|
+
"""
|
1488
|
+
Node in the tree is drawn special based on nodes current setting.
|
1489
|
+
@param node:
|
1490
|
+
@return:
|
1491
|
+
"""
|
1492
|
+
tree = self.wxtree
|
1493
|
+
node_item = node._item
|
1494
|
+
if node_item is None:
|
1495
|
+
return
|
1496
|
+
if self._freeze or self.context.elements.suppress_updates:
|
1497
|
+
return
|
1498
|
+
tree.SetItemBackgroundColour(node_item, None)
|
1499
|
+
try:
|
1500
|
+
if node.highlighted:
|
1501
|
+
tree.SetItemBackgroundColour(node_item, wx.LIGHT_GREY)
|
1502
|
+
elif node.emphasized:
|
1503
|
+
tree.SetItemBackgroundColour(node_item, wx.Colour(0x80A0A0))
|
1504
|
+
elif node.targeted:
|
1505
|
+
tree.SetItemBackgroundColour(node_item, wx.Colour(0xA080A0))
|
1506
|
+
except AttributeError:
|
1507
|
+
pass
|
1508
|
+
|
1509
|
+
def set_color(self, node, color=None):
|
1510
|
+
"""
|
1511
|
+
Node color is set.
|
1512
|
+
|
1513
|
+
@param node: Not to be colored
|
1514
|
+
@param color: Color to be set.
|
1515
|
+
@return:
|
1516
|
+
"""
|
1517
|
+
if not self.context.root.tree_colored:
|
1518
|
+
return
|
1519
|
+
item = node._item
|
1520
|
+
if item is None:
|
1521
|
+
return
|
1522
|
+
if self._freeze or self.context.elements.suppress_updates:
|
1523
|
+
return
|
1524
|
+
tree = self.wxtree
|
1525
|
+
wxcolor = self.safe_color(color)
|
1526
|
+
tree.SetItemTextColour(item, wxcolor)
|
1527
|
+
|
1528
|
+
def create_image_from_node(self, node):
|
1529
|
+
image = None
|
1530
|
+
mini_icon = self.context.root.mini_icon and not self._too_big
|
1531
|
+
c = None
|
1532
|
+
self.cache_requests += 1
|
1533
|
+
cached_id = -1
|
1534
|
+
# Do we have a standard representation?
|
1535
|
+
defaultcolor = Color("black")
|
1536
|
+
if mini_icon:
|
1537
|
+
if node.type == "elem image":
|
1538
|
+
try:
|
1539
|
+
image = self.renderer.make_thumbnail(
|
1540
|
+
node.active_image, width=self.iconsize, height=self.iconsize
|
1541
|
+
)
|
1542
|
+
except (MemoryError, RuntimeError):
|
1543
|
+
image = None
|
1544
|
+
else:
|
1545
|
+
# Establish colors (and some images)
|
1546
|
+
if node.type.startswith("op ") or node.type.startswith("util "):
|
1547
|
+
if (
|
1548
|
+
hasattr(node, "color")
|
1549
|
+
and node.color is not None
|
1550
|
+
and node.color.argb is not None
|
1551
|
+
):
|
1552
|
+
c = node.color
|
1553
|
+
elif node.type == "reference":
|
1554
|
+
c, image, cached_id = self.create_image_from_node(node.node)
|
1555
|
+
elif node.type.startswith("elem "):
|
1556
|
+
if (
|
1557
|
+
hasattr(node, "stroke")
|
1558
|
+
and node.stroke is not None
|
1559
|
+
and node.stroke.argb is not None
|
1560
|
+
):
|
1561
|
+
c = node.stroke
|
1562
|
+
if node.type.startswith("elem ") and node.type != "elem point":
|
1563
|
+
image = self.renderer.make_raster(
|
1564
|
+
node,
|
1565
|
+
node.paint_bounds,
|
1566
|
+
width=self.iconsize,
|
1567
|
+
height=self.iconsize,
|
1568
|
+
bitmap=True,
|
1569
|
+
keep_ratio=True,
|
1570
|
+
)
|
1571
|
+
else:
|
1572
|
+
# Establish at least colors (and an image for a reference)
|
1573
|
+
if node.type.startswith("op ") or node.type.startswith("util "):
|
1574
|
+
if (
|
1575
|
+
hasattr(node, "color")
|
1576
|
+
and node.color is not None
|
1577
|
+
and node.color.argb is not None
|
1578
|
+
):
|
1579
|
+
c = node.color
|
1580
|
+
elif node.type == "reference":
|
1581
|
+
c, image, cached_id = self.create_image_from_node(node.node)
|
1582
|
+
elif node.type == "elem text":
|
1583
|
+
if (
|
1584
|
+
hasattr(node, "fill")
|
1585
|
+
and node.fill is not None
|
1586
|
+
and node.fill.argb is not None
|
1587
|
+
):
|
1588
|
+
c = node.fill
|
1589
|
+
elif node.type.startswith("elem ") or node.type.startswith("effect "):
|
1590
|
+
if (
|
1591
|
+
hasattr(node, "stroke")
|
1592
|
+
and node.stroke is not None
|
1593
|
+
and node.stroke.argb is not None
|
1594
|
+
):
|
1595
|
+
c = node.stroke
|
1596
|
+
|
1597
|
+
# Have we already established an image, if no let's use the default
|
1598
|
+
if image is None:
|
1599
|
+
found = ""
|
1600
|
+
tofind = node.type
|
1601
|
+
if tofind == "util console":
|
1602
|
+
# Let's see whether we find the keyword...
|
1603
|
+
for key in self.default_images:
|
1604
|
+
if key.startswith("console "):
|
1605
|
+
skey = key[8:]
|
1606
|
+
if node.command is not None and skey in node.command:
|
1607
|
+
found = key
|
1608
|
+
break
|
1609
|
+
|
1610
|
+
if not found and tofind in self.default_images:
|
1611
|
+
# print (f"Wasn't found use {tofind}")
|
1612
|
+
found = tofind
|
1613
|
+
|
1614
|
+
if found:
|
1615
|
+
for stored_key, stored_color, img_obj, c_id in self.image_cache:
|
1616
|
+
if stored_key == found and stored_color == c:
|
1617
|
+
image = img_obj
|
1618
|
+
cached_id = c_id
|
1619
|
+
self.cache_hits += 1
|
1620
|
+
# print (f"Restore id {cached_id} for {c} - {found}")
|
1621
|
+
break
|
1622
|
+
if image is None:
|
1623
|
+
# has not been found yet...
|
1624
|
+
img_obj = self.default_images[found]
|
1625
|
+
image = img_obj.GetBitmap(
|
1626
|
+
color=c,
|
1627
|
+
resize=(self.iconsize, self.iconsize),
|
1628
|
+
noadjustment=True,
|
1629
|
+
buffer=1,
|
1630
|
+
)
|
1631
|
+
cached_id = self.tree_images.Add(bitmap=image)
|
1632
|
+
# print(f"Store id {cached_id} for {c} - {found}")
|
1633
|
+
self.image_cache.append((found, c, image, cached_id))
|
1634
|
+
|
1635
|
+
if c is None:
|
1636
|
+
c = defaultcolor
|
1637
|
+
# print (f"Icon gives color: {c} and cached-id={cached_id}")
|
1638
|
+
return c, image, cached_id
|
1639
|
+
|
1640
|
+
def set_icon(self, node, icon=None, force=False):
|
1641
|
+
"""
|
1642
|
+
Node icon to be created and applied
|
1643
|
+
|
1644
|
+
@param node: Node to have the icon set.
|
1645
|
+
@param icon: overriding icon to be forcibly set, rather than a default.
|
1646
|
+
@param force: force the icon setting
|
1647
|
+
@return: item_id if newly created / update
|
1648
|
+
"""
|
1649
|
+
root = self
|
1650
|
+
drawmode = self.elements.root.draw_mode
|
1651
|
+
if drawmode & DRAW_MODE_ICONS != 0:
|
1652
|
+
return
|
1653
|
+
# if self._freeze or self.context.elements.suppress_updates:
|
1654
|
+
# return
|
1655
|
+
if node is None:
|
1656
|
+
return
|
1657
|
+
try:
|
1658
|
+
item = node._item
|
1659
|
+
except AttributeError:
|
1660
|
+
return # Node.item can be none if launched from ExecuteJob where the nodes are not part of the tree.
|
1661
|
+
if node._item is None:
|
1662
|
+
return
|
1663
|
+
tree = root.wxtree
|
1664
|
+
if icon is None:
|
1665
|
+
if force is None:
|
1666
|
+
force = False
|
1667
|
+
image_id = tree.GetItemImage(item)
|
1668
|
+
if image_id >= self.tree_images.ImageCount:
|
1669
|
+
image_id = -1
|
1670
|
+
if image_id >= 0 and not force:
|
1671
|
+
# Don't do it twice
|
1672
|
+
return image_id
|
1673
|
+
|
1674
|
+
# print ("Default size for iconsize, tree_images", self.iconsize, self.tree_images.GetSize())
|
1675
|
+
c, image, cached_id = self.create_image_from_node(node)
|
1676
|
+
|
1677
|
+
if image is not None:
|
1678
|
+
if cached_id >= 0:
|
1679
|
+
image_id = cached_id
|
1680
|
+
elif image_id < 0:
|
1681
|
+
image_id = self.tree_images.Add(bitmap=image)
|
1682
|
+
else:
|
1683
|
+
self.tree_images.Replace(index=image_id, bitmap=image)
|
1684
|
+
tree.SetItemImage(item, image=image_id)
|
1685
|
+
# Let's have a look at all references....
|
1686
|
+
for subnode in node.references:
|
1687
|
+
try:
|
1688
|
+
subitem = subnode._item
|
1689
|
+
except AttributeError:
|
1690
|
+
subitem = None
|
1691
|
+
if subitem is None:
|
1692
|
+
continue
|
1693
|
+
tree.SetItemImage(subitem, image=image_id)
|
1694
|
+
|
1695
|
+
if c is not None:
|
1696
|
+
self.set_color(node, c)
|
1697
|
+
|
1698
|
+
else:
|
1699
|
+
image_id = tree.GetItemImage(item)
|
1700
|
+
if image_id >= self.tree_images.ImageCount:
|
1701
|
+
image_id = -1
|
1702
|
+
# Reset Image Node in List
|
1703
|
+
if image_id < 0:
|
1704
|
+
image_id = self.tree_images.Add(bitmap=icon)
|
1705
|
+
else:
|
1706
|
+
self.tree_images.Replace(index=image_id, bitmap=icon)
|
1707
|
+
|
1708
|
+
tree.SetItemImage(item, image=image_id)
|
1709
|
+
return image_id
|
1710
|
+
|
1711
|
+
def update_op_labels(self):
|
1712
|
+
startnode = self.elements.get(type="branch ops")._item
|
1713
|
+
if startnode is None:
|
1714
|
+
# Branch op never populated the tree, we cannot update sublayer.
|
1715
|
+
return
|
1716
|
+
child, cookie = self.wxtree.GetFirstChild(startnode)
|
1717
|
+
while child.IsOk():
|
1718
|
+
node = self.wxtree.GetItemData(child) # Make sure the map is updated...
|
1719
|
+
self.update_decorations(node=node, force=True)
|
1720
|
+
child, cookie = self.wxtree.GetNextChild(startnode, cookie)
|
1721
|
+
|
1722
|
+
def update_group_labels(self, src):
|
1723
|
+
# print(f"group_labels: {src}")
|
1724
|
+
stop_updates = not self._freeze
|
1725
|
+
if stop_updates:
|
1726
|
+
self.freeze_tree(True)
|
1727
|
+
for e in self.context.elements.elems_nodes():
|
1728
|
+
if e.type == "group":
|
1729
|
+
self.update_decorations(e)
|
1730
|
+
for e in self.context.elements.regmarks_nodes():
|
1731
|
+
if e.type == "group":
|
1732
|
+
self.update_decorations(e)
|
1733
|
+
if stop_updates:
|
1734
|
+
self.freeze_tree(False)
|
1735
|
+
|
1736
|
+
def reset_formatter_cache(self):
|
1737
|
+
self.formatter_cache.clear()
|
1738
|
+
|
1739
|
+
def update_decorations(self, node, force=False):
|
1740
|
+
"""
|
1741
|
+
Updates the decorations for a particular node/tree item
|
1742
|
+
|
1743
|
+
@param node:
|
1744
|
+
@param force: force updating decorations
|
1745
|
+
@return:
|
1746
|
+
"""
|
1747
|
+
|
1748
|
+
def my_create_label(node, text=None):
|
1749
|
+
if text is None:
|
1750
|
+
try:
|
1751
|
+
text = node._formatter
|
1752
|
+
except AttributeError:
|
1753
|
+
text = "{element_type}:{id}"
|
1754
|
+
# Just for the optical impression (who understands what a "Rect: None" means),
|
1755
|
+
# let's replace some of the more obvious ones...
|
1756
|
+
mymap = node.default_map()
|
1757
|
+
# We change power to either ppi or percent
|
1758
|
+
if "power" in mymap and "ppi" in mymap and "percent" in mymap:
|
1759
|
+
self.context.device.setting(
|
1760
|
+
bool, "use_percent_for_power_display", False
|
1761
|
+
)
|
1762
|
+
if self.context.device.use_percent_for_power_display:
|
1763
|
+
mymap["power"] = mymap["percent"]
|
1764
|
+
if "speed" in mymap and "speed_mm_min" in mymap:
|
1765
|
+
self.context.device.setting(bool, "use_mm_min_for_speed_display", False)
|
1766
|
+
if self.context.device.use_mm_min_for_speed_display:
|
1767
|
+
text = text.replace("mm/s", "mm/min")
|
1768
|
+
mymap["speed"] = mymap["speed_mm_min"]
|
1769
|
+
mymap["speed_unit"] = "mm/min"
|
1770
|
+
else:
|
1771
|
+
mymap["speed_unit"] = "mm/s"
|
1772
|
+
for key in mymap:
|
1773
|
+
if hasattr(node, key) and key in mymap and mymap[key] == "None":
|
1774
|
+
if getattr(node, key) is None:
|
1775
|
+
mymap[key] = "-"
|
1776
|
+
# There are a couple of translatable entries,
|
1777
|
+
# to make sure we don't get an unwanted translation we add
|
1778
|
+
# a special pattern to it
|
1779
|
+
translatable = (
|
1780
|
+
"element_type",
|
1781
|
+
"enabled",
|
1782
|
+
)
|
1783
|
+
pattern = "_TREE_"
|
1784
|
+
for key in mymap:
|
1785
|
+
if key in translatable:
|
1786
|
+
# Original value
|
1787
|
+
std = mymap[key]
|
1788
|
+
value = _(pattern + std)
|
1789
|
+
if not value.startswith(pattern):
|
1790
|
+
mymap[key] = value
|
1791
|
+
try:
|
1792
|
+
res = text.format_map(mymap)
|
1793
|
+
except (ValueError, KeyError):
|
1794
|
+
res = text
|
1795
|
+
return res
|
1796
|
+
|
1797
|
+
def get_formatter(nodetype):
|
1798
|
+
if nodetype not in self.formatter_cache:
|
1799
|
+
default = self.context.elements.lookup(f"format/{nodetype}")
|
1800
|
+
lbl = nodetype.replace(" ", "_")
|
1801
|
+
check_string = f"formatter_{lbl}_active"
|
1802
|
+
pattern_string = f"formatter_{lbl}"
|
1803
|
+
self.context.device.setting(bool, check_string, False)
|
1804
|
+
self.context.device.setting(str, pattern_string, default)
|
1805
|
+
bespoke = getattr(self.context.device, check_string, False)
|
1806
|
+
pattern = getattr(self.context.device, pattern_string, "")
|
1807
|
+
if bespoke and pattern is not None and pattern != "":
|
1808
|
+
default = pattern
|
1809
|
+
self.formatter_cache[nodetype] = default
|
1810
|
+
return self.formatter_cache[nodetype]
|
1811
|
+
|
1812
|
+
if force is None:
|
1813
|
+
force = False
|
1814
|
+
if node._item is None:
|
1815
|
+
# This node is not registered the tree has desynced.
|
1816
|
+
self.rebuild_tree(source="desync", target="all")
|
1817
|
+
return
|
1818
|
+
|
1819
|
+
self.set_icon(node, force=force)
|
1820
|
+
if hasattr(node, "node") and node.node is not None:
|
1821
|
+
formatter = get_formatter(node.node.type)
|
1822
|
+
if node.node.type.startswith("op "):
|
1823
|
+
if not self.context.elements.op_show_default:
|
1824
|
+
if hasattr(node.node, "speed"):
|
1825
|
+
node.node.speed = node.node.speed
|
1826
|
+
if hasattr(node.node, "power"):
|
1827
|
+
node.node.power = node.node.power
|
1828
|
+
if hasattr(node.node, "dwell_time"):
|
1829
|
+
node.node.dwell_time = node.node.dwell_time
|
1830
|
+
|
1831
|
+
checker = f"dangerlevel_{node.type.replace(' ', '_')}"
|
1832
|
+
if hasattr(self.context.device, checker):
|
1833
|
+
maxspeed_minpower = getattr(self.context.device, checker)
|
1834
|
+
if (
|
1835
|
+
isinstance(maxspeed_minpower, (tuple, list))
|
1836
|
+
and len(maxspeed_minpower) == 8
|
1837
|
+
):
|
1838
|
+
# minpower, maxposer, minspeed, maxspeed
|
1839
|
+
# print ("Yes: ", checker, maxspeed_minpower)
|
1840
|
+
danger = False
|
1841
|
+
if hasattr(node.node, "power"):
|
1842
|
+
value = node.node.power
|
1843
|
+
if maxspeed_minpower[0] and value < maxspeed_minpower[1]:
|
1844
|
+
danger = True
|
1845
|
+
if maxspeed_minpower[2] and value > maxspeed_minpower[3]:
|
1846
|
+
danger = True
|
1847
|
+
if hasattr(node.node, "speed"):
|
1848
|
+
value = node.node.speed
|
1849
|
+
if maxspeed_minpower[4] and value < maxspeed_minpower[5]:
|
1850
|
+
danger = True
|
1851
|
+
if maxspeed_minpower[6] and value > maxspeed_minpower[7]:
|
1852
|
+
danger = True
|
1853
|
+
if hasattr(node.node, "dangerous"):
|
1854
|
+
node.node.dangerous = danger
|
1855
|
+
else:
|
1856
|
+
setattr(self.context.device, checker, [False, 0] * 4)
|
1857
|
+
print(
|
1858
|
+
f"That's strange {checker}: {type(maxspeed_minpower).__name__}"
|
1859
|
+
)
|
1860
|
+
# node.node.is_dangerous(maxspeed, minpower)
|
1861
|
+
# label = "*" + node.node.create_label(formatter)
|
1862
|
+
label = f"*{my_create_label(node.node, formatter)}"
|
1863
|
+
else:
|
1864
|
+
formatter = get_formatter(node.type)
|
1865
|
+
if node.type.startswith("op "):
|
1866
|
+
# Not too elegant... op nodes should have a property default_speed, default_power
|
1867
|
+
if not self.context.elements.op_show_default:
|
1868
|
+
if hasattr(node, "speed"):
|
1869
|
+
node.speed = node.speed
|
1870
|
+
if hasattr(node, "power"):
|
1871
|
+
node.power = node.power
|
1872
|
+
if hasattr(node, "dwell_time"):
|
1873
|
+
node.dwell_time = node.dwell_time
|
1874
|
+
checker = f"dangerlevel_{node.type.replace(' ', '_')}"
|
1875
|
+
if hasattr(self.context.device, checker):
|
1876
|
+
maxspeed_minpower = getattr(self.context.device, checker)
|
1877
|
+
if (
|
1878
|
+
isinstance(maxspeed_minpower, (tuple, list))
|
1879
|
+
and len(maxspeed_minpower) == 8
|
1880
|
+
):
|
1881
|
+
# minpower, maxposer, minspeed, maxspeed
|
1882
|
+
# print ("Yes: ", checker, maxspeed_minpower)
|
1883
|
+
danger = False
|
1884
|
+
if hasattr(node, "power"):
|
1885
|
+
value = float(node.power)
|
1886
|
+
if maxspeed_minpower[0] and value < maxspeed_minpower[1]:
|
1887
|
+
danger = True
|
1888
|
+
if maxspeed_minpower[2] and value > maxspeed_minpower[3]:
|
1889
|
+
danger = True
|
1890
|
+
if hasattr(node, "speed"):
|
1891
|
+
value = float(node.speed)
|
1892
|
+
if maxspeed_minpower[4] and value < maxspeed_minpower[5]:
|
1893
|
+
danger = True
|
1894
|
+
if maxspeed_minpower[6] and value > maxspeed_minpower[7]:
|
1895
|
+
danger = True
|
1896
|
+
if hasattr(node, "dangerous"):
|
1897
|
+
node.dangerous = danger
|
1898
|
+
else:
|
1899
|
+
setattr(self.context.device, checker, [False, 0] * 4)
|
1900
|
+
print(
|
1901
|
+
f"That's strange {checker}: {type(maxspeed_minpower).__name__}"
|
1902
|
+
)
|
1903
|
+
# label = node.create_label(formatter)
|
1904
|
+
label = my_create_label(node, formatter)
|
1905
|
+
|
1906
|
+
self.wxtree.SetItemText(node._item, label)
|
1907
|
+
attribute_to_try = "fill" if node.type == "elem text" else "stroke"
|
1908
|
+
wxcolor = None
|
1909
|
+
if hasattr(node, attribute_to_try):
|
1910
|
+
wxcolor = self.safe_color(getattr(node, attribute_to_try))
|
1911
|
+
elif hasattr(node, "color"):
|
1912
|
+
wxcolor = self.safe_color(node.color)
|
1913
|
+
else:
|
1914
|
+
back_color = self.wxtree.GetBackgroundColour()
|
1915
|
+
rgb = back_color.Get()
|
1916
|
+
background = Color(rgb[0], rgb[1], rgb[2])
|
1917
|
+
if background is not None:
|
1918
|
+
c1 = Color("Black")
|
1919
|
+
c2 = Color("White")
|
1920
|
+
if Color.distance(background, c1) > Color.distance(background, c2):
|
1921
|
+
textcolor = c1
|
1922
|
+
else:
|
1923
|
+
textcolor = c2
|
1924
|
+
wxcolor = wx.Colour(swizzlecolor(textcolor))
|
1925
|
+
if self.context.root.tree_colored:
|
1926
|
+
try:
|
1927
|
+
self.wxtree.SetItemTextColour(node._item, wxcolor)
|
1928
|
+
except (AttributeError, KeyError, TypeError):
|
1929
|
+
pass
|
1930
|
+
|
1931
|
+
state_num = -1
|
1932
|
+
if node is self.elements.get(type="branch ops"):
|
1933
|
+
unassigned, unburnt = self.elements.have_unburnable_elements()
|
1934
|
+
if unassigned or unburnt:
|
1935
|
+
state_num = self.iconstates["warning"]
|
1936
|
+
else:
|
1937
|
+
# Has the node a lock attribute?
|
1938
|
+
lockit = node.lock if hasattr(node, "lock") else False
|
1939
|
+
if lockit:
|
1940
|
+
state_num = self.iconstates["lock"]
|
1941
|
+
scene = getattr(self.context.root, "mainscene", None)
|
1942
|
+
if scene is not None and node == scene.pane.reference_object:
|
1943
|
+
state_num = self.iconstates["refobject"]
|
1944
|
+
if state_num < 0:
|
1945
|
+
state_num = wx.TREE_ITEMSTATE_NONE
|
1946
|
+
if (
|
1947
|
+
node.type in op_nodes
|
1948
|
+
and hasattr(node, "is_visible")
|
1949
|
+
and not node.is_visible
|
1950
|
+
) or (
|
1951
|
+
node.type in elem_nodes and hasattr(node, "hidden") and node.hidden
|
1952
|
+
) or (
|
1953
|
+
hasattr(node, "node") and hasattr(node.node, "hidden") and node.node.hidden
|
1954
|
+
):
|
1955
|
+
state_num = self.iconstates["ghost"]
|
1956
|
+
self.wxtree.SetItemState(node._item, state_num)
|
1957
|
+
|
1958
|
+
def on_drag_begin_handler(self, event):
|
1959
|
+
"""
|
1960
|
+
Drag handler begin for the tree.
|
1961
|
+
|
1962
|
+
@param event:
|
1963
|
+
@return:
|
1964
|
+
"""
|
1965
|
+
|
1966
|
+
def typefamily(typename):
|
1967
|
+
# Combine similar nodetypes
|
1968
|
+
if typename.startswith("op "):
|
1969
|
+
result = "op"
|
1970
|
+
elif typename.startswith("elem "):
|
1971
|
+
result = "elem"
|
1972
|
+
elif typename.startswith("group"):
|
1973
|
+
result = "elem"
|
1974
|
+
elif typename.startswith("file"):
|
1975
|
+
result = "elem"
|
1976
|
+
else:
|
1977
|
+
result = typename
|
1978
|
+
return result
|
1979
|
+
|
1980
|
+
self.dragging_nodes = None
|
1981
|
+
|
1982
|
+
pt = event.GetPoint()
|
1983
|
+
drag_item, _ = self.wxtree.HitTest(pt)
|
1984
|
+
|
1985
|
+
if drag_item is None or drag_item.ID is None or not drag_item.IsOk():
|
1986
|
+
# errmsg = ""
|
1987
|
+
# if drag_item is None:
|
1988
|
+
# errmsg = "item was none"
|
1989
|
+
# elif drag_item.ID is None:
|
1990
|
+
# errmsg = "id was none"
|
1991
|
+
# elif not drag_item.IsOk():
|
1992
|
+
# errmsg = "IsOk was false"
|
1993
|
+
# print (f"Drag item was wrong: {errmsg}")
|
1994
|
+
event.Skip()
|
1995
|
+
return
|
1996
|
+
|
1997
|
+
self.dragging_nodes = []
|
1998
|
+
for item in self.wxtree.GetSelections():
|
1999
|
+
node = self.wxtree.GetItemData(item)
|
2000
|
+
if node is not None and node.is_draggable():
|
2001
|
+
self.dragging_nodes.append(node)
|
2002
|
+
|
2003
|
+
if not self.dragging_nodes:
|
2004
|
+
# print ("Dragging_nodes was empty")
|
2005
|
+
event.Skip()
|
2006
|
+
return
|
2007
|
+
|
2008
|
+
t = typefamily(self.dragging_nodes[0].type)
|
2009
|
+
for n in self.dragging_nodes:
|
2010
|
+
tt = typefamily(n.type)
|
2011
|
+
if t != tt:
|
2012
|
+
# Different typefamilies
|
2013
|
+
# print ("Different typefamilies")
|
2014
|
+
event.Skip()
|
2015
|
+
return
|
2016
|
+
if not n.is_draggable():
|
2017
|
+
# print ("Element was not draggable")
|
2018
|
+
event.Skip()
|
2019
|
+
return
|
2020
|
+
event.Allow()
|
2021
|
+
|
2022
|
+
def on_drag_end_handler(self, event):
|
2023
|
+
"""
|
2024
|
+
Drag end handler for the tree
|
2025
|
+
|
2026
|
+
@param event:
|
2027
|
+
@return:
|
2028
|
+
"""
|
2029
|
+
if self.dragging_nodes is None:
|
2030
|
+
event.Skip()
|
2031
|
+
return
|
2032
|
+
self.wxtree.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
|
2033
|
+
drop_item = event.GetItem()
|
2034
|
+
if drop_item is None or drop_item.ID is None:
|
2035
|
+
event.Skip()
|
2036
|
+
return
|
2037
|
+
drop_node = self.wxtree.GetItemData(drop_item)
|
2038
|
+
if drop_node is None:
|
2039
|
+
event.Skip()
|
2040
|
+
return
|
2041
|
+
# Is the node expanded? If yes regular dnd applies, if not we will add the node to the end...
|
2042
|
+
closed_leaf = (self.wxtree.ItemHasChildren(drop_item) and not self.wxtree.IsExpanded(drop_item))
|
2043
|
+
# We extend the logic by calling the appropriate elems routine
|
2044
|
+
skip = not self.elements.drag_and_drop(self.dragging_nodes, drop_node, flag=closed_leaf)
|
2045
|
+
if skip:
|
2046
|
+
event.Skip()
|
2047
|
+
self.dragging_nodes = None
|
2048
|
+
return
|
2049
|
+
event.Allow()
|
2050
|
+
# Make sure that the drop node is visible
|
2051
|
+
self.wxtree.Expand(drop_item)
|
2052
|
+
self.wxtree.EnsureVisible(drop_item)
|
2053
|
+
self.refresh_tree(source="drag end")
|
2054
|
+
# Do the dragging_nodes contain an operation?
|
2055
|
+
# Let's give an indication of that, as this may
|
2056
|
+
# have led to the creation of a new reference
|
2057
|
+
# node. For whatever reason this is not recognised
|
2058
|
+
# otherwise...
|
2059
|
+
if not self.dragging_nodes:
|
2060
|
+
# Dragging nodes were cleared (we must have rebuilt the entire tree)
|
2061
|
+
return
|
2062
|
+
for node in self.dragging_nodes:
|
2063
|
+
if node.type.startswith("op"):
|
2064
|
+
self.context.signal("tree_changed")
|
2065
|
+
break
|
2066
|
+
# self.rebuild_tree()
|
2067
|
+
self.reset_dragging()
|
2068
|
+
|
2069
|
+
def on_mouse_over(self, event):
|
2070
|
+
# establish the item we are over...
|
2071
|
+
event.Skip()
|
2072
|
+
ttip = ""
|
2073
|
+
pt = event.GetPosition()
|
2074
|
+
item, flags = self.wxtree.HitTest(pt)
|
2075
|
+
if self._last_hover_item is item:
|
2076
|
+
return
|
2077
|
+
if item:
|
2078
|
+
state = self.wxtree.GetItemState(item)
|
2079
|
+
node = self.wxtree.GetItemData(item)
|
2080
|
+
if node is not None:
|
2081
|
+
# Lets check the dragging status
|
2082
|
+
if self.dragging_nodes:
|
2083
|
+
if hasattr(node, "would_accept_drop"):
|
2084
|
+
would_drop = node.would_accept_drop(self.dragging_nodes)
|
2085
|
+
else:
|
2086
|
+
would_drop = False
|
2087
|
+
if would_drop:
|
2088
|
+
self.wxtree.SetCursor(wx.Cursor(wx.CURSOR_HAND))
|
2089
|
+
else:
|
2090
|
+
self.wxtree.SetCursor(wx.Cursor(wx.CURSOR_NO_ENTRY))
|
2091
|
+
else:
|
2092
|
+
self.wxtree.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
|
2093
|
+
|
2094
|
+
if hasattr(node, "_tooltip"):
|
2095
|
+
# That has precedence and will be displayed in all cases
|
2096
|
+
ttip = node._tooltip
|
2097
|
+
elif not self.context.disable_tree_tool_tips:
|
2098
|
+
if node.type == "blob":
|
2099
|
+
ttip = _(
|
2100
|
+
"This is binary data imported or generated\n"
|
2101
|
+
+ "that will be sent directly to the laser.\n"
|
2102
|
+
+ "Double-click to view."
|
2103
|
+
)
|
2104
|
+
elif node.type == "op cut":
|
2105
|
+
ttip = _(
|
2106
|
+
"This will engrave/cut the elements contained,\n"
|
2107
|
+
+ "following the vector-paths of the data.\n"
|
2108
|
+
+ "(Usually done last)"
|
2109
|
+
)
|
2110
|
+
elif node.type == "op engrave":
|
2111
|
+
ttip = _(
|
2112
|
+
"This will engrave the elements contained,\n"
|
2113
|
+
+ "following the vector-paths of the data."
|
2114
|
+
)
|
2115
|
+
elif node.type == "op image":
|
2116
|
+
ttip = _(
|
2117
|
+
"This engraves already created images pixel by pixel,\n"
|
2118
|
+
+ "applying the settings to the individual pictures"
|
2119
|
+
)
|
2120
|
+
elif node.type == "op raster":
|
2121
|
+
ttip = _(
|
2122
|
+
"This will render all contained elements\n"
|
2123
|
+
+ "into an intermediary image which then will be\n"
|
2124
|
+
+ "engraved pixel by pixel."
|
2125
|
+
)
|
2126
|
+
elif node.type == "op dots":
|
2127
|
+
ttip = _(
|
2128
|
+
"This will engrave a single point for a given period of time"
|
2129
|
+
)
|
2130
|
+
elif node.type == "util console":
|
2131
|
+
ttip = _(
|
2132
|
+
"This allows to execute an arbitrary command during the engrave process"
|
2133
|
+
)
|
2134
|
+
elif node.type == "util goto":
|
2135
|
+
ttip = _(
|
2136
|
+
"This will send the laser back to its logical start position"
|
2137
|
+
)
|
2138
|
+
elif node.type == "util home":
|
2139
|
+
ttip = _(
|
2140
|
+
"This will send the laser back to its physical start position"
|
2141
|
+
)
|
2142
|
+
elif node.type == "util input":
|
2143
|
+
ttip = _(
|
2144
|
+
"This will wait for active IO bits on the laser (mainly fibre laser for now)"
|
2145
|
+
)
|
2146
|
+
elif node.type == "util output":
|
2147
|
+
ttip = _(
|
2148
|
+
"This will set some IO bits on the laser (mainly fibre laser for now)"
|
2149
|
+
)
|
2150
|
+
elif node.type == "util wait":
|
2151
|
+
ttip = _(
|
2152
|
+
"This will pause the engrave process for a given period"
|
2153
|
+
)
|
2154
|
+
elif node.type == "branch reg":
|
2155
|
+
ttip = _(
|
2156
|
+
"The elements under this section will not be engraved,\n"
|
2157
|
+
+ "they can serve as a template or registration marks."
|
2158
|
+
)
|
2159
|
+
elif node.type == "elem line":
|
2160
|
+
bb = node.bounds
|
2161
|
+
if bb is not None:
|
2162
|
+
ww = Length(amount=bb[2] - bb[0], digits=1)
|
2163
|
+
hh = Length(amount=bb[3] - bb[1], digits=1)
|
2164
|
+
ll = Length(amount=node.length(), digits=1)
|
2165
|
+
ttip = f"{ww.length_mm} x {hh.length_mm}, L={ll.length_mm}"
|
2166
|
+
elif node.type == "elem rect":
|
2167
|
+
bb = node.bounds
|
2168
|
+
if bb is not None:
|
2169
|
+
ww = Length(amount=bb[2] - bb[0], digits=1)
|
2170
|
+
hh = Length(amount=bb[3] - bb[1], digits=1)
|
2171
|
+
ll = Length(amount=node.length(), digits=1)
|
2172
|
+
ttip = f"{ww.length_mm} x {hh.length_mm}, L={ll.length_mm}"
|
2173
|
+
elif node.type == "elem polyline":
|
2174
|
+
bb = node.bounds
|
2175
|
+
if bb is not None:
|
2176
|
+
ww = Length(amount=bb[2] - bb[0], digits=1)
|
2177
|
+
hh = Length(amount=bb[3] - bb[1], digits=1)
|
2178
|
+
ll = Length(amount=node.length(), digits=1)
|
2179
|
+
ttip = f"{ww.length_mm} x {hh.length_mm}, L={ll.length_mm}"
|
2180
|
+
ttip += f"\n{len(node)} pts"
|
2181
|
+
elif node.type == "elem ellipse":
|
2182
|
+
bb = node.bounds
|
2183
|
+
if bb is not None:
|
2184
|
+
ww = Length(amount=bb[2] - bb[0], digits=1)
|
2185
|
+
hh = Length(amount=bb[3] - bb[1], digits=1)
|
2186
|
+
ttip = f"{ww.length_mm} x {hh.length_mm}"
|
2187
|
+
elif node.type == "elem path":
|
2188
|
+
bb = node.bounds
|
2189
|
+
if bb is not None:
|
2190
|
+
ww = Length(amount=bb[2] - bb[0], digits=1)
|
2191
|
+
hh = Length(amount=bb[3] - bb[1], digits=1)
|
2192
|
+
ttip = f"{ww.length_mm} x {hh.length_mm}"
|
2193
|
+
ttip += f"\n{len(node.path)} segments"
|
2194
|
+
elif node.type == "elem text":
|
2195
|
+
bb = node.bounds
|
2196
|
+
if bb is not None:
|
2197
|
+
ww = Length(amount=bb[2] - bb[0], digits=1)
|
2198
|
+
hh = Length(amount=bb[3] - bb[1], digits=1)
|
2199
|
+
ttip = f"{ww.length_mm} x {hh.length_mm}"
|
2200
|
+
# ttip += f"\n{node.font}"
|
2201
|
+
elif node.type == "place current":
|
2202
|
+
ttip = _(
|
2203
|
+
"This is a placeholder for the 'place current' operation"
|
2204
|
+
)
|
2205
|
+
elif node.type == "place point":
|
2206
|
+
ttip = _(
|
2207
|
+
"This will define an origin from where all the elements in this scene\n"
|
2208
|
+
+ "will be plotted. You can have multiple such job start points"
|
2209
|
+
)
|
2210
|
+
elif node.type == "effect hatch":
|
2211
|
+
ttip = _(
|
2212
|
+
"This is a special node that will consume any other closed path\n"
|
2213
|
+
+ "you drag onto it and will fill the shape with a line pattern.\n"
|
2214
|
+
+ "To activate / deactivate this effect please use the context menu."
|
2215
|
+
)
|
2216
|
+
if node.type in op_nodes:
|
2217
|
+
if hasattr(node, "label") and node.label is not None:
|
2218
|
+
ttip += f"\n{node.id + ': ' if node.id is not None else ''}{node.display_label()}"
|
2219
|
+
ps_info = ""
|
2220
|
+
if hasattr(node, "power") and node.power is not None:
|
2221
|
+
try:
|
2222
|
+
p = float(node.power)
|
2223
|
+
if self.context.device.use_percent_for_power_display:
|
2224
|
+
ps_info += f"{', ' if ps_info else ''}{p / 10:.1f}%"
|
2225
|
+
else:
|
2226
|
+
ps_info += f"{', ' if ps_info else ''}{p:.0f}ppi"
|
2227
|
+
except ValueError:
|
2228
|
+
pass
|
2229
|
+
|
2230
|
+
if hasattr(node, "speed") and node.speed is not None:
|
2231
|
+
try:
|
2232
|
+
p = float(node.speed)
|
2233
|
+
if self.context.device.use_mm_min_for_speed_display:
|
2234
|
+
ps_info += (
|
2235
|
+
f"{', ' if ps_info else ''}{p * 60.0:.0f}mm/min"
|
2236
|
+
)
|
2237
|
+
else:
|
2238
|
+
ps_info += f"{', ' if ps_info else ''}{p:.0f}mm/s"
|
2239
|
+
except ValueError:
|
2240
|
+
pass
|
2241
|
+
|
2242
|
+
if (
|
2243
|
+
hasattr(self.context.device, "default_frequency")
|
2244
|
+
and hasattr(node, "frequency")
|
2245
|
+
and node.frequency is not None
|
2246
|
+
):
|
2247
|
+
try:
|
2248
|
+
p = float(node.frequency)
|
2249
|
+
ps_info += f"{', ' if ps_info else ''}{p:.0f}kHz"
|
2250
|
+
except ValueError:
|
2251
|
+
pass
|
2252
|
+
|
2253
|
+
if ps_info:
|
2254
|
+
ttip += f"\n{ps_info}"
|
2255
|
+
if state == self.iconstates["ghost"]:
|
2256
|
+
ttip = _("HIDDEN: ") + ttip
|
2257
|
+
self._last_hover_item = item
|
2258
|
+
if ttip != self.wxtree.GetToolTipText():
|
2259
|
+
self.wxtree.SetToolTip(ttip)
|
2260
|
+
|
2261
|
+
def on_item_right_click(self, event):
|
2262
|
+
"""
|
2263
|
+
Right click of element in tree.
|
2264
|
+
|
2265
|
+
@param event:
|
2266
|
+
@return:
|
2267
|
+
"""
|
2268
|
+
item = event.GetItem()
|
2269
|
+
if item is None:
|
2270
|
+
return
|
2271
|
+
node = self.wxtree.GetItemData(item)
|
2272
|
+
|
2273
|
+
create_menu(self.gui, node, self.elements)
|
2274
|
+
|
2275
|
+
def on_item_activated(self, event):
|
2276
|
+
"""
|
2277
|
+
Tree item is double-clicked. Launches PropertyWindow associated with that object.
|
2278
|
+
|
2279
|
+
@param event:
|
2280
|
+
@return:
|
2281
|
+
"""
|
2282
|
+
item = event.GetItem()
|
2283
|
+
node = self.wxtree.GetItemData(item)
|
2284
|
+
activate = self.elements.lookup("function/open_property_window_for_node")
|
2285
|
+
if activate is not None:
|
2286
|
+
activate(node)
|
2287
|
+
|
2288
|
+
def activate_selected_node(self, *args):
|
2289
|
+
"""
|
2290
|
+
Call activated on the first emphasized node.
|
2291
|
+
|
2292
|
+
@param args:
|
2293
|
+
@return:
|
2294
|
+
"""
|
2295
|
+
first_element = self.elements.first_element(emphasized=True)
|
2296
|
+
if first_element is None:
|
2297
|
+
first_element = self.elements.first_element(selected=True)
|
2298
|
+
if first_element is None:
|
2299
|
+
return
|
2300
|
+
if hasattr(first_element, "node"):
|
2301
|
+
# Reference
|
2302
|
+
first_element = first_element.node
|
2303
|
+
activate = self.elements.lookup("function/open_property_window_for_node")
|
2304
|
+
if activate is not None:
|
2305
|
+
activate(first_element)
|
2306
|
+
|
2307
|
+
def on_collapse(self, event):
|
2308
|
+
if self.do_not_select:
|
2309
|
+
# Do not select is part of a linux correction where moving nodes around in a drag and drop fashion could
|
2310
|
+
# cause them to appear to drop invalid nodes.
|
2311
|
+
return
|
2312
|
+
item = event.GetItem()
|
2313
|
+
if not item:
|
2314
|
+
return
|
2315
|
+
node = self.wxtree.GetItemData(item)
|
2316
|
+
node.expanded = False
|
2317
|
+
|
2318
|
+
def on_expand(self, event):
|
2319
|
+
if self.do_not_select:
|
2320
|
+
# Do not select is part of a linux correction where moving nodes around in a drag and drop fashion could
|
2321
|
+
# cause them to appear to drop invalid nodes.
|
2322
|
+
return
|
2323
|
+
item = event.GetItem()
|
2324
|
+
if not item:
|
2325
|
+
return
|
2326
|
+
node = self.wxtree.GetItemData(item)
|
2327
|
+
node.expanded = True
|
2328
|
+
|
2329
|
+
def on_state_icon(self, event):
|
2330
|
+
if self.do_not_select:
|
2331
|
+
# Do not select is part of a linux correction where moving nodes around in a drag and drop fashion could
|
2332
|
+
# cause them to appear to drop invalid nodes.
|
2333
|
+
return
|
2334
|
+
item = event.GetItem()
|
2335
|
+
if not item:
|
2336
|
+
return
|
2337
|
+
node = self.wxtree.GetItemData(item)
|
2338
|
+
if hasattr(node, "hidden"):
|
2339
|
+
node.hidden = False
|
2340
|
+
self.context.elements.set_emphasis([node])
|
2341
|
+
# self.context.signal("refresh_scene", "Scene")
|
2342
|
+
self.update_decorations(node)
|
2343
|
+
|
2344
|
+
def on_item_selection_changed(self, event):
|
2345
|
+
"""
|
2346
|
+
Tree menu item is changed. Modify the selection.
|
2347
|
+
|
2348
|
+
@param event:
|
2349
|
+
@return:
|
2350
|
+
"""
|
2351
|
+
if self.do_not_select:
|
2352
|
+
# Do not select is part of a linux correction where moving nodes around in a drag and drop fashion could
|
2353
|
+
# cause them to appear to drop invalid nodes.
|
2354
|
+
return
|
2355
|
+
# print (f"tree claims: {self.wxtree.FindFocus().GetId()}, parent claims: {self.wxtree.GetParent().FindFocus().GetId()}, toplevel claims: {self.wxtree.GetTopLevelParent().FindFocus().GetId()}, tree-id={self.wxtree.GetId()}")
|
2356
|
+
its_me = self.wxtree.FindFocus() is self.wxtree
|
2357
|
+
# Just out of curiosity, is there no image set? Then just do it again.
|
2358
|
+
item = event.GetItem()
|
2359
|
+
if item:
|
2360
|
+
image_id = self.wxtree.GetItemImage(item)
|
2361
|
+
if image_id >= self.tree_images.ImageCount:
|
2362
|
+
image_id = -1
|
2363
|
+
if image_id < 0:
|
2364
|
+
node = self.wxtree.GetItemData(item)
|
2365
|
+
if node is not None:
|
2366
|
+
self.set_icon(node, force=True)
|
2367
|
+
|
2368
|
+
selected = [
|
2369
|
+
self.wxtree.GetItemData(item) for item in self.wxtree.GetSelections()
|
2370
|
+
]
|
2371
|
+
|
2372
|
+
emphasized = list(selected)
|
2373
|
+
for i in range(len(emphasized)):
|
2374
|
+
node = emphasized[i]
|
2375
|
+
if node is None or node.type is None:
|
2376
|
+
# Rare issue seen building the tree during materials test and the node type didn't exist.
|
2377
|
+
return
|
2378
|
+
if node.type == "reference":
|
2379
|
+
emphasized[i] = node.node
|
2380
|
+
elif node.type.startswith("op"):
|
2381
|
+
for n in node.flat(types=("reference",), cascade=False):
|
2382
|
+
try:
|
2383
|
+
emphasized.append(n.node)
|
2384
|
+
except Exception:
|
2385
|
+
pass
|
2386
|
+
self.elements.set_emphasis(emphasized)
|
2387
|
+
self.elements.set_selected(selected)
|
2388
|
+
# self.refresh_tree(source="on_item_selection")
|
2389
|
+
event.Allow()
|
2390
|
+
|
2391
|
+
# We seem to lose focus, so lets reclaim it
|
2392
|
+
if its_me:
|
2393
|
+
def restore_focus():
|
2394
|
+
self.wxtree.SetFocus()
|
2395
|
+
wx.CallAfter(restore_focus)
|
2396
|
+
|
2397
|
+
def select_in_tree_by_emphasis(self, origin, *args):
|
2398
|
+
"""
|
2399
|
+
Selected the actual `wx.tree` control those items which are currently emphasized.
|
2400
|
+
|
2401
|
+
@return:
|
2402
|
+
"""
|
2403
|
+
self.do_not_select = True
|
2404
|
+
self.wxtree.UnselectAll()
|
2405
|
+
require_rebuild = False
|
2406
|
+
for e in self.elements.elems_nodes(emphasized=True):
|
2407
|
+
if e._item:
|
2408
|
+
self.wxtree.SelectItem(e._item, True)
|
2409
|
+
else:
|
2410
|
+
# That should not happen, apparently we have a not fully built tree
|
2411
|
+
require_rebuild = True
|
2412
|
+
break
|
2413
|
+
if require_rebuild:
|
2414
|
+
self.context.signal("rebuild_tree", "all")
|
2415
|
+
|
2416
|
+
self.do_not_select = False
|