meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7010__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 +1195 -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 +1844 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4561 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4329 -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 +933 -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/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 +462 -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 +198 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +835 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +630 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +284 -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 +1081 -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 +170 -133
- meerk40t/gui/choicepropertypanel.py +1673 -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 +1430 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +892 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2917 -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 +494 -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 +2468 -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 +589 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2952 -2756
- meerk40t/gui/simpleui.py +357 -333
- meerk40t/gui/simulation.py +2431 -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 +591 -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 +160 -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 +1444 -1169
- meerk40t/gui/wxmmain.py +5578 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1635 -1453
- meerk40t/gui/wxmtree.py +2410 -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 +2778 -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 +3809 -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 +102 -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 +390 -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 +672 -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 +940 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +989 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/METADATA +150 -139
- meerk40t-0.9.7010.dist-info/RECORD +445 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.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.7010.dist-info}/entry_points.txt +0 -0
@@ -1,1469 +1,1673 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
from meerk40t.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
"
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
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
|
-
# Do we have a parameter to
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
if
|
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
|
-
|
364
|
-
|
365
|
-
|
366
|
-
)
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
if
|
374
|
-
expansion_flag = 0
|
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
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
control
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
control
|
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
|
-
control
|
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
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
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
|
-
|
678
|
-
|
679
|
-
|
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
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
),
|
722
|
-
)
|
723
|
-
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
724
|
-
elif data_type in (
|
725
|
-
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
726
|
-
|
727
|
-
choice_list = list(map(str, c.get("choices", [c.get("default")])))
|
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
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
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
|
-
self,
|
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
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
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
|
-
if
|
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
|
-
|
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
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
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
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
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
|
-
current_sizer.Add(
|
1219
|
-
elif data_type
|
1220
|
-
#
|
1221
|
-
if label != "":
|
1222
|
-
control_sizer = StaticBoxSizer(
|
1223
|
-
self, wx.ID_ANY, label, wx.HORIZONTAL
|
1224
|
-
)
|
1225
|
-
else:
|
1226
|
-
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
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
|
-
if
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
ctrl
|
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
|
-
|
1
|
+
from copy import copy
|
2
|
+
|
3
|
+
import wx
|
4
|
+
|
5
|
+
from meerk40t.core.units import Angle, Length
|
6
|
+
from meerk40t.gui.laserrender import swizzlecolor
|
7
|
+
from meerk40t.gui.wxutils import (
|
8
|
+
EditableListCtrl,
|
9
|
+
ScrolledPanel,
|
10
|
+
StaticBoxSizer,
|
11
|
+
TextCtrl,
|
12
|
+
dip_size,
|
13
|
+
wxButton,
|
14
|
+
wxCheckBox,
|
15
|
+
wxComboBox,
|
16
|
+
wxRadioBox,
|
17
|
+
wxStaticBitmap,
|
18
|
+
wxStaticText,
|
19
|
+
)
|
20
|
+
from meerk40t.kernel import Context
|
21
|
+
from meerk40t.svgelements import Color
|
22
|
+
|
23
|
+
_ = wx.GetTranslation
|
24
|
+
|
25
|
+
|
26
|
+
class ChoicePropertyPanel(ScrolledPanel):
|
27
|
+
"""
|
28
|
+
ChoicePropertyPanel is a generic panel that presents a list of properties to be viewed and edited.
|
29
|
+
In most cases it can be initialized by passing a choices value which will read the registered choice values
|
30
|
+
and display the given properties, automatically generating an appropriate changers for that property.
|
31
|
+
|
32
|
+
In most cases the ChoicePropertyPanel should be used for properties of a dynamic nature. A lot of different
|
33
|
+
relationships can be established and the class should be kept fairly easy to extend. With a set dictionary
|
34
|
+
either registered in the Kernel as a choice or called directly on the ChoicePropertyPanel you can make dynamic
|
35
|
+
controls to set various properties. This avoids needing to create a new static window when a panel is just
|
36
|
+
providing options to change settings.
|
37
|
+
The choices need to be provided either as list of dictionaries or indirectly via
|
38
|
+
a string indicating a stored list registered in the given context under "choices"
|
39
|
+
The dictionary recognizes the following entries:
|
40
|
+
|
41
|
+
"object": The object to which the property defined in attr belongs to
|
42
|
+
"attr": The name of the attribute
|
43
|
+
"default": The default value if no value has been given before
|
44
|
+
"label": The label will be used for labelling the to be created UI-elements
|
45
|
+
"trailer": this text will be displayed immediately after the element
|
46
|
+
"tip": The tooltip that will be used for this element
|
47
|
+
"dynamic": a function called with the current dictionary choice. This is to update
|
48
|
+
values that may have changed since the choice was first established.
|
49
|
+
"type": This can be one of (no quotation marks, real python data types):
|
50
|
+
bool: will always be represented by a checkbox
|
51
|
+
str: normally be represented by a textbox (may be influenced by style)
|
52
|
+
int: normally be represented by a textbox (may be influenced by style)
|
53
|
+
float: normally be represented by a textbox (may be influenced by style)
|
54
|
+
Length: represented by a textbox
|
55
|
+
Angle: represented by a textbox
|
56
|
+
Color: represented by a color picker
|
57
|
+
"style": If given then the standard representation for a data-type (see above)
|
58
|
+
will be replaced by more tailored UI-elements:
|
59
|
+
"file": (only available for str) a file selection dialog is used,
|
60
|
+
this recognizes a further property "wildcard"
|
61
|
+
"slider:" Creates a slider (for int and float) that will use two additional
|
62
|
+
entries, "min" and "max.
|
63
|
+
"combo": see combosmall (but larger).
|
64
|
+
"option": Creates a combo box but also takes "display" as a parameter
|
65
|
+
that displays these strings rather than the underlying choices.
|
66
|
+
"combosmall": Available for str, int, float will fill the combo
|
67
|
+
with values defined in "choices" (additional parameter)
|
68
|
+
"binary": uses two additional settings "mask" and "bit" to
|
69
|
+
allow the bitwise manipulation of an int data type
|
70
|
+
"multiline": (only available for str) the content allows multiline input
|
71
|
+
"weight": only valid in subsections, default value 1, i.e. equal width
|
72
|
+
allocation, can be changed to force a different sizing behaviour
|
73
|
+
UI-Appearance
|
74
|
+
"page":
|
75
|
+
"section":
|
76
|
+
"subsection":
|
77
|
+
"priority":
|
78
|
+
These entries will create visible separation/joining of elements.
|
79
|
+
The dictionary list will be sorted first by priority, then page,
|
80
|
+
then section, then subsection. While normally every item ends up
|
81
|
+
on a new line, elements within a subsection remain in one horizontal
|
82
|
+
container.
|
83
|
+
Notabene:
|
84
|
+
a) to influence ordering without compromising the intended Page,
|
85
|
+
Section etc. names, the routine will remove a leading "_xxxx_" string
|
86
|
+
b) The Page, Section etc. names will be translated, so please provide
|
87
|
+
them in plain English
|
88
|
+
|
89
|
+
There are some special hacks to influence appearance / internal logic
|
90
|
+
"hidden": if set then this expert property will only appear if the
|
91
|
+
developer-mode has been set
|
92
|
+
"enabled": Is the control enabled (default yes, so does not need to be
|
93
|
+
provided)
|
94
|
+
"conditional": if given as tuple (cond_obj, cond_prop) then the (boolean)
|
95
|
+
value of the property cond_obj.cond_prop will decide if the element
|
96
|
+
will be enabled or not. (If a third value then the value must equal that value).
|
97
|
+
"signals": This for advanced treatment, normally any change to a property
|
98
|
+
will be announced to the wider mk-universe by sending a signal with the
|
99
|
+
attributes name as signal-indicator (this is used to inform other UI-
|
100
|
+
elements with the same content of such a change). If you want to invoke
|
101
|
+
additional logic (or don't want to write a specific signal-listen routine
|
102
|
+
to forward it to other routines) then you can add a single signal-name
|
103
|
+
or a list of signal-names to be called
|
104
|
+
"""
|
105
|
+
|
106
|
+
def __init__(
|
107
|
+
self,
|
108
|
+
*args,
|
109
|
+
context: Context = None,
|
110
|
+
choices=None,
|
111
|
+
scrolling=True,
|
112
|
+
constraint=None,
|
113
|
+
entries_per_column=None,
|
114
|
+
injector=None,
|
115
|
+
**kwds,
|
116
|
+
):
|
117
|
+
# constraints are either
|
118
|
+
# - None (default) - all choices will be display
|
119
|
+
# - a pair of integers (start, end), from where to where (index) to display
|
120
|
+
# use case: display the first 10 entries constraint=(0, 9)
|
121
|
+
# then the remaining: constraint=(10, -1)
|
122
|
+
# -1 defines the min / max boundaries
|
123
|
+
# - a list of strings that describe
|
124
|
+
# the pages to show : constraint=("page1", "page2")
|
125
|
+
# the pages to omit : constraint=("-page1", "-page2")
|
126
|
+
# a leading hyphen establishes omission
|
127
|
+
kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
|
128
|
+
ScrolledPanel.__init__(self, *args, **kwds)
|
129
|
+
self.context = context
|
130
|
+
self.context.themes.set_window_colors(self)
|
131
|
+
self.listeners = list()
|
132
|
+
self.entries_per_column = entries_per_column
|
133
|
+
if choices is None:
|
134
|
+
return
|
135
|
+
if isinstance(choices, str):
|
136
|
+
choices = [choices]
|
137
|
+
|
138
|
+
new_choices = []
|
139
|
+
# we need to create an independent copy of the lookup, otherwise
|
140
|
+
# any amendments to choices like injector will affect the original
|
141
|
+
standardhelp = ""
|
142
|
+
for choice in choices:
|
143
|
+
if isinstance(choice, dict):
|
144
|
+
if "help" not in choice:
|
145
|
+
choice["help"] = standardhelp
|
146
|
+
new_choices.append(choice)
|
147
|
+
elif isinstance(choice, str):
|
148
|
+
lookup_choice = self.context.lookup("choices", choice)
|
149
|
+
if lookup_choice is None:
|
150
|
+
continue
|
151
|
+
for c in lookup_choice:
|
152
|
+
if "help" not in c:
|
153
|
+
c["help"] = choice
|
154
|
+
new_choices.extend(lookup_choice)
|
155
|
+
else:
|
156
|
+
for c in choice:
|
157
|
+
if "help" not in c:
|
158
|
+
c["help"] = standardhelp
|
159
|
+
new_choices.extend(choice)
|
160
|
+
choices = new_choices
|
161
|
+
if injector is not None:
|
162
|
+
# We have additional stuff to be added, so be it
|
163
|
+
for c in injector:
|
164
|
+
choices.append(c)
|
165
|
+
if len(choices) == 0:
|
166
|
+
# No choices to process.
|
167
|
+
return
|
168
|
+
for c in choices:
|
169
|
+
needs_dynamic_call = c.get("dynamic")
|
170
|
+
if needs_dynamic_call:
|
171
|
+
# Calls dynamic function to update this dictionary before production
|
172
|
+
needs_dynamic_call(c)
|
173
|
+
# Let's see whether we have a section and a page property...
|
174
|
+
for c in choices:
|
175
|
+
try:
|
176
|
+
dummy = c["subsection"]
|
177
|
+
except KeyError:
|
178
|
+
c["subsection"] = ""
|
179
|
+
try:
|
180
|
+
dummy = c["section"]
|
181
|
+
except KeyError:
|
182
|
+
c["section"] = ""
|
183
|
+
try:
|
184
|
+
dummy = c["page"]
|
185
|
+
except KeyError:
|
186
|
+
c["page"] = ""
|
187
|
+
try:
|
188
|
+
dummy = c["priority"]
|
189
|
+
except KeyError:
|
190
|
+
c["priority"] = "ZZZZZZZZ"
|
191
|
+
# print ("Choices: " , choices)
|
192
|
+
prechoices = sorted(
|
193
|
+
sorted(
|
194
|
+
sorted(
|
195
|
+
sorted(choices, key=lambda d: d["priority"]),
|
196
|
+
key=lambda d: d["subsection"],
|
197
|
+
),
|
198
|
+
key=lambda d: d["section"],
|
199
|
+
),
|
200
|
+
key=lambda d: d["page"],
|
201
|
+
)
|
202
|
+
self.choices = list()
|
203
|
+
dealt_with = False
|
204
|
+
if constraint is not None:
|
205
|
+
if isinstance(constraint, (tuple, list, str)):
|
206
|
+
if isinstance(constraint, str):
|
207
|
+
# make it a tuple
|
208
|
+
constraint = (constraint,)
|
209
|
+
if len(constraint) > 0:
|
210
|
+
if isinstance(constraint[0], str):
|
211
|
+
dealt_with = True
|
212
|
+
# Section list
|
213
|
+
positive = list()
|
214
|
+
negative = list()
|
215
|
+
for item in constraint:
|
216
|
+
if item.startswith("-"):
|
217
|
+
item = item[1:]
|
218
|
+
negative.append(item.lower())
|
219
|
+
else:
|
220
|
+
positive.append(item.lower())
|
221
|
+
for i, c in enumerate(prechoices):
|
222
|
+
try:
|
223
|
+
this_page = c["page"].lower()
|
224
|
+
except KeyError:
|
225
|
+
this_page = ""
|
226
|
+
if len(negative) > 0 and len(positive) > 0:
|
227
|
+
# Negative takes precedence:
|
228
|
+
if not this_page in negative and this_page in positive:
|
229
|
+
self.choices.append(c)
|
230
|
+
elif len(negative) > 0:
|
231
|
+
# only negative....
|
232
|
+
if not this_page in negative:
|
233
|
+
self.choices.append(c)
|
234
|
+
elif len(positive) > 0:
|
235
|
+
# only positive....
|
236
|
+
if this_page in positive:
|
237
|
+
self.choices.append(c)
|
238
|
+
else:
|
239
|
+
dealt_with = True
|
240
|
+
# Section list
|
241
|
+
start_from = 0
|
242
|
+
end_at = len(prechoices)
|
243
|
+
if constraint[0] >= 0:
|
244
|
+
start_from = constraint[0]
|
245
|
+
if len(constraint) > 1 and constraint[1] >= 0:
|
246
|
+
end_at = constraint[1]
|
247
|
+
if start_from < 0:
|
248
|
+
start_from = 0
|
249
|
+
if end_at > len(prechoices):
|
250
|
+
end_at = len(prechoices)
|
251
|
+
if end_at < start_from:
|
252
|
+
end_at = len(prechoices)
|
253
|
+
for i, c in enumerate(prechoices):
|
254
|
+
if start_from <= i < end_at:
|
255
|
+
self.choices.append(c)
|
256
|
+
else:
|
257
|
+
# Empty constraint
|
258
|
+
pass
|
259
|
+
if not dealt_with:
|
260
|
+
# no valid constraints
|
261
|
+
self.choices = prechoices
|
262
|
+
if len(self.choices) == 0:
|
263
|
+
return
|
264
|
+
sizer_very_main = wx.BoxSizer(wx.HORIZONTAL)
|
265
|
+
sizer_main = wx.BoxSizer(wx.VERTICAL)
|
266
|
+
sizer_very_main.Add(sizer_main, 1, wx.EXPAND, 0)
|
267
|
+
last_page = ""
|
268
|
+
last_section = ""
|
269
|
+
last_subsection = ""
|
270
|
+
last_box = None
|
271
|
+
current_main_sizer = sizer_main
|
272
|
+
current_sec_sizer = sizer_main
|
273
|
+
current_sizer = sizer_main
|
274
|
+
# By default, 0 as we are stacking up stuff
|
275
|
+
expansion_flag = 0
|
276
|
+
current_col_entry = -1
|
277
|
+
for i, c in enumerate(self.choices):
|
278
|
+
wants_listener = True
|
279
|
+
current_col_entry += 1
|
280
|
+
if self.entries_per_column is not None:
|
281
|
+
if current_col_entry >= self.entries_per_column:
|
282
|
+
current_col_entry = -1
|
283
|
+
prev_main = sizer_main
|
284
|
+
sizer_main = wx.BoxSizer(wx.VERTICAL)
|
285
|
+
if prev_main == current_main_sizer:
|
286
|
+
current_main_sizer = sizer_main
|
287
|
+
if prev_main == current_sec_sizer:
|
288
|
+
current_sec_sizer = sizer_main
|
289
|
+
if prev_main == current_sizer:
|
290
|
+
current_sizer = sizer_main
|
291
|
+
|
292
|
+
sizer_very_main.Add(sizer_main, 1, wx.EXPAND, 0)
|
293
|
+
# I think we should reset all sections to make them
|
294
|
+
# reappear in the next columns
|
295
|
+
last_page = ""
|
296
|
+
last_section = ""
|
297
|
+
last_subsection = ""
|
298
|
+
|
299
|
+
if isinstance(c, tuple):
|
300
|
+
# If c is tuple
|
301
|
+
dict_c = dict()
|
302
|
+
try:
|
303
|
+
dict_c["object"] = c[0]
|
304
|
+
dict_c["attr"] = c[1]
|
305
|
+
dict_c["default"] = c[2]
|
306
|
+
dict_c["label"] = c[3]
|
307
|
+
dict_c["tip"] = c[4]
|
308
|
+
dict_c["type"] = c[5]
|
309
|
+
except IndexError:
|
310
|
+
pass
|
311
|
+
c = dict_c
|
312
|
+
try:
|
313
|
+
attr = c["attr"]
|
314
|
+
obj = c["object"]
|
315
|
+
except KeyError:
|
316
|
+
continue
|
317
|
+
this_subsection = c.get("subsection", "")
|
318
|
+
this_section = c.get("section", "")
|
319
|
+
this_page = c.get("page", "")
|
320
|
+
ctrl_width = c.get("width", 0)
|
321
|
+
# Do we have a parameter to add a trailing label after the control
|
322
|
+
trailer = c.get("trailer")
|
323
|
+
# Is there another signal to send?
|
324
|
+
additional_signal = []
|
325
|
+
sig = c.get("signals")
|
326
|
+
if isinstance(sig, str):
|
327
|
+
additional_signal.append(sig)
|
328
|
+
elif isinstance(sig, (tuple, list)):
|
329
|
+
for _sig in sig:
|
330
|
+
additional_signal.append(_sig)
|
331
|
+
|
332
|
+
# Do we have a parameter to hide the control unless in expert mode
|
333
|
+
hidden = c.get("hidden", False)
|
334
|
+
hidden = (
|
335
|
+
bool(hidden) if hidden != "False" else False
|
336
|
+
) # bool("False") = True
|
337
|
+
# Do we have a parameter to affect the space consumption?
|
338
|
+
weight = int(c.get("weight", 1))
|
339
|
+
if weight < 0:
|
340
|
+
weight = 0
|
341
|
+
developer_mode = self.context.root.setting(bool, "developer_mode", False)
|
342
|
+
if not developer_mode and hidden:
|
343
|
+
continue
|
344
|
+
# get default value
|
345
|
+
if hasattr(obj, attr):
|
346
|
+
data = getattr(obj, attr)
|
347
|
+
else:
|
348
|
+
# if obj lacks attr, default must have been assigned.
|
349
|
+
try:
|
350
|
+
data = c["default"]
|
351
|
+
except KeyError:
|
352
|
+
# This choice is in error.
|
353
|
+
continue
|
354
|
+
data_style = c.get("style", None)
|
355
|
+
data_type = type(data)
|
356
|
+
data_type = c.get("type", data_type)
|
357
|
+
choice_list = None
|
358
|
+
label = c.get("label", attr) # Undefined label is the attr
|
359
|
+
|
360
|
+
if last_page != this_page:
|
361
|
+
expansion_flag = 0
|
362
|
+
last_section = ""
|
363
|
+
last_subsection = ""
|
364
|
+
# We could do a notebook, but let's choose a simple StaticBoxSizer instead...
|
365
|
+
last_box = StaticBoxSizer(
|
366
|
+
self, wx.ID_ANY, _(self.unsorted_label(this_page)), wx.VERTICAL
|
367
|
+
)
|
368
|
+
sizer_main.Add(last_box, 0, wx.EXPAND, 0)
|
369
|
+
current_main_sizer = last_box
|
370
|
+
current_sec_sizer = last_box
|
371
|
+
current_sizer = last_box
|
372
|
+
|
373
|
+
if last_section != this_section:
|
374
|
+
expansion_flag = 0
|
375
|
+
last_subsection = ""
|
376
|
+
if this_section != "":
|
377
|
+
last_box = StaticBoxSizer(
|
378
|
+
self,
|
379
|
+
id=wx.ID_ANY,
|
380
|
+
label=_(self.unsorted_label(this_section)),
|
381
|
+
orientation=wx.VERTICAL,
|
382
|
+
)
|
383
|
+
current_main_sizer.Add(last_box, 0, wx.EXPAND, 0)
|
384
|
+
else:
|
385
|
+
last_box = current_main_sizer
|
386
|
+
current_sizer = last_box
|
387
|
+
current_sec_sizer = last_box
|
388
|
+
|
389
|
+
if last_subsection != this_subsection:
|
390
|
+
expansion_flag = 0
|
391
|
+
if this_subsection != "":
|
392
|
+
expansion_flag = 1
|
393
|
+
lbl = _(self.unsorted_label(this_subsection))
|
394
|
+
if lbl != "":
|
395
|
+
last_box = StaticBoxSizer(
|
396
|
+
self,
|
397
|
+
id=wx.ID_ANY,
|
398
|
+
label=lbl,
|
399
|
+
orientation=wx.HORIZONTAL,
|
400
|
+
)
|
401
|
+
else:
|
402
|
+
last_box = wx.BoxSizer(wx.HORIZONTAL)
|
403
|
+
current_sec_sizer.Add(last_box, 0, wx.EXPAND, 0)
|
404
|
+
img = c.get("icon", None)
|
405
|
+
if img is not None:
|
406
|
+
icon = wxStaticBitmap(self, wx.ID_ANY, bitmap=img)
|
407
|
+
last_box.Add(icon, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
408
|
+
last_box.AddSpacer(5)
|
409
|
+
else:
|
410
|
+
last_box = current_sec_sizer
|
411
|
+
current_sizer = last_box
|
412
|
+
|
413
|
+
control = None
|
414
|
+
control_sizer = None
|
415
|
+
if data_type == str and data_style == "info":
|
416
|
+
# This is just an info box.
|
417
|
+
wants_listener = False
|
418
|
+
msgs = label.split("\n")
|
419
|
+
controls = []
|
420
|
+
for lbl in msgs:
|
421
|
+
control = wxStaticText(self, label=lbl)
|
422
|
+
current_sizer.Add(control, expansion_flag * weight, wx.EXPAND, 0)
|
423
|
+
elif data_type == bool and data_style == "button":
|
424
|
+
# This is just a signal to the outside world.
|
425
|
+
wants_listener = False
|
426
|
+
control = wxButton(self, label=label)
|
427
|
+
|
428
|
+
def on_button(param, obj, addsig):
|
429
|
+
def check(event=None):
|
430
|
+
# We just set it to True to kick it off
|
431
|
+
setattr(obj, param, True)
|
432
|
+
# We don't signal ourselves...
|
433
|
+
self.context.signal(param, True, obj)
|
434
|
+
for _sig in addsig:
|
435
|
+
self.context.signal(_sig)
|
436
|
+
|
437
|
+
return check
|
438
|
+
|
439
|
+
control.Bind(
|
440
|
+
wx.EVT_BUTTON,
|
441
|
+
on_button(attr, obj, additional_signal),
|
442
|
+
)
|
443
|
+
if ctrl_width > 0:
|
444
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
445
|
+
current_sizer.Add(control, expansion_flag * weight, wx.EXPAND, 0)
|
446
|
+
elif data_type == bool:
|
447
|
+
# Bool type objects get a checkbox.
|
448
|
+
control = wxCheckBox(self, label=label)
|
449
|
+
control.SetValue(data)
|
450
|
+
control.SetMinSize(dip_size(self, -1, 23))
|
451
|
+
|
452
|
+
def on_checkbox_check(param, ctrl, obj, addsig):
|
453
|
+
def check(event=None):
|
454
|
+
v = ctrl.GetValue()
|
455
|
+
current_value = getattr(obj, param)
|
456
|
+
if current_value != bool(v):
|
457
|
+
setattr(obj, param, bool(v))
|
458
|
+
self.context.signal(param, v, obj)
|
459
|
+
for _sig in addsig:
|
460
|
+
self.context.signal(_sig)
|
461
|
+
|
462
|
+
return check
|
463
|
+
|
464
|
+
control.Bind(
|
465
|
+
wx.EVT_CHECKBOX,
|
466
|
+
on_checkbox_check(attr, control, obj, additional_signal),
|
467
|
+
)
|
468
|
+
|
469
|
+
current_sizer.Add(control, expansion_flag * weight, wx.EXPAND, 0)
|
470
|
+
elif data_type == str and data_style == "multiline":
|
471
|
+
control_sizer = StaticBoxSizer(self, wx.ID_ANY, label, wx.HORIZONTAL)
|
472
|
+
control = TextCtrl(
|
473
|
+
self,
|
474
|
+
wx.ID_ANY,
|
475
|
+
style=wx.TE_MULTILINE,
|
476
|
+
)
|
477
|
+
ctrl_width = c.get("width", 0)
|
478
|
+
if ctrl_width > 0:
|
479
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
480
|
+
control.SetValue(str(data))
|
481
|
+
if ctrl_width > 0:
|
482
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
483
|
+
if ctrl_width > 0:
|
484
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
485
|
+
control_sizer.Add(control, 1, wx.EXPAND, 0)
|
486
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
487
|
+
|
488
|
+
def on_generic_multi(param, ctrl, obj, dtype, addsig):
|
489
|
+
def text():
|
490
|
+
v = ctrl.GetValue()
|
491
|
+
try:
|
492
|
+
dtype_v = dtype(v)
|
493
|
+
current_value = getattr(obj, param)
|
494
|
+
if current_value != dtype_v:
|
495
|
+
setattr(obj, param, dtype_v)
|
496
|
+
self.context.signal(param, dtype_v, obj)
|
497
|
+
for _sig in addsig:
|
498
|
+
self.context.signal(_sig)
|
499
|
+
except ValueError:
|
500
|
+
# cannot cast to data_type, pass
|
501
|
+
pass
|
502
|
+
|
503
|
+
return text
|
504
|
+
|
505
|
+
control.SetActionRoutine(
|
506
|
+
on_generic_multi(attr, control, obj, data_type, additional_signal)
|
507
|
+
)
|
508
|
+
|
509
|
+
elif data_type == str and data_style == "file":
|
510
|
+
control_sizer = StaticBoxSizer(self, wx.ID_ANY, label, wx.HORIZONTAL)
|
511
|
+
control = TextCtrl(
|
512
|
+
self,
|
513
|
+
wx.ID_ANY,
|
514
|
+
style=wx.TE_PROCESS_ENTER,
|
515
|
+
)
|
516
|
+
control_btn = wxButton(self, wx.ID_ANY, "...")
|
517
|
+
|
518
|
+
def set_file(filename: str):
|
519
|
+
# if not filename:
|
520
|
+
# filename = _("No File")
|
521
|
+
control.SetValue(filename)
|
522
|
+
|
523
|
+
def on_button_filename(param, ctrl, obj, wildcard, addsig):
|
524
|
+
def click(event=None):
|
525
|
+
with wx.FileDialog(
|
526
|
+
self,
|
527
|
+
label,
|
528
|
+
wildcard=wildcard if wildcard else "*",
|
529
|
+
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_PREVIEW,
|
530
|
+
) as fileDialog:
|
531
|
+
if fileDialog.ShowModal() == wx.ID_CANCEL:
|
532
|
+
return # the user changed their mind
|
533
|
+
pathname = str(fileDialog.GetPath())
|
534
|
+
ctrl.SetValue(pathname)
|
535
|
+
self.Layout()
|
536
|
+
current_value = getattr(obj, param)
|
537
|
+
if current_value != pathname:
|
538
|
+
try:
|
539
|
+
setattr(obj, param, pathname)
|
540
|
+
self.context.signal(param, pathname, obj)
|
541
|
+
for _sig in addsig:
|
542
|
+
self.context.signal(_sig)
|
543
|
+
except ValueError:
|
544
|
+
# cannot cast to data_type, pass
|
545
|
+
pass
|
546
|
+
|
547
|
+
return click
|
548
|
+
|
549
|
+
def on_file_text(param, ctrl, obj, dtype, addsig):
|
550
|
+
def filetext():
|
551
|
+
v = ctrl.GetValue()
|
552
|
+
try:
|
553
|
+
dtype_v = dtype(v)
|
554
|
+
current_value = getattr(obj, param)
|
555
|
+
if current_value != dtype_v:
|
556
|
+
setattr(obj, param, dtype_v)
|
557
|
+
self.context.signal(param, dtype_v, obj)
|
558
|
+
for _sig in addsig:
|
559
|
+
self.context.signal(_sig)
|
560
|
+
except ValueError:
|
561
|
+
# cannot cast to data_type, pass
|
562
|
+
pass
|
563
|
+
|
564
|
+
return filetext
|
565
|
+
|
566
|
+
control.SetActionRoutine(
|
567
|
+
on_file_text(attr, control, obj, data_type, additional_signal)
|
568
|
+
)
|
569
|
+
|
570
|
+
ctrl_width = c.get("width", 0)
|
571
|
+
if ctrl_width > 0:
|
572
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
573
|
+
control.SetValue(str(data))
|
574
|
+
if ctrl_width > 0:
|
575
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
576
|
+
if ctrl_width > 0:
|
577
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
578
|
+
control_sizer.Add(control, 1, wx.EXPAND, 0)
|
579
|
+
control_sizer.Add(control_btn, 0, wx.EXPAND, 0)
|
580
|
+
control_btn.Bind(
|
581
|
+
wx.EVT_BUTTON,
|
582
|
+
on_button_filename(
|
583
|
+
attr, control, obj, c.get("wildcard", "*"), additional_signal
|
584
|
+
),
|
585
|
+
)
|
586
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
587
|
+
elif data_type in (int, float) and data_style == "slider":
|
588
|
+
if label != "":
|
589
|
+
control_sizer = StaticBoxSizer(
|
590
|
+
self, wx.ID_ANY, label, wx.HORIZONTAL
|
591
|
+
)
|
592
|
+
else:
|
593
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
594
|
+
minvalue = c.get("min", 0)
|
595
|
+
maxvalue = c.get("max", 0)
|
596
|
+
if data_type == float:
|
597
|
+
value = float(data)
|
598
|
+
elif data_type == int:
|
599
|
+
value = int(data)
|
600
|
+
else:
|
601
|
+
value = int(data)
|
602
|
+
control = wx.Slider(
|
603
|
+
self,
|
604
|
+
wx.ID_ANY,
|
605
|
+
value=value,
|
606
|
+
minValue=minvalue,
|
607
|
+
maxValue=maxvalue,
|
608
|
+
style=wx.SL_HORIZONTAL | wx.SL_VALUE_LABEL,
|
609
|
+
)
|
610
|
+
|
611
|
+
def on_slider(param, ctrl, obj, dtype, addsig):
|
612
|
+
def select(event=None):
|
613
|
+
v = dtype(ctrl.GetValue())
|
614
|
+
current_value = getattr(obj, param)
|
615
|
+
if current_value != v:
|
616
|
+
setattr(obj, param, v)
|
617
|
+
self.context.signal(param, v, obj)
|
618
|
+
for _sig in addsig:
|
619
|
+
self.context.signal(_sig)
|
620
|
+
|
621
|
+
return select
|
622
|
+
|
623
|
+
if ctrl_width > 0:
|
624
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
625
|
+
control_sizer.Add(control, 1, wx.EXPAND, 0)
|
626
|
+
control.Bind(
|
627
|
+
wx.EVT_SLIDER,
|
628
|
+
on_slider(attr, control, obj, data_type, additional_signal),
|
629
|
+
)
|
630
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
631
|
+
elif data_type in (str, int, float) and data_style == "combo":
|
632
|
+
if label != "":
|
633
|
+
control_sizer = StaticBoxSizer(
|
634
|
+
self, wx.ID_ANY, label, wx.HORIZONTAL
|
635
|
+
)
|
636
|
+
else:
|
637
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
638
|
+
choice_list = list(map(str, c.get("choices", [c.get("default")])))
|
639
|
+
control = wxComboBox(
|
640
|
+
self,
|
641
|
+
wx.ID_ANY,
|
642
|
+
choices=choice_list,
|
643
|
+
style=wx.CB_DROPDOWN | wx.CB_READONLY,
|
644
|
+
)
|
645
|
+
if data is not None:
|
646
|
+
if data_type == str:
|
647
|
+
control.SetValue(str(data))
|
648
|
+
else:
|
649
|
+
least = None
|
650
|
+
for entry in choice_list:
|
651
|
+
if least is None:
|
652
|
+
least = entry
|
653
|
+
else:
|
654
|
+
if abs(data_type(entry) - data) < abs(
|
655
|
+
data_type(least) - data
|
656
|
+
):
|
657
|
+
least = entry
|
658
|
+
if least is not None:
|
659
|
+
control.SetValue(least)
|
660
|
+
|
661
|
+
def on_combo_text(param, ctrl, obj, dtype, addsig):
|
662
|
+
def select(event=None):
|
663
|
+
v = dtype(ctrl.GetValue())
|
664
|
+
current_value = getattr(obj, param)
|
665
|
+
if current_value != v:
|
666
|
+
setattr(obj, param, v)
|
667
|
+
self.context.signal(param, v, obj)
|
668
|
+
for _sig in addsig:
|
669
|
+
self.context.signal(_sig)
|
670
|
+
|
671
|
+
return select
|
672
|
+
|
673
|
+
if ctrl_width > 0:
|
674
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
675
|
+
control_sizer.Add(control, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
676
|
+
control.Bind(
|
677
|
+
wx.EVT_COMBOBOX,
|
678
|
+
on_combo_text(attr, control, obj, data_type, additional_signal),
|
679
|
+
)
|
680
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
681
|
+
elif data_type in (str, int) and data_style == "radio":
|
682
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
683
|
+
choice_list = list(map(str, c.get("choices", [c.get("default")])))
|
684
|
+
control = wxRadioBox(
|
685
|
+
self,
|
686
|
+
wx.ID_ANY,
|
687
|
+
label,
|
688
|
+
choices=choice_list,
|
689
|
+
majorDimension=3,
|
690
|
+
style=wx.RA_SPECIFY_COLS, # wx.RA_SPECIFY_ROWS,
|
691
|
+
)
|
692
|
+
if data is not None:
|
693
|
+
if data_type == str:
|
694
|
+
control.SetSelection(0)
|
695
|
+
for idx, _c in enumerate(choice_list):
|
696
|
+
if _c == data:
|
697
|
+
control.SetSelection(idx)
|
698
|
+
else:
|
699
|
+
control.SetSelection(int(data))
|
700
|
+
|
701
|
+
def on_radio_select(param, ctrl, obj, dtype, addsig):
|
702
|
+
def select(event=None):
|
703
|
+
if dtype == int:
|
704
|
+
v = dtype(ctrl.GetSelection())
|
705
|
+
else:
|
706
|
+
v = dtype(ctrl.GetLabel())
|
707
|
+
current_value = getattr(obj, param)
|
708
|
+
if current_value != v:
|
709
|
+
setattr(obj, param, v)
|
710
|
+
self.context.signal(param, v, obj)
|
711
|
+
for _sig in addsig:
|
712
|
+
self.context.signal(_sig)
|
713
|
+
|
714
|
+
return select
|
715
|
+
|
716
|
+
if ctrl_width > 0:
|
717
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
718
|
+
control_sizer.Add(control, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
719
|
+
control.Bind(
|
720
|
+
wx.EVT_RADIOBOX,
|
721
|
+
on_radio_select(attr, control, obj, data_type, additional_signal),
|
722
|
+
)
|
723
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
724
|
+
elif data_type in (int, str) and data_style == "option":
|
725
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
726
|
+
display_list = list(map(str, c.get("display")))
|
727
|
+
choice_list = list(map(str, c.get("choices", [c.get("default")])))
|
728
|
+
try:
|
729
|
+
index = choice_list.index(str(data))
|
730
|
+
except ValueError:
|
731
|
+
# Value was not in list.
|
732
|
+
index = 0
|
733
|
+
if data is None:
|
734
|
+
data = c.get("default")
|
735
|
+
display_list.insert(0, str(data))
|
736
|
+
choice_list.insert(0, str(data))
|
737
|
+
control = wxComboBox(
|
738
|
+
self,
|
739
|
+
wx.ID_ANY,
|
740
|
+
choices=display_list,
|
741
|
+
style=wx.CB_DROPDOWN | wx.CB_READONLY,
|
742
|
+
)
|
743
|
+
control.SetSelection(index)
|
744
|
+
|
745
|
+
# Constrain the width
|
746
|
+
testsize = control.GetBestSize()
|
747
|
+
control.SetMaxSize(dip_size(self, testsize[0] + 30, -1))
|
748
|
+
# print ("Display: %s" % display_list)
|
749
|
+
# print ("Choices: %s" % choice_list)
|
750
|
+
# print ("To set: %s" % str(data))
|
751
|
+
|
752
|
+
def on_combosmall_option(param, ctrl, obj, dtype, addsig, choice_list):
|
753
|
+
def select(event=None):
|
754
|
+
cl = choice_list[ctrl.GetSelection()]
|
755
|
+
v = dtype(cl)
|
756
|
+
current_value = getattr(obj, param)
|
757
|
+
if current_value != v:
|
758
|
+
setattr(obj, param, v)
|
759
|
+
self.context.signal(param, v, obj)
|
760
|
+
for _sig in addsig:
|
761
|
+
self.context.signal(_sig)
|
762
|
+
|
763
|
+
return select
|
764
|
+
|
765
|
+
if label != "":
|
766
|
+
# Try to center it vertically to the controls extent
|
767
|
+
wd, ht = control.GetSize()
|
768
|
+
label_text = wxStaticText(self, id=wx.ID_ANY, label=label + " ")
|
769
|
+
# label_text.SetMinSize(dip_size(self, -1, ht))
|
770
|
+
control_sizer.Add(label_text, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
771
|
+
control_sizer.Add(control, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
772
|
+
control.Bind(
|
773
|
+
wx.EVT_COMBOBOX,
|
774
|
+
on_combosmall_option(
|
775
|
+
attr, control, obj, data_type, additional_signal, choice_list
|
776
|
+
),
|
777
|
+
)
|
778
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
779
|
+
elif data_type in (str, int, float) and data_style == "combosmall":
|
780
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
781
|
+
exclusive = c.get("exclusive", True)
|
782
|
+
cb_style = wx.CB_DROPDOWN
|
783
|
+
if exclusive:
|
784
|
+
cb_style = cb_style | wx.CB_READONLY
|
785
|
+
|
786
|
+
choice_list = list(map(str, c.get("choices", [c.get("default")])))
|
787
|
+
control = wxComboBox(
|
788
|
+
self,
|
789
|
+
wx.ID_ANY,
|
790
|
+
choices=choice_list,
|
791
|
+
style = cb_style,
|
792
|
+
)
|
793
|
+
# Constrain the width
|
794
|
+
testsize = control.GetBestSize()
|
795
|
+
control.SetMaxSize(dip_size(self, testsize[0] + 30, -1))
|
796
|
+
# print ("Choices: %s" % choice_list)
|
797
|
+
# print ("To set: %s" % str(data))
|
798
|
+
if data is not None:
|
799
|
+
if data_type == str:
|
800
|
+
control.SetValue(str(data))
|
801
|
+
else:
|
802
|
+
least = None
|
803
|
+
for entry in choice_list:
|
804
|
+
if least is None:
|
805
|
+
least = entry
|
806
|
+
else:
|
807
|
+
if abs(data_type(entry) - data) < abs(
|
808
|
+
data_type(least) - data
|
809
|
+
):
|
810
|
+
least = entry
|
811
|
+
if least is not None:
|
812
|
+
control.SetValue(least)
|
813
|
+
|
814
|
+
def on_combosmall_text(param, ctrl, obj, dtype, addsig):
|
815
|
+
def select(event=None):
|
816
|
+
v = dtype(ctrl.GetValue())
|
817
|
+
current_value = getattr(obj, param)
|
818
|
+
if current_value != v:
|
819
|
+
# print (f"Setting it to {v}")
|
820
|
+
setattr(obj, param, v)
|
821
|
+
self.context.signal(param, v, obj)
|
822
|
+
for _sig in addsig:
|
823
|
+
self.context.signal(_sig)
|
824
|
+
|
825
|
+
return select
|
826
|
+
|
827
|
+
if label != "":
|
828
|
+
# Try to center it vertically to the controls extent
|
829
|
+
wd, ht = control.GetSize()
|
830
|
+
label_text = wxStaticText(self, id=wx.ID_ANY, label=label + " ")
|
831
|
+
# label_text.SetMinSize(dip_size(self, -1, ht))
|
832
|
+
control_sizer.Add(label_text, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
833
|
+
if ctrl_width > 0:
|
834
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
835
|
+
control_sizer.Add(control, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
836
|
+
control.Bind(
|
837
|
+
wx.EVT_COMBOBOX,
|
838
|
+
on_combosmall_text(
|
839
|
+
attr, control, obj, data_type, additional_signal
|
840
|
+
),
|
841
|
+
)
|
842
|
+
if not exclusive:
|
843
|
+
control.Bind(
|
844
|
+
wx.EVT_TEXT,
|
845
|
+
on_combosmall_text(
|
846
|
+
attr, control, obj, data_type, additional_signal
|
847
|
+
),
|
848
|
+
)
|
849
|
+
|
850
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
851
|
+
elif data_type == int and data_style == "binary":
|
852
|
+
mask = c.get("mask")
|
853
|
+
|
854
|
+
# get default value
|
855
|
+
mask_bits = 0
|
856
|
+
if mask is not None and hasattr(obj, mask):
|
857
|
+
mask_bits = getattr(obj, mask)
|
858
|
+
|
859
|
+
if label != "":
|
860
|
+
control_sizer = StaticBoxSizer(
|
861
|
+
self, wx.ID_ANY, label, wx.HORIZONTAL
|
862
|
+
)
|
863
|
+
else:
|
864
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
865
|
+
|
866
|
+
def on_checkbox_check(param, ctrl, obj, bit, addsig, enable_ctrl=None):
|
867
|
+
def check(event=None):
|
868
|
+
v = ctrl.GetValue()
|
869
|
+
if enable_ctrl is not None:
|
870
|
+
enable_ctrl.Enable(v)
|
871
|
+
current = getattr(obj, param)
|
872
|
+
if v:
|
873
|
+
current |= 1 << bit
|
874
|
+
else:
|
875
|
+
current = ~((~current) | (1 << bit))
|
876
|
+
current_value = getattr(obj, param)
|
877
|
+
if current_value != current:
|
878
|
+
setattr(obj, param, current)
|
879
|
+
self.context.signal(param, v, obj)
|
880
|
+
for _sig in addsig:
|
881
|
+
self.context.signal(_sig)
|
882
|
+
|
883
|
+
return check
|
884
|
+
|
885
|
+
bit_sizer = wx.BoxSizer(wx.VERTICAL)
|
886
|
+
label_text = wxStaticText(
|
887
|
+
self, wx.ID_ANY, "", style=wx.ALIGN_CENTRE_HORIZONTAL
|
888
|
+
)
|
889
|
+
bit_sizer.Add(label_text, 0, wx.EXPAND, 0)
|
890
|
+
if mask is not None:
|
891
|
+
label_text = wxStaticText(
|
892
|
+
self,
|
893
|
+
wx.ID_ANY,
|
894
|
+
_("mask") + " ",
|
895
|
+
style=wx.ALIGN_CENTRE_HORIZONTAL,
|
896
|
+
)
|
897
|
+
bit_sizer.Add(label_text, 0, wx.EXPAND, 0)
|
898
|
+
label_text = wxStaticText(
|
899
|
+
self, wx.ID_ANY, _("value") + " ", style=wx.ALIGN_CENTRE_HORIZONTAL
|
900
|
+
)
|
901
|
+
bit_sizer.Add(label_text, 0, wx.EXPAND, 0)
|
902
|
+
control_sizer.Add(bit_sizer, 0, wx.EXPAND, 0)
|
903
|
+
|
904
|
+
bits = c.get("bits", 8)
|
905
|
+
for b in range(bits):
|
906
|
+
# Label
|
907
|
+
bit_sizer = wx.BoxSizer(wx.VERTICAL)
|
908
|
+
label_text = wxStaticText(
|
909
|
+
self, wx.ID_ANY, str(b), style=wx.ALIGN_CENTRE_HORIZONTAL
|
910
|
+
)
|
911
|
+
bit_sizer.Add(label_text, 0, wx.EXPAND, 0)
|
912
|
+
|
913
|
+
# value bit
|
914
|
+
control = wxCheckBox(self)
|
915
|
+
control.SetValue(bool((data >> b) & 1))
|
916
|
+
if mask:
|
917
|
+
control.Enable(bool((mask_bits >> b) & 1))
|
918
|
+
control.Bind(
|
919
|
+
wx.EVT_CHECKBOX,
|
920
|
+
on_checkbox_check(attr, control, obj, b, additional_signal),
|
921
|
+
)
|
922
|
+
|
923
|
+
# mask bit
|
924
|
+
if mask:
|
925
|
+
mask_ctrl = wxCheckBox(self)
|
926
|
+
mask_ctrl.SetValue(bool((mask_bits >> b) & 1))
|
927
|
+
mask_ctrl.Bind(
|
928
|
+
wx.EVT_CHECKBOX,
|
929
|
+
on_checkbox_check(
|
930
|
+
mask,
|
931
|
+
mask_ctrl,
|
932
|
+
obj,
|
933
|
+
b,
|
934
|
+
additional_signal,
|
935
|
+
enable_ctrl=control,
|
936
|
+
),
|
937
|
+
)
|
938
|
+
bit_sizer.Add(mask_ctrl, 0, wx.EXPAND, 0)
|
939
|
+
|
940
|
+
bit_sizer.Add(control, 0, wx.EXPAND, 0)
|
941
|
+
control_sizer.Add(bit_sizer, 0, wx.EXPAND, 0)
|
942
|
+
|
943
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
944
|
+
elif data_type == str and data_style == "color":
|
945
|
+
# str data_type with style "color" objects do get a button with the background.
|
946
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
947
|
+
control = wxButton(self, -1)
|
948
|
+
|
949
|
+
def set_color(ctrl, color: Color):
|
950
|
+
ctrl.SetLabel(str(color.hex))
|
951
|
+
ctrl.SetBackgroundColour(wx.Colour(swizzlecolor(color)))
|
952
|
+
if Color.distance(color, Color("black")) > Color.distance(
|
953
|
+
color, Color("white")
|
954
|
+
):
|
955
|
+
ctrl.SetForegroundColour(wx.BLACK)
|
956
|
+
else:
|
957
|
+
ctrl.SetForegroundColour(wx.WHITE)
|
958
|
+
ctrl.color = color
|
959
|
+
|
960
|
+
def on_button_color(param, ctrl, obj, addsig):
|
961
|
+
def click(event=None):
|
962
|
+
color_data = wx.ColourData()
|
963
|
+
color_data.SetColour(wx.Colour(swizzlecolor(ctrl.color)))
|
964
|
+
dlg = wx.ColourDialog(self, color_data)
|
965
|
+
if dlg.ShowModal() == wx.ID_OK:
|
966
|
+
color_data = dlg.GetColourData()
|
967
|
+
data = Color(
|
968
|
+
swizzlecolor(color_data.GetColour().GetRGB()), 1.0
|
969
|
+
)
|
970
|
+
set_color(ctrl, data)
|
971
|
+
try:
|
972
|
+
data_v = data.hexa
|
973
|
+
current_value = getattr(obj, param)
|
974
|
+
if current_value != data_v:
|
975
|
+
setattr(obj, param, data_v)
|
976
|
+
self.context.signal(param, data_v, obj)
|
977
|
+
for _sig in addsig:
|
978
|
+
self.context.signal(_sig)
|
979
|
+
except ValueError:
|
980
|
+
# cannot cast to data_type, pass
|
981
|
+
pass
|
982
|
+
|
983
|
+
return click
|
984
|
+
|
985
|
+
datastr = data
|
986
|
+
data = Color(datastr)
|
987
|
+
set_color(control, data)
|
988
|
+
if ctrl_width > 0:
|
989
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
990
|
+
control_sizer.Add(control, 0, wx.EXPAND, 0)
|
991
|
+
color_info = wxStaticText(self, wx.ID_ANY, label)
|
992
|
+
control_sizer.Add(color_info, 1, wx.ALIGN_CENTER_VERTICAL)
|
993
|
+
|
994
|
+
control.Bind(
|
995
|
+
wx.EVT_BUTTON,
|
996
|
+
on_button_color(attr, control, obj, additional_signal),
|
997
|
+
)
|
998
|
+
if ctrl_width > 0:
|
999
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
1000
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
1001
|
+
elif data_type == list and data_style == "chart":
|
1002
|
+
chart = EditableListCtrl(
|
1003
|
+
self,
|
1004
|
+
wx.ID_ANY,
|
1005
|
+
style=wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES | wx.LC_SINGLE_SEL,
|
1006
|
+
context=self.context, list_name=f"list_chart_{attr}",
|
1007
|
+
)
|
1008
|
+
l_columns = c.get("columns", [])
|
1009
|
+
|
1010
|
+
def fill_ctrl(ctrl, local_obj, param, columns):
|
1011
|
+
data = getattr(local_obj, param)
|
1012
|
+
ctrl.ClearAll()
|
1013
|
+
for column in columns:
|
1014
|
+
wd = column.get("width", 150)
|
1015
|
+
if wd < 0:
|
1016
|
+
wd = ctrl.Size[0] - 10
|
1017
|
+
ctrl.AppendColumn(
|
1018
|
+
column.get("label", ""),
|
1019
|
+
format=wx.LIST_FORMAT_LEFT,
|
1020
|
+
width=wd,
|
1021
|
+
)
|
1022
|
+
ctrl.resize_columns()
|
1023
|
+
for dataline in data:
|
1024
|
+
if isinstance(dataline, dict):
|
1025
|
+
for kk in dataline.keys():
|
1026
|
+
key = kk
|
1027
|
+
break
|
1028
|
+
row_id = ctrl.InsertItem(
|
1029
|
+
ctrl.GetItemCount(),
|
1030
|
+
dataline.get(key, 0),
|
1031
|
+
)
|
1032
|
+
for column_id, column in enumerate(columns):
|
1033
|
+
c_attr = column.get("attr")
|
1034
|
+
ctrl.SetItem(
|
1035
|
+
row_id, column_id, str(dataline.get(c_attr, ""))
|
1036
|
+
)
|
1037
|
+
elif isinstance(dataline, str):
|
1038
|
+
row_id = ctrl.InsertItem(
|
1039
|
+
ctrl.GetItemCount(),
|
1040
|
+
dataline,
|
1041
|
+
)
|
1042
|
+
elif isinstance(dataline, (list, tuple)):
|
1043
|
+
row_id = ctrl.InsertItem(
|
1044
|
+
ctrl.GetItemCount(),
|
1045
|
+
dataline[0],
|
1046
|
+
)
|
1047
|
+
for column_id, column in enumerate(columns):
|
1048
|
+
# c_attr = column.get("attr")
|
1049
|
+
ctrl.SetItem(row_id, column_id, dataline[column_id])
|
1050
|
+
|
1051
|
+
fill_ctrl(chart, obj, attr, l_columns)
|
1052
|
+
|
1053
|
+
def on_chart_start(columns, param, ctrl, local_obj):
|
1054
|
+
def chart_start(event=None):
|
1055
|
+
for column in columns:
|
1056
|
+
if column.get("editable", False):
|
1057
|
+
event.Allow()
|
1058
|
+
else:
|
1059
|
+
event.Veto()
|
1060
|
+
|
1061
|
+
return chart_start
|
1062
|
+
|
1063
|
+
chart.Bind(
|
1064
|
+
wx.EVT_LIST_BEGIN_LABEL_EDIT,
|
1065
|
+
on_chart_start(l_columns, attr, chart, obj),
|
1066
|
+
)
|
1067
|
+
|
1068
|
+
def on_chart_stop(columns, param, ctrl, local_obj):
|
1069
|
+
def chart_stop(event=None):
|
1070
|
+
row_id = event.GetIndex() # Get the current row
|
1071
|
+
col_id = event.GetColumn() # Get the current column
|
1072
|
+
new_data = event.GetLabel() # Get the changed data
|
1073
|
+
ctrl.SetItem(row_id, col_id, new_data)
|
1074
|
+
column = columns[col_id]
|
1075
|
+
c_attr = column.get("attr")
|
1076
|
+
c_type = column.get("type")
|
1077
|
+
values = getattr(local_obj, param)
|
1078
|
+
if isinstance(values[row_id], dict):
|
1079
|
+
values[row_id][c_attr] = c_type(new_data)
|
1080
|
+
self.context.signal(param, values, row_id, param)
|
1081
|
+
elif isinstance(values[row_id], str):
|
1082
|
+
values[row_id] = c_type(new_data)
|
1083
|
+
self.context.signal(param, values, row_id)
|
1084
|
+
else:
|
1085
|
+
values[row_id][col_id] = c_type(new_data)
|
1086
|
+
self.context.signal(param, values, row_id)
|
1087
|
+
|
1088
|
+
return chart_stop
|
1089
|
+
|
1090
|
+
chart.Bind(
|
1091
|
+
wx.EVT_LIST_END_LABEL_EDIT,
|
1092
|
+
on_chart_stop(l_columns, attr, chart, obj),
|
1093
|
+
)
|
1094
|
+
|
1095
|
+
allow_deletion = c.get("allow_deletion", False)
|
1096
|
+
allow_duplication = c.get("allow_duplication", False)
|
1097
|
+
|
1098
|
+
def on_chart_contextmenu(
|
1099
|
+
columns, param, ctrl, local_obj, allow_del, allow_dup, default
|
1100
|
+
):
|
1101
|
+
def chart_menu(event=None):
|
1102
|
+
# row_id = event.GetIndex() # Get the current row
|
1103
|
+
|
1104
|
+
x, y = event.GetPosition()
|
1105
|
+
row_id, flags = ctrl.HitTest((x, y))
|
1106
|
+
if row_id < 0:
|
1107
|
+
l_allow_del = False
|
1108
|
+
l_allow_dup = False
|
1109
|
+
else:
|
1110
|
+
l_allow_del = allow_del
|
1111
|
+
l_allow_dup = allow_dup
|
1112
|
+
menu = wx.Menu()
|
1113
|
+
if l_allow_del:
|
1114
|
+
|
1115
|
+
def on_delete(event):
|
1116
|
+
values = getattr(local_obj, param)
|
1117
|
+
# try:
|
1118
|
+
values.pop(row_id)
|
1119
|
+
self.context.signal(param, values, 0, param)
|
1120
|
+
fill_ctrl(ctrl, local_obj, param, columns)
|
1121
|
+
# except IndexError:
|
1122
|
+
# pass
|
1123
|
+
|
1124
|
+
menuitem = menu.Append(
|
1125
|
+
wx.ID_ANY, _("Delete this entry"), ""
|
1126
|
+
)
|
1127
|
+
self.Bind(
|
1128
|
+
wx.EVT_MENU,
|
1129
|
+
on_delete,
|
1130
|
+
id=menuitem.GetId(),
|
1131
|
+
)
|
1132
|
+
if l_allow_dup:
|
1133
|
+
|
1134
|
+
def on_duplicate(event):
|
1135
|
+
values = getattr(local_obj, param)
|
1136
|
+
if isinstance(values[row_id], dict):
|
1137
|
+
newentry = dict()
|
1138
|
+
for key, content in values[row_id].items():
|
1139
|
+
newentry[key] = content
|
1140
|
+
else:
|
1141
|
+
newentry = copy(values[row_id])
|
1142
|
+
values.append(newentry)
|
1143
|
+
self.context.signal(param, values, 0, param)
|
1144
|
+
# except IndexError:
|
1145
|
+
# pass
|
1146
|
+
fill_ctrl(ctrl, local_obj, param, columns)
|
1147
|
+
|
1148
|
+
menuitem = menu.Append(
|
1149
|
+
wx.ID_ANY, _("Duplicate this entry"), ""
|
1150
|
+
)
|
1151
|
+
self.Bind(
|
1152
|
+
wx.EVT_MENU,
|
1153
|
+
on_duplicate,
|
1154
|
+
id=menuitem.GetId(),
|
1155
|
+
)
|
1156
|
+
|
1157
|
+
def on_default(event):
|
1158
|
+
values = getattr(local_obj, param)
|
1159
|
+
values.clear()
|
1160
|
+
for e in default:
|
1161
|
+
values.append(e)
|
1162
|
+
|
1163
|
+
self.context.signal(param, values, 0, param)
|
1164
|
+
fill_ctrl(ctrl, obj, param, columns)
|
1165
|
+
# except IndexError:
|
1166
|
+
# pass
|
1167
|
+
|
1168
|
+
menuitem = menu.Append(wx.ID_ANY, _("Restore defaults"), "")
|
1169
|
+
self.Bind(
|
1170
|
+
wx.EVT_MENU,
|
1171
|
+
on_default,
|
1172
|
+
id=menuitem.GetId(),
|
1173
|
+
)
|
1174
|
+
|
1175
|
+
if menu.MenuItemCount != 0:
|
1176
|
+
self.PopupMenu(menu)
|
1177
|
+
menu.Destroy()
|
1178
|
+
|
1179
|
+
return chart_menu
|
1180
|
+
|
1181
|
+
default = c.get("default", [])
|
1182
|
+
# chart.Bind(
|
1183
|
+
# wx.EVT_LIST_ITEM_RIGHT_CLICK,
|
1184
|
+
# on_chart_contextmenu(
|
1185
|
+
# l_columns,
|
1186
|
+
# attr,
|
1187
|
+
# chart,
|
1188
|
+
# obj,
|
1189
|
+
# allow_deletion,
|
1190
|
+
# allow_duplication,
|
1191
|
+
# default,
|
1192
|
+
# ),
|
1193
|
+
# )
|
1194
|
+
# chart.Bind(
|
1195
|
+
# wx.EVT_LIST_COL_RIGHT_CLICK,
|
1196
|
+
# on_chart_contextmenu(
|
1197
|
+
# l_columns,
|
1198
|
+
# attr,
|
1199
|
+
# chart,
|
1200
|
+
# obj,
|
1201
|
+
# allow_deletion,
|
1202
|
+
# allow_duplication,
|
1203
|
+
# default,
|
1204
|
+
# ),
|
1205
|
+
# )
|
1206
|
+
chart.Bind(
|
1207
|
+
wx.EVT_RIGHT_DOWN,
|
1208
|
+
on_chart_contextmenu(
|
1209
|
+
l_columns,
|
1210
|
+
attr,
|
1211
|
+
chart,
|
1212
|
+
obj,
|
1213
|
+
allow_deletion,
|
1214
|
+
allow_duplication,
|
1215
|
+
default,
|
1216
|
+
),
|
1217
|
+
)
|
1218
|
+
current_sizer.Add(chart, expansion_flag * weight, wx.EXPAND, 0)
|
1219
|
+
elif data_type in (str, int, float):
|
1220
|
+
# str, int, and float type objects get a TextCtrl setter.
|
1221
|
+
if label != "" and data_style != "flat":
|
1222
|
+
control_sizer = StaticBoxSizer(
|
1223
|
+
self, wx.ID_ANY, label, wx.HORIZONTAL
|
1224
|
+
)
|
1225
|
+
else:
|
1226
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
1227
|
+
if label != "":
|
1228
|
+
label_text = wxStaticText(self, id=wx.ID_ANY, label=label)
|
1229
|
+
control_sizer.Add(label_text, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
1230
|
+
|
1231
|
+
if data_type == int:
|
1232
|
+
check_flag = "int"
|
1233
|
+
limit = True
|
1234
|
+
lower_range = c.get("lower", None)
|
1235
|
+
upper_range = c.get("upper", None)
|
1236
|
+
elif data_type == float:
|
1237
|
+
check_flag = "float"
|
1238
|
+
limit = True
|
1239
|
+
lower_range = c.get("lower", None)
|
1240
|
+
upper_range = c.get("upper", None)
|
1241
|
+
else:
|
1242
|
+
check_flag = ""
|
1243
|
+
limit = False
|
1244
|
+
lower_range = None
|
1245
|
+
upper_range = None
|
1246
|
+
|
1247
|
+
control = TextCtrl(
|
1248
|
+
self,
|
1249
|
+
wx.ID_ANY,
|
1250
|
+
style=wx.TE_PROCESS_ENTER,
|
1251
|
+
limited=limit,
|
1252
|
+
check=check_flag,
|
1253
|
+
)
|
1254
|
+
if lower_range is not None:
|
1255
|
+
control.lower_limit = lower_range
|
1256
|
+
control.lower_limit_err = lower_range
|
1257
|
+
if upper_range is not None:
|
1258
|
+
control.upper_limit = upper_range
|
1259
|
+
control.upper_limit_err = upper_range
|
1260
|
+
ctrl_width = c.get("width", 0)
|
1261
|
+
if ctrl_width > 0:
|
1262
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
1263
|
+
control.SetValue(str(data))
|
1264
|
+
if ctrl_width > 0:
|
1265
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
1266
|
+
control_sizer.Add(control, 1, wx.EXPAND, 0)
|
1267
|
+
|
1268
|
+
def on_generic_text(param, ctrl, obj, dtype, addsig):
|
1269
|
+
def text():
|
1270
|
+
v = ctrl.GetValue()
|
1271
|
+
try:
|
1272
|
+
dtype_v = dtype(v)
|
1273
|
+
current_value = getattr(obj, param)
|
1274
|
+
if current_value != dtype_v:
|
1275
|
+
setattr(obj, param, dtype_v)
|
1276
|
+
self.context.signal(param, dtype_v, obj)
|
1277
|
+
for _sig in addsig:
|
1278
|
+
self.context.signal(_sig)
|
1279
|
+
except ValueError:
|
1280
|
+
# cannot cast to data_type, pass
|
1281
|
+
pass
|
1282
|
+
|
1283
|
+
return text
|
1284
|
+
|
1285
|
+
control.SetActionRoutine(
|
1286
|
+
on_generic_text(attr, control, obj, data_type, additional_signal)
|
1287
|
+
)
|
1288
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
1289
|
+
elif data_type == Length:
|
1290
|
+
# Length type is a TextCtrl with special checks
|
1291
|
+
if label != "":
|
1292
|
+
control_sizer = StaticBoxSizer(
|
1293
|
+
self, wx.ID_ANY, label, wx.HORIZONTAL
|
1294
|
+
)
|
1295
|
+
else:
|
1296
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
1297
|
+
nonzero = c.get("nonzero", False)
|
1298
|
+
if nonzero is None or not isinstance(nonzero, bool):
|
1299
|
+
nonzero = False
|
1300
|
+
control = TextCtrl(
|
1301
|
+
self,
|
1302
|
+
wx.ID_ANY,
|
1303
|
+
style=wx.TE_PROCESS_ENTER,
|
1304
|
+
limited=True,
|
1305
|
+
check="length",
|
1306
|
+
nonzero=nonzero,
|
1307
|
+
)
|
1308
|
+
if isinstance(data, Length):
|
1309
|
+
if not data._preferred_units:
|
1310
|
+
data._preferred_units = "mm"
|
1311
|
+
if not data._digits:
|
1312
|
+
if data._preferred_units in ("mm", "cm", "in", "inch"):
|
1313
|
+
data._digits = 4
|
1314
|
+
display_value = data.preferred_length
|
1315
|
+
else:
|
1316
|
+
display_value = str(data)
|
1317
|
+
control.SetValue(display_value)
|
1318
|
+
if ctrl_width > 0:
|
1319
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
1320
|
+
control_sizer.Add(control, 1, wx.EXPAND, 0)
|
1321
|
+
|
1322
|
+
def on_length_text(param, ctrl, obj, dtype, addsig):
|
1323
|
+
def text():
|
1324
|
+
try:
|
1325
|
+
v = Length(ctrl.GetValue())
|
1326
|
+
data_v = v.preferred_length
|
1327
|
+
current_value = getattr(obj, param)
|
1328
|
+
if str(current_value) != str(data_v):
|
1329
|
+
setattr(obj, param, data_v)
|
1330
|
+
self.context.signal(param, data_v, obj)
|
1331
|
+
for _sig in addsig:
|
1332
|
+
self.context.signal(_sig)
|
1333
|
+
except ValueError:
|
1334
|
+
# cannot cast to data_type, pass
|
1335
|
+
pass
|
1336
|
+
|
1337
|
+
return text
|
1338
|
+
|
1339
|
+
control.SetActionRoutine(
|
1340
|
+
on_length_text(attr, control, obj, data_type, additional_signal)
|
1341
|
+
)
|
1342
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
1343
|
+
elif data_type == Angle:
|
1344
|
+
# Angle type is a TextCtrl with special checks
|
1345
|
+
if label != "":
|
1346
|
+
control_sizer = StaticBoxSizer(
|
1347
|
+
self, wx.ID_ANY, label, wx.HORIZONTAL
|
1348
|
+
)
|
1349
|
+
else:
|
1350
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
1351
|
+
control = TextCtrl(
|
1352
|
+
self,
|
1353
|
+
wx.ID_ANY,
|
1354
|
+
style=wx.TE_PROCESS_ENTER,
|
1355
|
+
check="angle",
|
1356
|
+
limited=True,
|
1357
|
+
)
|
1358
|
+
control.SetValue(str(data))
|
1359
|
+
if ctrl_width > 0:
|
1360
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
1361
|
+
control_sizer.Add(control, 1, wx.EXPAND, 0)
|
1362
|
+
|
1363
|
+
def on_angle_text(param, ctrl, obj, dtype, addsig):
|
1364
|
+
def text():
|
1365
|
+
try:
|
1366
|
+
v = Angle(ctrl.GetValue(), digits=5)
|
1367
|
+
data_v = str(v)
|
1368
|
+
current_value = str(getattr(obj, param))
|
1369
|
+
if current_value != data_v:
|
1370
|
+
setattr(obj, param, data_v)
|
1371
|
+
self.context.signal(param, data_v, obj)
|
1372
|
+
for _sig in addsig:
|
1373
|
+
self.context.signal(_sig)
|
1374
|
+
except ValueError:
|
1375
|
+
# cannot cast to data_type, pass
|
1376
|
+
pass
|
1377
|
+
|
1378
|
+
return text
|
1379
|
+
|
1380
|
+
control.SetActionRoutine(
|
1381
|
+
on_angle_text(attr, control, obj, data_type, additional_signal)
|
1382
|
+
)
|
1383
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
1384
|
+
elif data_type == Color:
|
1385
|
+
# Color data_type objects are get a button with the background.
|
1386
|
+
if label != "":
|
1387
|
+
control_sizer = StaticBoxSizer(
|
1388
|
+
self, wx.ID_ANY, label, wx.HORIZONTAL
|
1389
|
+
)
|
1390
|
+
else:
|
1391
|
+
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
1392
|
+
control = wxButton(self, -1)
|
1393
|
+
|
1394
|
+
def set_color(ctrl, color: Color):
|
1395
|
+
ctrl.SetLabel(str(color.hex))
|
1396
|
+
ctrl.SetBackgroundColour(wx.Colour(swizzlecolor(color)))
|
1397
|
+
if Color.distance(color, Color("black")) > Color.distance(
|
1398
|
+
color, Color("white")
|
1399
|
+
):
|
1400
|
+
ctrl.SetForegroundColour(wx.BLACK)
|
1401
|
+
else:
|
1402
|
+
ctrl.SetForegroundColour(wx.WHITE)
|
1403
|
+
ctrl.color = color
|
1404
|
+
|
1405
|
+
def on_button_color(param, ctrl, obj, addsig):
|
1406
|
+
def click(event=None):
|
1407
|
+
color_data = wx.ColourData()
|
1408
|
+
color_data.SetColour(wx.Colour(swizzlecolor(ctrl.color)))
|
1409
|
+
dlg = wx.ColourDialog(self, color_data)
|
1410
|
+
if dlg.ShowModal() == wx.ID_OK:
|
1411
|
+
color_data = dlg.GetColourData()
|
1412
|
+
data = Color(
|
1413
|
+
swizzlecolor(color_data.GetColour().GetRGB()), 1.0
|
1414
|
+
)
|
1415
|
+
set_color(ctrl, data)
|
1416
|
+
try:
|
1417
|
+
data_v = data_type(data)
|
1418
|
+
current_value = getattr(obj, param)
|
1419
|
+
if current_value != data_v:
|
1420
|
+
setattr(obj, param, data_v)
|
1421
|
+
self.context.signal(param, data_v, obj)
|
1422
|
+
for _sig in addsig:
|
1423
|
+
self.context.signal(_sig)
|
1424
|
+
except ValueError:
|
1425
|
+
# cannot cast to data_type, pass
|
1426
|
+
pass
|
1427
|
+
|
1428
|
+
return click
|
1429
|
+
|
1430
|
+
set_color(control, data)
|
1431
|
+
if ctrl_width > 0:
|
1432
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
1433
|
+
control_sizer.Add(control, 0, wx.EXPAND, 0)
|
1434
|
+
|
1435
|
+
control.Bind(
|
1436
|
+
wx.EVT_BUTTON,
|
1437
|
+
on_button_color(attr, control, obj, additional_signal),
|
1438
|
+
)
|
1439
|
+
if ctrl_width > 0:
|
1440
|
+
control.SetMaxSize(dip_size(self, ctrl_width, -1))
|
1441
|
+
current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
|
1442
|
+
else:
|
1443
|
+
# Requires a registered data_type
|
1444
|
+
continue
|
1445
|
+
|
1446
|
+
if trailer and control_sizer:
|
1447
|
+
trailer_text = wxStaticText(self, id=wx.ID_ANY, label=f" {trailer}")
|
1448
|
+
control_sizer.Add(trailer_text, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
1449
|
+
|
1450
|
+
if control is None:
|
1451
|
+
continue # We're binary or some other style without a specific control.
|
1452
|
+
|
1453
|
+
# Get enabled value
|
1454
|
+
try:
|
1455
|
+
enabled = c["enabled"]
|
1456
|
+
control.Enable(enabled)
|
1457
|
+
except KeyError:
|
1458
|
+
# Listen to establish whether this control should be enabled based on another control's value.
|
1459
|
+
try:
|
1460
|
+
conditional = c["conditional"]
|
1461
|
+
if len(conditional) == 2:
|
1462
|
+
c_obj, c_attr = conditional
|
1463
|
+
enabled = bool(getattr(c_obj, c_attr))
|
1464
|
+
c_equals = True
|
1465
|
+
control.Enable(enabled)
|
1466
|
+
elif len(conditional) == 3:
|
1467
|
+
c_obj, c_attr, c_equals = conditional
|
1468
|
+
enabled = bool(getattr(c_obj, c_attr) == c_equals)
|
1469
|
+
control.Enable(enabled)
|
1470
|
+
elif len(conditional) == 4:
|
1471
|
+
c_obj, c_attr, c_from, c_to = conditional
|
1472
|
+
enabled = bool(c_from <= getattr(c_obj, c_attr) <= c_to)
|
1473
|
+
c_equals = (c_from, c_to)
|
1474
|
+
control.Enable(enabled)
|
1475
|
+
|
1476
|
+
def on_enable_listener(param, ctrl, obj, eqs):
|
1477
|
+
def listen(origin, value, target=None):
|
1478
|
+
try:
|
1479
|
+
if isinstance(eqs, (list, tuple)):
|
1480
|
+
enable = bool(
|
1481
|
+
eqs[0] <= getattr(obj, param) <= eqs[1]
|
1482
|
+
)
|
1483
|
+
else:
|
1484
|
+
enable = bool(getattr(obj, param) == eqs)
|
1485
|
+
ctrl.Enable(enable)
|
1486
|
+
except (IndexError, RuntimeError):
|
1487
|
+
pass
|
1488
|
+
|
1489
|
+
return listen
|
1490
|
+
|
1491
|
+
listener = on_enable_listener(c_attr, control, c_obj, c_equals)
|
1492
|
+
self.listeners.append((c_attr, listener, c_obj))
|
1493
|
+
context.listen(c_attr, listener)
|
1494
|
+
except KeyError:
|
1495
|
+
pass
|
1496
|
+
|
1497
|
+
# Now we listen to 'ourselves' as well to learn about changes somewhere else...
|
1498
|
+
def on_update_listener(param, ctrl, dtype, dstyle, choicelist, sourceobj):
|
1499
|
+
def listen_to_myself(origin, value, target=None):
|
1500
|
+
if self.context.kernel.is_shutdown:
|
1501
|
+
return
|
1502
|
+
|
1503
|
+
if target is None or target is not sourceobj:
|
1504
|
+
# print (f"Signal for {param}={value}, but no target given or different to source")
|
1505
|
+
return
|
1506
|
+
update_needed = False
|
1507
|
+
# print (f"attr={param}, origin={origin}, value={value}, datatype={dtype}, datastyle={dstyle}")
|
1508
|
+
data = None
|
1509
|
+
if value is not None:
|
1510
|
+
try:
|
1511
|
+
data = dtype(value)
|
1512
|
+
except ValueError:
|
1513
|
+
pass
|
1514
|
+
if data is None:
|
1515
|
+
try:
|
1516
|
+
data = c["default"]
|
1517
|
+
except KeyError:
|
1518
|
+
pass
|
1519
|
+
if data is None:
|
1520
|
+
# print (f"Invalid data based on {value}, exiting")
|
1521
|
+
return
|
1522
|
+
# Let's just access the ctrl to see whether it has been already
|
1523
|
+
# destroyed (as we are in the midst of a shutdown)
|
1524
|
+
try:
|
1525
|
+
dummy = hasattr(ctrl, "GetValue")
|
1526
|
+
except RuntimeError:
|
1527
|
+
return
|
1528
|
+
if dtype == bool:
|
1529
|
+
# Bool type objects get a checkbox.
|
1530
|
+
if ctrl.GetValue() != data:
|
1531
|
+
ctrl.SetValue(data)
|
1532
|
+
elif dtype == str and dstyle == "file":
|
1533
|
+
if ctrl.GetValue() != data:
|
1534
|
+
ctrl.SetValue(data)
|
1535
|
+
elif dtype in (int, float) and dstyle == "slider":
|
1536
|
+
if ctrl.GetValue() != data:
|
1537
|
+
ctrl.SetValue(data)
|
1538
|
+
elif dtype in (str, int, float) and dstyle == "combo":
|
1539
|
+
if dtype == str:
|
1540
|
+
ctrl.SetValue(str(data))
|
1541
|
+
else:
|
1542
|
+
least = None
|
1543
|
+
for entry in choicelist:
|
1544
|
+
if least is None:
|
1545
|
+
least = entry
|
1546
|
+
else:
|
1547
|
+
if abs(dtype(entry) - data) < abs(
|
1548
|
+
dtype(least) - data
|
1549
|
+
):
|
1550
|
+
least = entry
|
1551
|
+
if least is not None:
|
1552
|
+
ctrl.SetValue(least)
|
1553
|
+
elif dtype in (str, int, float) and dstyle == "combosmall":
|
1554
|
+
if dtype == str:
|
1555
|
+
ctrl.SetValue(str(data))
|
1556
|
+
else:
|
1557
|
+
least = None
|
1558
|
+
for entry in choicelist:
|
1559
|
+
if least is None:
|
1560
|
+
least = entry
|
1561
|
+
else:
|
1562
|
+
if abs(dtype(entry) - data) < abs(
|
1563
|
+
dtype(least) - data
|
1564
|
+
):
|
1565
|
+
least = entry
|
1566
|
+
if least is not None:
|
1567
|
+
ctrl.SetValue(least)
|
1568
|
+
elif dtype == int and dstyle == "binary":
|
1569
|
+
pass # not supported...
|
1570
|
+
elif (dtype == str and dstyle == "color") or dtype == Color:
|
1571
|
+
# Color dtype objects are a button with the background set to the color
|
1572
|
+
def set_color(color: Color):
|
1573
|
+
ctrl.SetLabel(str(color.hex))
|
1574
|
+
ctrl.SetBackgroundColour(wx.Colour(swizzlecolor(color)))
|
1575
|
+
if Color.distance(color, Color("black")) > Color.distance(
|
1576
|
+
color, Color("white")
|
1577
|
+
):
|
1578
|
+
ctrl.SetForegroundColour(wx.BLACK)
|
1579
|
+
else:
|
1580
|
+
ctrl.SetForegroundColour(wx.WHITE)
|
1581
|
+
ctrl.color = color
|
1582
|
+
|
1583
|
+
if isinstance(data, str):
|
1584
|
+
# print ("Needed to change type")
|
1585
|
+
data = Color(data)
|
1586
|
+
# print (f"Will set color to {data}")
|
1587
|
+
set_color(data)
|
1588
|
+
elif dtype in (str, int, float):
|
1589
|
+
if hasattr(ctrl, "GetValue"):
|
1590
|
+
try:
|
1591
|
+
if dtype(ctrl.GetValue()) != data:
|
1592
|
+
update_needed = True
|
1593
|
+
except ValueError:
|
1594
|
+
update_needed = True
|
1595
|
+
if update_needed:
|
1596
|
+
ctrl.SetValue(str(data))
|
1597
|
+
elif dtype == Length:
|
1598
|
+
if float(data) != float(Length(ctrl.GetValue())):
|
1599
|
+
update_needed = True
|
1600
|
+
if update_needed:
|
1601
|
+
if not isinstance(data, str):
|
1602
|
+
data = Length(data).length_mm
|
1603
|
+
ctrl.SetValue(str(data))
|
1604
|
+
elif dtype == Angle:
|
1605
|
+
if ctrl.GetValue() != str(data):
|
1606
|
+
ctrl.SetValue(str(data))
|
1607
|
+
|
1608
|
+
return listen_to_myself
|
1609
|
+
|
1610
|
+
if wants_listener:
|
1611
|
+
update_listener = on_update_listener(
|
1612
|
+
attr, control, data_type, data_style, choice_list, obj
|
1613
|
+
)
|
1614
|
+
self.listeners.append((attr, update_listener, obj))
|
1615
|
+
context.listen(attr, update_listener)
|
1616
|
+
tip = c.get("tip")
|
1617
|
+
if tip and not context.root.disable_tool_tips:
|
1618
|
+
# Set the tool tip if 'tip' is available
|
1619
|
+
control.SetToolTip(tip)
|
1620
|
+
_help = c.get("help")
|
1621
|
+
if _help and hasattr(control, "SetHelpText"):
|
1622
|
+
control.SetHelpText(_help)
|
1623
|
+
last_page = this_page
|
1624
|
+
last_section = this_section
|
1625
|
+
last_subsection = this_subsection
|
1626
|
+
|
1627
|
+
self.SetSizer(sizer_very_main)
|
1628
|
+
sizer_very_main.Fit(self)
|
1629
|
+
self.Bind(wx.EVT_CLOSE, self.on_close)
|
1630
|
+
# Make sure stuff gets scrolled if necessary by default
|
1631
|
+
if scrolling:
|
1632
|
+
self.SetupScrolling()
|
1633
|
+
|
1634
|
+
def on_close(self, event):
|
1635
|
+
# We should not need this, but better safe than sorry
|
1636
|
+
event.Skip()
|
1637
|
+
self.pane_hide()
|
1638
|
+
|
1639
|
+
@staticmethod
|
1640
|
+
def unsorted_label(original):
|
1641
|
+
# Special sort key just to sort stuff - we fix the preceeding "_sortcriteria_Correct label"
|
1642
|
+
result = original
|
1643
|
+
if result.startswith("_"):
|
1644
|
+
idx = result.find("_", 1)
|
1645
|
+
if idx >= 0:
|
1646
|
+
result = result[idx + 1 :]
|
1647
|
+
return result
|
1648
|
+
|
1649
|
+
def reload(self):
|
1650
|
+
for attr, listener, obj in self.listeners:
|
1651
|
+
try:
|
1652
|
+
value = getattr(obj, attr)
|
1653
|
+
except AttributeError as e:
|
1654
|
+
# print(f"error: {e}")
|
1655
|
+
continue
|
1656
|
+
listener("internal", value, obj)
|
1657
|
+
|
1658
|
+
def module_close(self, *args, **kwargs):
|
1659
|
+
self.pane_hide()
|
1660
|
+
|
1661
|
+
def pane_hide(self):
|
1662
|
+
# print (f"hide called: {len(self.listeners)}")
|
1663
|
+
if len(self.listeners):
|
1664
|
+
for attr, listener, obj in self.listeners:
|
1665
|
+
self.context.unlisten(attr, listener)
|
1666
|
+
del listener
|
1667
|
+
self.listeners.clear()
|
1668
|
+
|
1669
|
+
def pane_show(self):
|
1670
|
+
# print ("show called")
|
1671
|
+
# if len(self.listeners) == 0:
|
1672
|
+
# print ("..but no one cares")
|
1673
|
+
pass
|