meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7020__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/__init__.py +1 -1
- meerk40t/balormk/balor_params.py +167 -167
- meerk40t/balormk/clone_loader.py +457 -457
- meerk40t/balormk/controller.py +1566 -1512
- meerk40t/balormk/cylindermod.py +64 -0
- meerk40t/balormk/device.py +966 -1959
- meerk40t/balormk/driver.py +778 -591
- meerk40t/balormk/galvo_commands.py +1194 -0
- meerk40t/balormk/gui/balorconfig.py +237 -111
- meerk40t/balormk/gui/balorcontroller.py +191 -184
- meerk40t/balormk/gui/baloroperationproperties.py +116 -115
- meerk40t/balormk/gui/corscene.py +845 -0
- meerk40t/balormk/gui/gui.py +179 -147
- meerk40t/balormk/livelightjob.py +466 -382
- meerk40t/balormk/mock_connection.py +131 -109
- meerk40t/balormk/plugin.py +133 -135
- meerk40t/balormk/usb_connection.py +306 -301
- meerk40t/camera/__init__.py +1 -1
- meerk40t/camera/camera.py +514 -397
- meerk40t/camera/gui/camerapanel.py +1241 -1095
- meerk40t/camera/gui/gui.py +58 -58
- meerk40t/camera/plugin.py +441 -399
- meerk40t/ch341/__init__.py +27 -27
- meerk40t/ch341/ch341device.py +628 -628
- meerk40t/ch341/libusb.py +595 -589
- meerk40t/ch341/mock.py +171 -171
- meerk40t/ch341/windriver.py +157 -157
- meerk40t/constants.py +13 -0
- meerk40t/core/__init__.py +1 -1
- meerk40t/core/bindalias.py +550 -539
- meerk40t/core/core.py +47 -47
- meerk40t/core/cutcode/cubiccut.py +73 -73
- meerk40t/core/cutcode/cutcode.py +315 -312
- meerk40t/core/cutcode/cutgroup.py +141 -137
- meerk40t/core/cutcode/cutobject.py +192 -185
- meerk40t/core/cutcode/dwellcut.py +37 -37
- meerk40t/core/cutcode/gotocut.py +29 -29
- meerk40t/core/cutcode/homecut.py +29 -29
- meerk40t/core/cutcode/inputcut.py +34 -34
- meerk40t/core/cutcode/linecut.py +33 -33
- meerk40t/core/cutcode/outputcut.py +34 -34
- meerk40t/core/cutcode/plotcut.py +335 -335
- meerk40t/core/cutcode/quadcut.py +61 -61
- meerk40t/core/cutcode/rastercut.py +168 -148
- meerk40t/core/cutcode/waitcut.py +34 -34
- meerk40t/core/cutplan.py +1843 -1316
- meerk40t/core/drivers.py +330 -329
- meerk40t/core/elements/align.py +801 -669
- meerk40t/core/elements/branches.py +1858 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4595 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4315 -3617
- meerk40t/core/elements/files.py +117 -64
- meerk40t/core/elements/geometry.py +473 -224
- meerk40t/core/elements/grid.py +467 -316
- meerk40t/core/elements/materials.py +158 -94
- meerk40t/core/elements/notes.py +50 -38
- meerk40t/core/elements/offset_clpr.py +934 -912
- meerk40t/core/elements/offset_mk.py +963 -955
- meerk40t/core/elements/penbox.py +339 -267
- meerk40t/core/elements/placements.py +300 -83
- meerk40t/core/elements/render.py +785 -687
- meerk40t/core/elements/shapes.py +2618 -2092
- meerk40t/core/elements/testcases.py +105 -0
- meerk40t/core/elements/trace.py +651 -563
- meerk40t/core/elements/tree_commands.py +415 -409
- meerk40t/core/elements/undo_redo.py +116 -58
- meerk40t/core/elements/wordlist.py +319 -200
- meerk40t/core/exceptions.py +9 -9
- meerk40t/core/laserjob.py +220 -220
- meerk40t/core/logging.py +63 -63
- meerk40t/core/node/blobnode.py +83 -86
- meerk40t/core/node/bootstrap.py +105 -103
- meerk40t/core/node/branch_elems.py +40 -31
- meerk40t/core/node/branch_ops.py +45 -38
- meerk40t/core/node/branch_regmark.py +48 -41
- meerk40t/core/node/cutnode.py +29 -32
- meerk40t/core/node/effect_hatch.py +375 -257
- meerk40t/core/node/effect_warp.py +398 -0
- meerk40t/core/node/effect_wobble.py +441 -309
- meerk40t/core/node/elem_ellipse.py +404 -309
- meerk40t/core/node/elem_image.py +1082 -801
- meerk40t/core/node/elem_line.py +358 -292
- meerk40t/core/node/elem_path.py +259 -201
- meerk40t/core/node/elem_point.py +129 -102
- meerk40t/core/node/elem_polyline.py +310 -246
- meerk40t/core/node/elem_rect.py +376 -286
- meerk40t/core/node/elem_text.py +445 -418
- meerk40t/core/node/filenode.py +59 -40
- meerk40t/core/node/groupnode.py +138 -74
- meerk40t/core/node/image_processed.py +777 -766
- meerk40t/core/node/image_raster.py +156 -113
- meerk40t/core/node/layernode.py +31 -31
- meerk40t/core/node/mixins.py +135 -107
- meerk40t/core/node/node.py +1427 -1304
- meerk40t/core/node/nutils.py +117 -114
- meerk40t/core/node/op_cut.py +463 -335
- meerk40t/core/node/op_dots.py +296 -251
- meerk40t/core/node/op_engrave.py +414 -311
- meerk40t/core/node/op_image.py +755 -369
- meerk40t/core/node/op_raster.py +787 -522
- meerk40t/core/node/place_current.py +37 -40
- meerk40t/core/node/place_point.py +329 -126
- meerk40t/core/node/refnode.py +58 -47
- meerk40t/core/node/rootnode.py +225 -219
- meerk40t/core/node/util_console.py +48 -48
- meerk40t/core/node/util_goto.py +84 -65
- meerk40t/core/node/util_home.py +61 -61
- meerk40t/core/node/util_input.py +102 -102
- meerk40t/core/node/util_output.py +102 -102
- meerk40t/core/node/util_wait.py +65 -65
- meerk40t/core/parameters.py +709 -707
- meerk40t/core/planner.py +875 -785
- meerk40t/core/plotplanner.py +656 -652
- meerk40t/core/space.py +120 -113
- meerk40t/core/spoolers.py +706 -705
- meerk40t/core/svg_io.py +1836 -1549
- meerk40t/core/treeop.py +534 -445
- meerk40t/core/undos.py +278 -124
- meerk40t/core/units.py +784 -680
- meerk40t/core/view.py +393 -322
- meerk40t/core/webhelp.py +62 -62
- meerk40t/core/wordlist.py +513 -504
- meerk40t/cylinder/cylinder.py +247 -0
- meerk40t/cylinder/gui/cylindersettings.py +41 -0
- meerk40t/cylinder/gui/gui.py +24 -0
- meerk40t/device/__init__.py +1 -1
- meerk40t/device/basedevice.py +322 -123
- meerk40t/device/devicechoices.py +50 -0
- meerk40t/device/dummydevice.py +163 -128
- meerk40t/device/gui/defaultactions.py +618 -602
- meerk40t/device/gui/effectspanel.py +114 -0
- meerk40t/device/gui/formatterpanel.py +253 -290
- meerk40t/device/gui/warningpanel.py +337 -260
- meerk40t/device/mixins.py +13 -13
- meerk40t/dxf/__init__.py +1 -1
- meerk40t/dxf/dxf_io.py +766 -554
- meerk40t/dxf/plugin.py +47 -35
- meerk40t/external_plugins.py +79 -79
- meerk40t/external_plugins_build.py +28 -28
- meerk40t/extra/cag.py +112 -116
- meerk40t/extra/coolant.py +403 -0
- meerk40t/extra/encode_detect.py +204 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +834 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +628 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +283 -0
- meerk40t/extra/param_functions.py +1542 -1556
- meerk40t/extra/potrace.py +257 -253
- meerk40t/extra/serial_exchange.py +118 -0
- meerk40t/extra/updater.py +602 -453
- meerk40t/extra/vectrace.py +147 -146
- meerk40t/extra/winsleep.py +83 -83
- meerk40t/extra/xcs_reader.py +597 -0
- meerk40t/fill/fills.py +781 -335
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +614 -567
- meerk40t/grbl/control.py +87 -87
- meerk40t/grbl/controller.py +990 -903
- meerk40t/grbl/device.py +1084 -768
- meerk40t/grbl/driver.py +989 -771
- meerk40t/grbl/emulator.py +532 -497
- meerk40t/grbl/gcodejob.py +783 -767
- meerk40t/grbl/gui/grblconfiguration.py +373 -298
- meerk40t/grbl/gui/grblcontroller.py +485 -271
- meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
- meerk40t/grbl/gui/grbloperationconfig.py +105 -0
- meerk40t/grbl/gui/gui.py +147 -116
- meerk40t/grbl/interpreter.py +44 -44
- meerk40t/grbl/loader.py +22 -22
- meerk40t/grbl/mock_connection.py +56 -56
- meerk40t/grbl/plugin.py +294 -264
- meerk40t/grbl/serial_connection.py +93 -88
- meerk40t/grbl/tcp_connection.py +81 -79
- meerk40t/grbl/ws_connection.py +112 -0
- meerk40t/gui/__init__.py +1 -1
- meerk40t/gui/about.py +2042 -296
- meerk40t/gui/alignment.py +1644 -1608
- meerk40t/gui/autoexec.py +199 -0
- meerk40t/gui/basicops.py +791 -670
- meerk40t/gui/bufferview.py +77 -71
- meerk40t/gui/busy.py +232 -133
- meerk40t/gui/choicepropertypanel.py +1662 -1469
- meerk40t/gui/consolepanel.py +706 -542
- meerk40t/gui/devicepanel.py +687 -581
- meerk40t/gui/dialogoptions.py +110 -107
- meerk40t/gui/executejob.py +316 -306
- meerk40t/gui/fonts.py +90 -90
- meerk40t/gui/functionwrapper.py +252 -0
- meerk40t/gui/gui_mixins.py +729 -0
- meerk40t/gui/guicolors.py +205 -182
- meerk40t/gui/help_assets/help_assets.py +218 -201
- meerk40t/gui/helper.py +154 -0
- meerk40t/gui/hersheymanager.py +1440 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +897 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2944 -0
- meerk40t/gui/materialtest.py +1722 -1694
- meerk40t/gui/mkdebug.py +646 -359
- meerk40t/gui/mwindow.py +163 -140
- meerk40t/gui/navigationpanels.py +2605 -2467
- meerk40t/gui/notes.py +143 -142
- meerk40t/gui/opassignment.py +414 -410
- meerk40t/gui/operation_info.py +310 -299
- meerk40t/gui/plugin.py +500 -328
- meerk40t/gui/position.py +714 -669
- meerk40t/gui/preferences.py +901 -650
- meerk40t/gui/propertypanels/attributes.py +1461 -1131
- meerk40t/gui/propertypanels/blobproperty.py +117 -114
- meerk40t/gui/propertypanels/consoleproperty.py +83 -80
- meerk40t/gui/propertypanels/gotoproperty.py +77 -0
- meerk40t/gui/propertypanels/groupproperties.py +223 -217
- meerk40t/gui/propertypanels/hatchproperty.py +489 -469
- meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
- meerk40t/gui/propertypanels/inputproperty.py +59 -58
- meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
- meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
- meerk40t/gui/propertypanels/outputproperty.py +59 -58
- meerk40t/gui/propertypanels/pathproperty.py +389 -380
- meerk40t/gui/propertypanels/placementproperty.py +1214 -383
- meerk40t/gui/propertypanels/pointproperty.py +140 -136
- meerk40t/gui/propertypanels/propertywindow.py +313 -181
- meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
- meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
- meerk40t/gui/propertypanels/textproperty.py +770 -755
- meerk40t/gui/propertypanels/waitproperty.py +56 -55
- meerk40t/gui/propertypanels/warpproperty.py +121 -0
- meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
- meerk40t/gui/ribbon.py +2471 -2210
- meerk40t/gui/scene/scene.py +1100 -1051
- meerk40t/gui/scene/sceneconst.py +22 -22
- meerk40t/gui/scene/scenepanel.py +439 -349
- meerk40t/gui/scene/scenespacewidget.py +365 -365
- meerk40t/gui/scene/widget.py +518 -505
- meerk40t/gui/scenewidgets/affinemover.py +215 -215
- meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
- meerk40t/gui/scenewidgets/bedwidget.py +120 -97
- meerk40t/gui/scenewidgets/elementswidget.py +137 -107
- meerk40t/gui/scenewidgets/gridwidget.py +785 -745
- meerk40t/gui/scenewidgets/guidewidget.py +765 -765
- meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
- meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
- meerk40t/gui/scenewidgets/nodeselector.py +28 -28
- meerk40t/gui/scenewidgets/rectselectwidget.py +592 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2958 -2756
- meerk40t/gui/simpleui.py +362 -333
- meerk40t/gui/simulation.py +2451 -2094
- meerk40t/gui/snapoptions.py +208 -203
- meerk40t/gui/spoolerpanel.py +1227 -1180
- meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
- meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
- meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
- meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
- meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
- meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
- meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
- meerk40t/gui/themes.py +200 -78
- meerk40t/gui/tips.py +590 -0
- meerk40t/gui/toolwidgets/circlebrush.py +35 -35
- meerk40t/gui/toolwidgets/toolcircle.py +248 -242
- meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
- meerk40t/gui/toolwidgets/tooldraw.py +97 -90
- meerk40t/gui/toolwidgets/toolellipse.py +219 -212
- meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
- meerk40t/gui/toolwidgets/toolline.py +39 -144
- meerk40t/gui/toolwidgets/toollinetext.py +79 -236
- meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
- meerk40t/gui/toolwidgets/toolmeasure.py +163 -216
- meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
- meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
- meerk40t/gui/toolwidgets/toolparameter.py +754 -668
- meerk40t/gui/toolwidgets/toolplacement.py +108 -108
- meerk40t/gui/toolwidgets/toolpoint.py +68 -59
- meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
- meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
- meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
- meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
- meerk40t/gui/toolwidgets/toolrect.py +211 -207
- meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
- meerk40t/gui/toolwidgets/toolribbon.py +598 -113
- meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
- meerk40t/gui/toolwidgets/tooltext.py +98 -89
- meerk40t/gui/toolwidgets/toolvector.py +213 -204
- meerk40t/gui/toolwidgets/toolwidget.py +39 -39
- meerk40t/gui/usbconnect.py +98 -91
- meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
- meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
- meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
- meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
- meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
- meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
- meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
- meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
- meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
- meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
- meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
- meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
- meerk40t/gui/wordlisteditor.py +985 -931
- meerk40t/gui/wxmeerk40t.py +1447 -1169
- meerk40t/gui/wxmmain.py +5644 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1631 -1453
- meerk40t/gui/wxmtree.py +2416 -2089
- meerk40t/gui/wxutils.py +1769 -1099
- meerk40t/gui/zmatrix.py +102 -102
- meerk40t/image/__init__.py +1 -1
- meerk40t/image/dither.py +429 -0
- meerk40t/image/imagetools.py +2793 -2269
- meerk40t/internal_plugins.py +150 -130
- meerk40t/kernel/__init__.py +63 -12
- meerk40t/kernel/channel.py +259 -212
- meerk40t/kernel/context.py +538 -538
- meerk40t/kernel/exceptions.py +41 -41
- meerk40t/kernel/functions.py +463 -414
- meerk40t/kernel/jobs.py +100 -100
- meerk40t/kernel/kernel.py +3828 -3571
- meerk40t/kernel/lifecycles.py +71 -71
- meerk40t/kernel/module.py +49 -49
- meerk40t/kernel/service.py +147 -147
- meerk40t/kernel/settings.py +383 -343
- meerk40t/lihuiyu/controller.py +883 -876
- meerk40t/lihuiyu/device.py +1181 -1069
- meerk40t/lihuiyu/driver.py +1466 -1372
- meerk40t/lihuiyu/gui/gui.py +127 -106
- meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
- meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
- meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
- meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
- meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
- meerk40t/lihuiyu/interpreter.py +53 -53
- meerk40t/lihuiyu/laserspeed.py +450 -450
- meerk40t/lihuiyu/loader.py +90 -90
- meerk40t/lihuiyu/parser.py +404 -404
- meerk40t/lihuiyu/plugin.py +101 -102
- meerk40t/lihuiyu/tcp_connection.py +111 -109
- meerk40t/main.py +231 -165
- meerk40t/moshi/builder.py +788 -781
- meerk40t/moshi/controller.py +505 -499
- meerk40t/moshi/device.py +495 -442
- meerk40t/moshi/driver.py +862 -696
- meerk40t/moshi/gui/gui.py +78 -76
- meerk40t/moshi/gui/moshicontrollergui.py +538 -522
- meerk40t/moshi/gui/moshidrivergui.py +87 -75
- meerk40t/moshi/plugin.py +43 -43
- meerk40t/network/console_server.py +140 -57
- meerk40t/network/kernelserver.py +10 -9
- meerk40t/network/tcp_server.py +142 -140
- meerk40t/network/udp_server.py +103 -77
- meerk40t/network/web_server.py +404 -0
- meerk40t/newly/controller.py +1158 -1144
- meerk40t/newly/device.py +874 -732
- meerk40t/newly/driver.py +540 -412
- meerk40t/newly/gui/gui.py +219 -188
- meerk40t/newly/gui/newlyconfig.py +116 -101
- meerk40t/newly/gui/newlycontroller.py +193 -186
- meerk40t/newly/gui/operationproperties.py +51 -51
- meerk40t/newly/mock_connection.py +82 -82
- meerk40t/newly/newly_params.py +56 -56
- meerk40t/newly/plugin.py +1214 -1246
- meerk40t/newly/usb_connection.py +322 -322
- meerk40t/rotary/gui/gui.py +52 -46
- meerk40t/rotary/gui/rotarysettings.py +240 -232
- meerk40t/rotary/rotary.py +202 -98
- meerk40t/ruida/control.py +291 -91
- meerk40t/ruida/controller.py +138 -1088
- meerk40t/ruida/device.py +676 -231
- meerk40t/ruida/driver.py +534 -472
- meerk40t/ruida/emulator.py +1494 -1491
- meerk40t/ruida/exceptions.py +4 -4
- meerk40t/ruida/gui/gui.py +71 -76
- meerk40t/ruida/gui/ruidaconfig.py +239 -72
- meerk40t/ruida/gui/ruidacontroller.py +187 -184
- meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
- meerk40t/ruida/loader.py +54 -52
- meerk40t/ruida/mock_connection.py +57 -109
- meerk40t/ruida/plugin.py +124 -87
- meerk40t/ruida/rdjob.py +2084 -945
- meerk40t/ruida/serial_connection.py +116 -0
- meerk40t/ruida/tcp_connection.py +146 -0
- meerk40t/ruida/udp_connection.py +73 -0
- meerk40t/svgelements.py +9671 -9669
- meerk40t/tools/driver_to_path.py +584 -579
- meerk40t/tools/geomstr.py +5583 -4680
- meerk40t/tools/jhfparser.py +357 -292
- meerk40t/tools/kerftest.py +904 -890
- meerk40t/tools/livinghinges.py +1168 -1033
- meerk40t/tools/pathtools.py +987 -949
- meerk40t/tools/pmatrix.py +234 -0
- meerk40t/tools/pointfinder.py +942 -942
- meerk40t/tools/polybool.py +941 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +1047 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +150 -139
- meerk40t-0.9.7020.dist-info/RECORD +446 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +1 -1
- meerk40t/balormk/elementlightjob.py +0 -159
- meerk40t-0.9.3001.dist-info/RECORD +0 -437
- test/bootstrap.py +0 -63
- test/test_cli.py +0 -12
- test/test_core_cutcode.py +0 -418
- test/test_core_elements.py +0 -144
- test/test_core_plotplanner.py +0 -397
- test/test_core_viewports.py +0 -312
- test/test_drivers_grbl.py +0 -108
- test/test_drivers_lihuiyu.py +0 -443
- test/test_drivers_newly.py +0 -113
- test/test_element_degenerate_points.py +0 -43
- test/test_elements_classify.py +0 -97
- test/test_elements_penbox.py +0 -22
- test/test_file_svg.py +0 -176
- test/test_fill.py +0 -155
- test/test_geomstr.py +0 -1523
- test/test_geomstr_nodes.py +0 -18
- test/test_imagetools_actualize.py +0 -306
- test/test_imagetools_wizard.py +0 -258
- test/test_kernel.py +0 -200
- test/test_laser_speeds.py +0 -3303
- test/test_length.py +0 -57
- test/test_lifecycle.py +0 -66
- test/test_operations.py +0 -251
- test/test_operations_hatch.py +0 -57
- test/test_ruida.py +0 -19
- test/test_spooler.py +0 -22
- test/test_tools_rasterplotter.py +0 -29
- test/test_wobble.py +0 -133
- test/test_zingl.py +0 -124
- {test → meerk40t/cylinder}/__init__.py +0 -0
- /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,2944 @@
|
|
1
|
+
"""
|
2
|
+
GUI to manage material library entries.
|
3
|
+
In essence a material library setting is a persistent list of operations.
|
4
|
+
They are stored in the operations.cfg file in the meerk40t working directory
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import xml.etree.ElementTree as ET
|
9
|
+
from platform import system
|
10
|
+
|
11
|
+
import wx
|
12
|
+
|
13
|
+
from meerk40t.core.node.node import Node
|
14
|
+
from meerk40t.gui.icons import (
|
15
|
+
icon_hatch,
|
16
|
+
icon_library,
|
17
|
+
icon_points,
|
18
|
+
icons8_caret_down,
|
19
|
+
icons8_caret_up,
|
20
|
+
icons8_console,
|
21
|
+
icons8_direction,
|
22
|
+
icons8_image,
|
23
|
+
icons8_laser_beam,
|
24
|
+
icons8_laserbeam_weak,
|
25
|
+
)
|
26
|
+
from meerk40t.gui.mwindow import MWindow
|
27
|
+
from meerk40t.gui.wxutils import (
|
28
|
+
EditableListCtrl,
|
29
|
+
ScrolledPanel,
|
30
|
+
StaticBoxSizer,
|
31
|
+
TextCtrl,
|
32
|
+
dip_size,
|
33
|
+
wxButton,
|
34
|
+
wxCheckBox,
|
35
|
+
wxComboBox,
|
36
|
+
wxStaticText,
|
37
|
+
wxTreeCtrl,
|
38
|
+
)
|
39
|
+
from meerk40t.kernel.settings import Settings
|
40
|
+
from meerk40t.svgelements import Color
|
41
|
+
|
42
|
+
_ = wx.GetTranslation
|
43
|
+
|
44
|
+
|
45
|
+
class ImportDialog(wx.Dialog):
|
46
|
+
def __init__(self, *args, context=None, filename=None, **kwds):
|
47
|
+
kwds["style"] = (
|
48
|
+
kwds.get("style", 0) | wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
|
49
|
+
)
|
50
|
+
wx.Dialog.__init__(self, *args, **kwds)
|
51
|
+
self.context = context
|
52
|
+
self.context.themes.set_window_colors(self)
|
53
|
+
self.txt_filename = TextCtrl(self, wx.ID_ANY)
|
54
|
+
self.btn_file = wxButton(self, wx.ID_ANY, "...")
|
55
|
+
self.check_consolidate = wxCheckBox(
|
56
|
+
self, wx.ID_ANY, _("Consolidate same thickness for material")
|
57
|
+
)
|
58
|
+
self.check_lens = wxCheckBox(self, wx.ID_ANY, _("Compensate Lens-Sizes"))
|
59
|
+
self.txt_lens_old = TextCtrl(self, wx.ID_ANY)
|
60
|
+
self.txt_lens_new = TextCtrl(self, wx.ID_ANY)
|
61
|
+
self.check_wattage = wxCheckBox(self, wx.ID_ANY, _("Compensate Power-Levels"))
|
62
|
+
self.txt_wattage_old = TextCtrl(self, wx.ID_ANY)
|
63
|
+
self.txt_wattage_new = TextCtrl(self, wx.ID_ANY)
|
64
|
+
self.btn_ok = wxButton(self, wx.ID_OK, _("OK"))
|
65
|
+
self.btn_cancel = wxButton(self, wx.ID_CANCEL, _("Cancel"))
|
66
|
+
|
67
|
+
self._define_layout()
|
68
|
+
self.validate(None)
|
69
|
+
self.on_check(None)
|
70
|
+
self.check_consolidate.SetValue(True)
|
71
|
+
self._define_logic()
|
72
|
+
if filename is not None:
|
73
|
+
self.txt_filename.SetValue(filename)
|
74
|
+
|
75
|
+
def _define_layout(self):
|
76
|
+
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
77
|
+
file_sizer = StaticBoxSizer(self, wx.ID_ANY, _("File to import"), wx.VERTICAL)
|
78
|
+
|
79
|
+
file_box = wx.BoxSizer(wx.HORIZONTAL)
|
80
|
+
|
81
|
+
file_box.Add(self.txt_filename, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
82
|
+
file_box.Add(self.btn_file, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
83
|
+
file_sizer.Add(file_box, 0, wx.EXPAND, 0)
|
84
|
+
|
85
|
+
file_sizer.Add(self.check_consolidate, 0, 0, 0)
|
86
|
+
|
87
|
+
main_sizer.Add(file_sizer, 0, wx.EXPAND, 0)
|
88
|
+
|
89
|
+
lens_sizer = StaticBoxSizer(
|
90
|
+
self, wx.ID_ANY, _("Different Lens-Size"), wx.VERTICAL
|
91
|
+
)
|
92
|
+
|
93
|
+
lens_param_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
94
|
+
label_old = wxStaticText(self, wx.ID_ANY, _("Old:"))
|
95
|
+
unit_old = wxStaticText(self, wx.ID_ANY, "mm")
|
96
|
+
label_new = wxStaticText(self, wx.ID_ANY, _("New:"))
|
97
|
+
unit_new = wxStaticText(self, wx.ID_ANY, "mm")
|
98
|
+
lens_param_sizer.Add(label_old, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
99
|
+
lens_param_sizer.Add(self.txt_lens_old, 0, 0, 0)
|
100
|
+
lens_param_sizer.Add(unit_old, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
101
|
+
|
102
|
+
lens_param_sizer.AddSpacer(25)
|
103
|
+
|
104
|
+
lens_param_sizer.Add(label_new, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
105
|
+
lens_param_sizer.Add(self.txt_lens_new, 0, 0, 0)
|
106
|
+
lens_param_sizer.Add(unit_new, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
107
|
+
|
108
|
+
lens_sizer.Add(self.check_lens, 0, 0, 0)
|
109
|
+
lens_sizer.Add(lens_param_sizer, 0, 0, 0)
|
110
|
+
main_sizer.Add(lens_sizer, 0, wx.EXPAND, 0)
|
111
|
+
|
112
|
+
wattage_sizer = StaticBoxSizer(
|
113
|
+
self, wx.ID_ANY, _("Different Laser-Power"), wx.VERTICAL
|
114
|
+
)
|
115
|
+
|
116
|
+
wattage_param_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
117
|
+
label_old = wxStaticText(self, wx.ID_ANY, _("Old:"))
|
118
|
+
unit_old = wxStaticText(self, wx.ID_ANY, "W")
|
119
|
+
label_new = wxStaticText(self, wx.ID_ANY, _("New:"))
|
120
|
+
unit_new = wxStaticText(self, wx.ID_ANY, "W")
|
121
|
+
wattage_param_sizer.Add(label_old, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
122
|
+
wattage_param_sizer.Add(self.txt_wattage_old, 0, 0, 0)
|
123
|
+
wattage_param_sizer.Add(unit_old, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
124
|
+
|
125
|
+
wattage_param_sizer.AddSpacer(25)
|
126
|
+
|
127
|
+
wattage_param_sizer.Add(label_new, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
128
|
+
wattage_param_sizer.Add(self.txt_wattage_new, 0, 0, 0)
|
129
|
+
wattage_param_sizer.Add(unit_new, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
130
|
+
|
131
|
+
wattage_sizer.Add(self.check_wattage, 0, 0, 0)
|
132
|
+
wattage_sizer.Add(wattage_param_sizer, 0, 0, 0)
|
133
|
+
main_sizer.Add(wattage_sizer, 0, wx.EXPAND, 0)
|
134
|
+
|
135
|
+
box_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
136
|
+
box_sizer.Add(self.btn_ok, 0, 0, 0)
|
137
|
+
box_sizer.Add(self.btn_cancel, 0, 0, 0)
|
138
|
+
main_sizer.Add(box_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
|
139
|
+
|
140
|
+
self.SetSizer(main_sizer)
|
141
|
+
self.Layout()
|
142
|
+
main_sizer.Fit(self)
|
143
|
+
self.txt_filename.SetToolTip(
|
144
|
+
_("Provide the full filename for material library")
|
145
|
+
)
|
146
|
+
self.btn_file.SetToolTip(_("Click to select files"))
|
147
|
+
self.check_consolidate.SetToolTip(
|
148
|
+
_("This will group entries with the same material description together")
|
149
|
+
)
|
150
|
+
self.check_lens.SetToolTip(
|
151
|
+
_(
|
152
|
+
"If active, power and speed values will be adjusted,\nto accommodate different lens-sizes"
|
153
|
+
)
|
154
|
+
)
|
155
|
+
self.check_wattage.SetToolTip(
|
156
|
+
_(
|
157
|
+
"If active, power and speed values will be adjusted,\nto accommodate different laser power"
|
158
|
+
)
|
159
|
+
)
|
160
|
+
|
161
|
+
def _define_logic(self):
|
162
|
+
self.Bind(wx.EVT_TEXT, self.validate, self.txt_filename)
|
163
|
+
self.Bind(wx.EVT_BUTTON, self.on_file, self.btn_file)
|
164
|
+
self.Bind(wx.EVT_CHECKBOX, self.on_check, self.check_lens)
|
165
|
+
self.Bind(wx.EVT_CHECKBOX, self.on_check, self.check_wattage)
|
166
|
+
|
167
|
+
def on_file(self, event):
|
168
|
+
mydlg = wx.FileDialog(
|
169
|
+
self,
|
170
|
+
message=_("Choose a library-file"),
|
171
|
+
wildcard="Supported files|*.lib;*.ini;*.clb;*.cfg|EZcad files (*.lib;*.ini)|*.lib;*.ini|Lightburn files (*.clb)|*.clb|MeerK40t operations (*.cfg)|*.cfg|All files (*.*)|*.*",
|
172
|
+
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_PREVIEW,
|
173
|
+
)
|
174
|
+
if mydlg.ShowModal() == wx.ID_OK:
|
175
|
+
# This returns a Python list of files that were selected.
|
176
|
+
self.txt_filename.SetValue(mydlg.GetPath())
|
177
|
+
self.validate()
|
178
|
+
mydlg.Destroy()
|
179
|
+
|
180
|
+
def on_check(self, event):
|
181
|
+
flag = self.check_lens.GetValue()
|
182
|
+
self.txt_lens_old.Enable(flag)
|
183
|
+
self.txt_lens_new.Enable(flag)
|
184
|
+
flag = self.check_wattage.GetValue()
|
185
|
+
self.txt_wattage_old.Enable(flag)
|
186
|
+
self.txt_wattage_new.Enable(flag)
|
187
|
+
|
188
|
+
def validate(self, *args):
|
189
|
+
flag = True
|
190
|
+
fname = self.txt_filename.GetValue()
|
191
|
+
if fname == "" or not os.path.exists(fname):
|
192
|
+
flag = False
|
193
|
+
if flag and fname.endswith(".clb"):
|
194
|
+
self.check_consolidate.Enable(True)
|
195
|
+
else:
|
196
|
+
self.check_consolidate.Enable(False)
|
197
|
+
|
198
|
+
self.btn_ok.Enable(flag)
|
199
|
+
|
200
|
+
def result(self):
|
201
|
+
old_lens = None
|
202
|
+
new_lens = None
|
203
|
+
factor_from_lens = 1.0
|
204
|
+
if self.check_lens.GetValue():
|
205
|
+
a_s = self.txt_lens_old.GetValue()
|
206
|
+
b_s = self.txt_lens_new.GetValue()
|
207
|
+
try:
|
208
|
+
a = float(a_s)
|
209
|
+
b = float(b_s)
|
210
|
+
if a != 0 and b != 0:
|
211
|
+
old_lens = a_s
|
212
|
+
new_lens = b_s
|
213
|
+
factor_from_lens = b / a
|
214
|
+
except ValueError:
|
215
|
+
pass
|
216
|
+
|
217
|
+
old_power = None
|
218
|
+
new_power = None
|
219
|
+
factor_from_power = 1.0
|
220
|
+
if self.check_wattage.GetValue():
|
221
|
+
a_s = self.txt_wattage_old.GetValue()
|
222
|
+
b_s = self.txt_wattage_new.GetValue()
|
223
|
+
try:
|
224
|
+
a = float(a_s)
|
225
|
+
b = float(b_s)
|
226
|
+
if a != 0 and b != 0:
|
227
|
+
old_power = a_s
|
228
|
+
new_power = b_s
|
229
|
+
factor_from_power = a / b
|
230
|
+
except ValueError:
|
231
|
+
pass
|
232
|
+
|
233
|
+
fname = self.txt_filename.GetValue()
|
234
|
+
if fname == "" or not os.path.exists(fname):
|
235
|
+
fname = None
|
236
|
+
|
237
|
+
consolidate = self.check_consolidate.GetValue()
|
238
|
+
if not self.check_consolidate.Enabled:
|
239
|
+
consolidate = False
|
240
|
+
factor = factor_from_lens * factor_from_power
|
241
|
+
info = (
|
242
|
+
fname,
|
243
|
+
old_lens,
|
244
|
+
new_lens,
|
245
|
+
old_power,
|
246
|
+
new_power,
|
247
|
+
factor_from_lens,
|
248
|
+
factor_from_power,
|
249
|
+
factor,
|
250
|
+
consolidate,
|
251
|
+
)
|
252
|
+
return info
|
253
|
+
|
254
|
+
|
255
|
+
class MaterialPanel(ScrolledPanel):
|
256
|
+
"""
|
257
|
+
Panel to modify material library settings.
|
258
|
+
In essence a material library setting is a persistent list of operations.
|
259
|
+
They are stored in the operations.cfg file in the meerk40t working directory
|
260
|
+
|
261
|
+
Internal development note:
|
262
|
+
I have tried the dataview TreeListCtrl to self.display_list the different entries:
|
263
|
+
this was crashing consistently, so I stopped following this path
|
264
|
+
"""
|
265
|
+
|
266
|
+
def __init__(self, *args, context=None, **kwds):
|
267
|
+
kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
|
268
|
+
ScrolledPanel.__init__(self, *args, **kwds)
|
269
|
+
self.context = context
|
270
|
+
self.context.themes.set_window_colors(self)
|
271
|
+
self.op_data = self.context.elements.op_data
|
272
|
+
self.SetHelpText("materialmanager")
|
273
|
+
self.parent_panel = None
|
274
|
+
self.current_item = None
|
275
|
+
self._active_material = None
|
276
|
+
self._active_operation = None
|
277
|
+
self.no_reload = False
|
278
|
+
self.share_ready = False
|
279
|
+
self.state_images = wx.ImageList()
|
280
|
+
# Categorisation
|
281
|
+
# 0 = Material (thickness), 1 = Lasertype (Material), 2 = Thickness (Material)
|
282
|
+
self.categorisation = 0
|
283
|
+
# Intentionally not translated, to allow data exchange
|
284
|
+
materials = [
|
285
|
+
"Plywood",
|
286
|
+
"Solid wood",
|
287
|
+
"Acrylic",
|
288
|
+
"Foam",
|
289
|
+
"Leather",
|
290
|
+
"Cardboard",
|
291
|
+
"Cork",
|
292
|
+
"Textiles",
|
293
|
+
"Slate",
|
294
|
+
"Paper",
|
295
|
+
"Aluminium",
|
296
|
+
"Steel",
|
297
|
+
"Copper",
|
298
|
+
"Silver",
|
299
|
+
"Gold",
|
300
|
+
"Zinc",
|
301
|
+
"Metal",
|
302
|
+
]
|
303
|
+
# Dictionary with key=Materialname, entry=Description (Name, Lasertype, entries)
|
304
|
+
self.material_list = dict()
|
305
|
+
self.operation_list = dict()
|
306
|
+
self.display_list = list()
|
307
|
+
self.deletion_methods = dict()
|
308
|
+
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
309
|
+
filter_box = StaticBoxSizer(
|
310
|
+
self, wx.ID_ANY, _("Filter Materials"), wx.HORIZONTAL
|
311
|
+
)
|
312
|
+
label_1 = wxStaticText(self, wx.ID_ANY, _("Material"))
|
313
|
+
filter_box.Add(label_1, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
314
|
+
|
315
|
+
self.txt_material = wxComboBox(
|
316
|
+
self, wx.ID_ANY, choices=materials, style=wx.CB_SORT
|
317
|
+
)
|
318
|
+
# self.txt_material = TextCtrl(self, wx.ID_ANY, "", limited=True)
|
319
|
+
|
320
|
+
filter_box.Add(self.txt_material, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
321
|
+
|
322
|
+
label_2 = wxStaticText(self, wx.ID_ANY, _("Thickness"))
|
323
|
+
filter_box.Add(label_2, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
324
|
+
|
325
|
+
self.txt_thickness = TextCtrl(self, wx.ID_ANY, "", limited=True)
|
326
|
+
filter_box.Add(self.txt_thickness, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
327
|
+
|
328
|
+
label_3 = wxStaticText(self, wx.ID_ANY, _("Laser"))
|
329
|
+
filter_box.Add(label_3, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
330
|
+
|
331
|
+
self.laser_choices = [
|
332
|
+
_("<All Lasertypes>"),
|
333
|
+
]
|
334
|
+
dev_infos = list(self.context.find("provider/friendly"))
|
335
|
+
# Gets a list of tuples (description, key, path)
|
336
|
+
dev_infos.sort(key=lambda e: e[0][1])
|
337
|
+
for e in dev_infos:
|
338
|
+
self.laser_choices.append(e[0][0])
|
339
|
+
|
340
|
+
self.combo_lasertype = wxComboBox(
|
341
|
+
self,
|
342
|
+
wx.ID_ANY,
|
343
|
+
choices=self.laser_choices,
|
344
|
+
style=wx.CB_DROPDOWN | wx.CB_READONLY,
|
345
|
+
)
|
346
|
+
self.combo_lasertype.SetMaxSize(dip_size(self, 110, -1))
|
347
|
+
|
348
|
+
filter_box.Add(self.combo_lasertype, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
349
|
+
|
350
|
+
self.btn_reset = wxButton(self, wx.ID_ANY, _("Reset Filter"))
|
351
|
+
filter_box.Add(self.btn_reset, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
352
|
+
main_sizer.Add(filter_box, 0, wx.EXPAND, 0)
|
353
|
+
result_box = StaticBoxSizer(
|
354
|
+
self, wx.ID_ANY, _("Matching library entries"), wx.VERTICAL
|
355
|
+
)
|
356
|
+
self.tree_library = wxTreeCtrl(
|
357
|
+
self,
|
358
|
+
wx.ID_ANY,
|
359
|
+
style=wx.BORDER_SUNKEN | wx.TR_HAS_BUTTONS
|
360
|
+
# | wx.TR_HIDE_ROOT
|
361
|
+
| wx.TR_ROW_LINES | wx.TR_SINGLE,
|
362
|
+
)
|
363
|
+
self.tree_library.SetToolTip(_("Click to select / Right click for actions"))
|
364
|
+
|
365
|
+
self.list_preview = EditableListCtrl(
|
366
|
+
self,
|
367
|
+
wx.ID_ANY,
|
368
|
+
style=wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES | wx.LC_SINGLE_SEL,
|
369
|
+
context=self.context, list_name="list_materialmanager"
|
370
|
+
)
|
371
|
+
|
372
|
+
self.list_preview.AppendColumn(_("#"), format=wx.LIST_FORMAT_LEFT, width=55)
|
373
|
+
self.list_preview.AppendColumn(
|
374
|
+
_("Operation"),
|
375
|
+
format=wx.LIST_FORMAT_LEFT,
|
376
|
+
width=60,
|
377
|
+
)
|
378
|
+
self.list_preview.AppendColumn(_("Id"), format=wx.LIST_FORMAT_LEFT, width=60)
|
379
|
+
self.list_preview.AppendColumn(
|
380
|
+
_("Label"), format=wx.LIST_FORMAT_LEFT, width=100
|
381
|
+
)
|
382
|
+
self.list_preview.AppendColumn(
|
383
|
+
_("Power") + " [ppi]", format=wx.LIST_FORMAT_LEFT, width=50
|
384
|
+
)
|
385
|
+
self.list_preview.AppendColumn(
|
386
|
+
_("Speed") + " [mm/s]", format=wx.LIST_FORMAT_LEFT, width=50
|
387
|
+
)
|
388
|
+
self.list_preview.AppendColumn(
|
389
|
+
_("Frequency") + " [kHz]", format=wx.LIST_FORMAT_LEFT, width=50
|
390
|
+
)
|
391
|
+
self.list_preview.AppendColumn(
|
392
|
+
_("Passes"), format=wx.LIST_FORMAT_LEFT, width=50
|
393
|
+
)
|
394
|
+
self.list_preview.resize_columns()
|
395
|
+
self.list_preview.SetToolTip(_("Click to select / Right click for actions"))
|
396
|
+
self.opinfo = {
|
397
|
+
"op cut": ("Cut", icons8_laser_beam),
|
398
|
+
"op raster": ("Raster", icons8_direction),
|
399
|
+
"op image": ("Image", icons8_image),
|
400
|
+
"op engrave": ("Engrave", icons8_laserbeam_weak),
|
401
|
+
"op dots": ("Dots", icon_points),
|
402
|
+
"op hatch": ("Hatch", icon_hatch),
|
403
|
+
"generic": ("Generic", icons8_console),
|
404
|
+
}
|
405
|
+
|
406
|
+
param_box = StaticBoxSizer(self, wx.ID_ANY, _("Information"), wx.VERTICAL)
|
407
|
+
|
408
|
+
box1 = wx.BoxSizer(wx.HORIZONTAL)
|
409
|
+
box2 = wx.BoxSizer(wx.HORIZONTAL)
|
410
|
+
box3 = wx.BoxSizer(wx.HORIZONTAL)
|
411
|
+
box4 = wx.BoxSizer(wx.HORIZONTAL)
|
412
|
+
self.box_minimal = wx.BoxSizer(wx.VERTICAL)
|
413
|
+
self.box_extended = wx.BoxSizer(wx.VERTICAL)
|
414
|
+
|
415
|
+
param_box.Add(self.box_minimal, 0, wx.EXPAND, 0)
|
416
|
+
param_box.Add(self.box_extended, 0, wx.EXPAND, 0)
|
417
|
+
|
418
|
+
self.box_minimal.Add(box1, 0, wx.EXPAND, 0)
|
419
|
+
self.box_minimal.Add(box2, 0, wx.EXPAND, 0)
|
420
|
+
self.box_extended.Add(box3, 0, wx.EXPAND, 0)
|
421
|
+
self.box_extended.Add(box4, 0, wx.EXPAND, 0)
|
422
|
+
|
423
|
+
def size_it(ctrl, minsize, maxsize):
|
424
|
+
ctrl.SetMinSize(dip_size(self, minsize, -1))
|
425
|
+
ctrl.SetMaxSize(dip_size(self, maxsize, -1))
|
426
|
+
|
427
|
+
label = wxStaticText(self, wx.ID_ANY, _("Title"))
|
428
|
+
# size_it(label, 60, 100)
|
429
|
+
box1.Add(label, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
430
|
+
self.txt_entry_title = TextCtrl(self, wx.ID_ANY, "")
|
431
|
+
box1.Add(self.txt_entry_title, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
432
|
+
|
433
|
+
self.btn_set = wxButton(self, wx.ID_ANY, _("Set"))
|
434
|
+
self.btn_set.SetToolTip(
|
435
|
+
_(
|
436
|
+
"Change the name / lasertype of the current entry\nRight-Click: assign lasertype to all visible entries"
|
437
|
+
)
|
438
|
+
)
|
439
|
+
|
440
|
+
box1.Add(self.btn_set, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
441
|
+
self.btn_expand = wxButton(
|
442
|
+
self,
|
443
|
+
wx.ID_ANY,
|
444
|
+
)
|
445
|
+
self.btn_expand.SetSize(dip_size(self, 25, 25))
|
446
|
+
self.btn_expand.SetMinSize(dip_size(self, 25, 25))
|
447
|
+
self.btn_expand.SetMaxSize(dip_size(self, 25, 25))
|
448
|
+
box1.Add(self.btn_expand, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
449
|
+
|
450
|
+
label = wxStaticText(self, wx.ID_ANY, _("Material"))
|
451
|
+
size_it(label, 60, 100)
|
452
|
+
box2.Add(label, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
453
|
+
# self.txt_entry_material = TextCtrl(self, wx.ID_ANY, "")
|
454
|
+
self.txt_entry_material = wxComboBox(
|
455
|
+
self, wx.ID_ANY, choices=materials, style=wx.CB_SORT
|
456
|
+
)
|
457
|
+
|
458
|
+
box2.Add(self.txt_entry_material, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
459
|
+
|
460
|
+
label = wxStaticText(self, wx.ID_ANY, _("Thickness"))
|
461
|
+
size_it(label, 60, 100)
|
462
|
+
box2.Add(label, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
463
|
+
self.txt_entry_thickness = TextCtrl(self, wx.ID_ANY, "", limited=True)
|
464
|
+
box2.Add(self.txt_entry_thickness, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
465
|
+
|
466
|
+
label = wxStaticText(self, wx.ID_ANY, _("Laser"))
|
467
|
+
size_it(label, 60, 100)
|
468
|
+
box3.Add(label, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
469
|
+
|
470
|
+
choices = self.laser_choices # [1:]
|
471
|
+
self.combo_entry_type = wxComboBox(
|
472
|
+
self, wx.ID_ANY, choices=choices, style=wx.CB_DROPDOWN | wx.CB_READONLY
|
473
|
+
)
|
474
|
+
self.combo_entry_type.SetMaxSize(dip_size(self, 110, -1))
|
475
|
+
|
476
|
+
box3.Add(self.combo_entry_type, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
477
|
+
box3.AddSpacer(20)
|
478
|
+
|
479
|
+
label = wxStaticText(self, wx.ID_ANY, _("Id"))
|
480
|
+
size_it(label, 60, 100)
|
481
|
+
box3.Add(label, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
482
|
+
self.txt_entry_section = TextCtrl(
|
483
|
+
self,
|
484
|
+
wx.ID_ANY,
|
485
|
+
"",
|
486
|
+
limited=True,
|
487
|
+
check="empty",
|
488
|
+
)
|
489
|
+
box3.Add(self.txt_entry_section, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
490
|
+
|
491
|
+
label = wxStaticText(self, wx.ID_ANY, _("Power"))
|
492
|
+
size_it(label, 60, 100)
|
493
|
+
box4.Add(label, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
494
|
+
self.txt_entry_power = TextCtrl(
|
495
|
+
self,
|
496
|
+
wx.ID_ANY,
|
497
|
+
"",
|
498
|
+
limited=True,
|
499
|
+
)
|
500
|
+
unit = wxStaticText(self, wx.ID_ANY, _("W"))
|
501
|
+
box4.Add(self.txt_entry_power, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
502
|
+
box4.Add(unit, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
503
|
+
box4.AddSpacer(20)
|
504
|
+
|
505
|
+
label = wxStaticText(self, wx.ID_ANY, _("Lens-Size"))
|
506
|
+
size_it(label, 60, 100)
|
507
|
+
box4.Add(label, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
508
|
+
self.txt_entry_lens = TextCtrl(
|
509
|
+
self,
|
510
|
+
wx.ID_ANY,
|
511
|
+
"",
|
512
|
+
limited=True,
|
513
|
+
)
|
514
|
+
unit = wxStaticText(self, wx.ID_ANY, _("mm"))
|
515
|
+
box4.Add(self.txt_entry_lens, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
516
|
+
box4.Add(unit, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
517
|
+
|
518
|
+
self.txt_entry_note = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE)
|
519
|
+
self.txt_entry_note.SetMinSize(dip_size(self, -1, 2 * 23))
|
520
|
+
self.box_extended.Add(self.txt_entry_note, 0, wx.EXPAND, 0)
|
521
|
+
|
522
|
+
result_box.Add(self.tree_library, 1, wx.EXPAND, 0)
|
523
|
+
result_box.Add(param_box, 0, wx.EXPAND, 0)
|
524
|
+
result_box.Add(self.list_preview, 1, wx.EXPAND, 0)
|
525
|
+
|
526
|
+
self.txt_material.SetToolTip(_("Filter entries with a certain title."))
|
527
|
+
self.txt_thickness.SetToolTip(
|
528
|
+
_("Filter entries with a certain material thickness.")
|
529
|
+
)
|
530
|
+
self.combo_lasertype.SetToolTip(_("Filter entries of a certain laser type"))
|
531
|
+
|
532
|
+
self.txt_entry_section.SetToolTip(_("Internal name of the library entry."))
|
533
|
+
self.txt_entry_material.SetToolTip(_("Name of the library entry."))
|
534
|
+
self.txt_entry_thickness.SetToolTip(_("Thickness of the material."))
|
535
|
+
self.combo_entry_type.SetToolTip(
|
536
|
+
_("Is this entry specific for a certain laser?")
|
537
|
+
)
|
538
|
+
self.txt_entry_note.SetToolTip(_("You can add additional information here."))
|
539
|
+
|
540
|
+
button_box = wx.BoxSizer(wx.VERTICAL)
|
541
|
+
|
542
|
+
self.btn_new = wxButton(self, wx.ID_ANY, _("Add new"))
|
543
|
+
self.btn_new.SetToolTip(_("Add a new library entry"))
|
544
|
+
self.btn_use_current = wxButton(self, wx.ID_ANY, _("Get current"))
|
545
|
+
self.btn_use_current.SetToolTip(_("Use the currently defined operations"))
|
546
|
+
self.btn_apply = wxButton(self, wx.ID_ANY, _("Load into Tree"))
|
547
|
+
self.btn_apply.SetToolTip(
|
548
|
+
_("Apply the current library entry to the operations branch")
|
549
|
+
)
|
550
|
+
self.btn_simple_apply = wxButton(self, wx.ID_ANY, _("Use for statusbar"))
|
551
|
+
self.btn_simple_apply.SetToolTip(
|
552
|
+
_("Use the current library entry for the statusbar icons")
|
553
|
+
)
|
554
|
+
self.btn_delete = wxButton(self, wx.ID_ANY, _("Delete"))
|
555
|
+
self.btn_delete.SetToolTip(_("Delete the current library entry"))
|
556
|
+
self.btn_duplicate = wxButton(self, wx.ID_ANY, _("Duplicate"))
|
557
|
+
self.btn_duplicate.SetToolTip(_("Duplicate the current library entry"))
|
558
|
+
self.btn_import = wxButton(self, wx.ID_ANY, _("Import"))
|
559
|
+
self.btn_import.SetToolTip(
|
560
|
+
_("Import a material library from ezcad or LightBurn")
|
561
|
+
)
|
562
|
+
self.btn_share = wxButton(self, wx.ID_ANY, _("Share"))
|
563
|
+
self.btn_share.SetToolTip(
|
564
|
+
_("Share the current library entry with the MeerK40t community")
|
565
|
+
)
|
566
|
+
|
567
|
+
button_box.Add(self.btn_new, 0, wx.EXPAND, 0)
|
568
|
+
button_box.Add(self.btn_use_current, 0, wx.EXPAND, 0)
|
569
|
+
button_box.Add(self.btn_apply, 0, wx.EXPAND, 0)
|
570
|
+
button_box.Add(self.btn_simple_apply, 0, wx.EXPAND, 0)
|
571
|
+
button_box.Add(self.btn_delete, 0, wx.EXPAND, 0)
|
572
|
+
button_box.Add(self.btn_duplicate, 0, wx.EXPAND, 0)
|
573
|
+
button_box.AddSpacer(self.btn_duplicate.Size[1])
|
574
|
+
button_box.Add(self.btn_import, 0, wx.EXPAND, 0)
|
575
|
+
button_box.Add(self.btn_share, 0, wx.EXPAND, 0)
|
576
|
+
outer_box = wx.BoxSizer(wx.HORIZONTAL)
|
577
|
+
outer_box.Add(result_box, 1, wx.EXPAND, 0)
|
578
|
+
outer_box.Add(button_box, 0, wx.EXPAND, 0)
|
579
|
+
main_sizer.Add(outer_box, 1, wx.EXPAND, 0)
|
580
|
+
|
581
|
+
self.SetSizer(main_sizer)
|
582
|
+
self.btn_reset.Bind(wx.EVT_BUTTON, self.on_reset)
|
583
|
+
self.combo_lasertype.Bind(wx.EVT_COMBOBOX, self.update_list)
|
584
|
+
self.txt_material.Bind(wx.EVT_TEXT, self.update_list)
|
585
|
+
self.txt_thickness.Bind(wx.EVT_TEXT, self.update_list)
|
586
|
+
self.btn_new.Bind(wx.EVT_BUTTON, self.on_new)
|
587
|
+
self.btn_use_current.Bind(wx.EVT_BUTTON, self.on_use_current)
|
588
|
+
self.btn_apply.Bind(wx.EVT_BUTTON, self.on_apply_tree)
|
589
|
+
self.btn_simple_apply.Bind(wx.EVT_BUTTON, self.on_apply_statusbar)
|
590
|
+
self.btn_delete.Bind(wx.EVT_BUTTON, self.on_delete)
|
591
|
+
self.btn_duplicate.Bind(wx.EVT_BUTTON, self.on_duplicate)
|
592
|
+
self.btn_import.Bind(wx.EVT_BUTTON, self.on_import)
|
593
|
+
self.btn_share.Bind(wx.EVT_BUTTON, self.on_share)
|
594
|
+
self.btn_expand.Bind(wx.EVT_BUTTON, self.toggle_extended)
|
595
|
+
self.btn_set.Bind(wx.EVT_BUTTON, self.update_entry)
|
596
|
+
self.btn_set.Bind(wx.EVT_RIGHT_DOWN, self.update_lasertype_for_all)
|
597
|
+
self.tree_library.Bind(wx.EVT_TREE_SEL_CHANGED, self.on_list_selection)
|
598
|
+
self.list_preview.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_preview_selection)
|
599
|
+
self.list_preview.Bind(
|
600
|
+
wx.EVT_LIST_BEGIN_LABEL_EDIT, self.before_operation_update
|
601
|
+
)
|
602
|
+
self.list_preview.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.on_operation_update)
|
603
|
+
|
604
|
+
self.tree_library.Bind(
|
605
|
+
wx.EVT_RIGHT_DOWN,
|
606
|
+
self.on_library_rightclick,
|
607
|
+
)
|
608
|
+
self.Bind(
|
609
|
+
wx.EVT_LIST_ITEM_RIGHT_CLICK, self.on_preview_rightclick, self.list_preview
|
610
|
+
)
|
611
|
+
self.Bind(
|
612
|
+
wx.EVT_LIST_COL_RIGHT_CLICK, self.on_preview_rightclick, self.list_preview
|
613
|
+
)
|
614
|
+
self.Bind(wx.EVT_SIZE, self.on_resize)
|
615
|
+
self.SetupScrolling()
|
616
|
+
# Hide not-yet-supported functions
|
617
|
+
self.btn_share.Show(self.share_ready)
|
618
|
+
self.active_material = None
|
619
|
+
self.expanded_info = False
|
620
|
+
self.Layout()
|
621
|
+
self.on_resize(None)
|
622
|
+
self.on_reset(None)
|
623
|
+
|
624
|
+
@property
|
625
|
+
def expanded_info(self):
|
626
|
+
return self._expanded_info
|
627
|
+
|
628
|
+
@expanded_info.setter
|
629
|
+
def expanded_info(self, newvalue):
|
630
|
+
self._expanded_info = newvalue
|
631
|
+
info = dip_size(self, 20, 20)
|
632
|
+
icon_size = info[0] * self.context.root.bitmap_correction_scale
|
633
|
+
if self._expanded_info:
|
634
|
+
self.btn_expand.SetBitmap(icons8_caret_up.GetBitmap(resize=icon_size))
|
635
|
+
self.btn_expand.SetToolTip(_("Click to hide extended infos"))
|
636
|
+
else:
|
637
|
+
self.btn_expand.SetBitmap(icons8_caret_down.GetBitmap(resize=icon_size))
|
638
|
+
self.btn_expand.SetToolTip(_("Click to show extended infos"))
|
639
|
+
self.box_extended.ShowItems(self._expanded_info)
|
640
|
+
self.Layout()
|
641
|
+
|
642
|
+
def toggle_extended(self, event):
|
643
|
+
self.expanded_info = not self.expanded_info
|
644
|
+
|
645
|
+
@property
|
646
|
+
def active_material(self):
|
647
|
+
return self._active_material
|
648
|
+
|
649
|
+
@active_material.setter
|
650
|
+
def active_material(self, newvalue):
|
651
|
+
self._active_material = newvalue
|
652
|
+
active = bool(newvalue is not None)
|
653
|
+
self.btn_apply.Enable(active)
|
654
|
+
self.btn_simple_apply.Enable(active)
|
655
|
+
self.btn_delete.Enable(active)
|
656
|
+
self.btn_duplicate.Enable(active)
|
657
|
+
self.txt_entry_section.Enable(active)
|
658
|
+
self.txt_entry_material.Enable(active)
|
659
|
+
self.txt_entry_lens.Enable(active)
|
660
|
+
self.txt_entry_power.Enable(active)
|
661
|
+
self.txt_entry_title.Enable(active)
|
662
|
+
self.txt_entry_thickness.Enable(active)
|
663
|
+
self.txt_entry_note.Enable(active)
|
664
|
+
self.combo_entry_type.Enable(active)
|
665
|
+
self.btn_set.Enable(active)
|
666
|
+
self.list_preview.Enable(active)
|
667
|
+
self.fill_preview()
|
668
|
+
|
669
|
+
@property
|
670
|
+
def is_balor(self):
|
671
|
+
if self.active_material is None:
|
672
|
+
return False
|
673
|
+
# a) laser-settings are set to fibre = 3
|
674
|
+
# b) laser-settings are set to general and we have a defined fibre laser
|
675
|
+
# Will be updated in fill_preview
|
676
|
+
return self._balor
|
677
|
+
|
678
|
+
def _add_deletion_method(self, level=0, keyprimary=None, primaryvalue=None, keysecondary=None, secondaryvalue=None)->int:
|
679
|
+
index = -1
|
680
|
+
while index in self.deletion_methods:
|
681
|
+
index -= 1
|
682
|
+
self.deletion_methods[index] = (level, keyprimary, primaryvalue, keysecondary, secondaryvalue)
|
683
|
+
return index
|
684
|
+
|
685
|
+
def retrieve_material_list(
|
686
|
+
self,
|
687
|
+
filtername=None,
|
688
|
+
filterlaser=None,
|
689
|
+
filterthickness=None,
|
690
|
+
reload=True,
|
691
|
+
setter=None,
|
692
|
+
):
|
693
|
+
if reload:
|
694
|
+
self.material_list.clear()
|
695
|
+
self.deletion_methods = dict()
|
696
|
+
for section in self.op_data.section_set():
|
697
|
+
if section == "previous":
|
698
|
+
continue
|
699
|
+
count = 0
|
700
|
+
secname = section
|
701
|
+
secdesc = ""
|
702
|
+
sectitle = ""
|
703
|
+
thick = ""
|
704
|
+
ltype = 0 # All lasers
|
705
|
+
note = ""
|
706
|
+
for subsection in self.op_data.derivable(secname):
|
707
|
+
if subsection.endswith(" info"):
|
708
|
+
secdesc = self.op_data.read_persistent(
|
709
|
+
str, subsection, "material", ""
|
710
|
+
)
|
711
|
+
sectitle = self.op_data.read_persistent(
|
712
|
+
str, subsection, "title", ""
|
713
|
+
)
|
714
|
+
thick = self.op_data.read_persistent(
|
715
|
+
str, subsection, "thickness", ""
|
716
|
+
)
|
717
|
+
ltype = self.op_data.read_persistent(
|
718
|
+
int, subsection, "laser", 0
|
719
|
+
)
|
720
|
+
note = self.op_data.read_persistent(str, subsection, "note", "")
|
721
|
+
else:
|
722
|
+
count += 1
|
723
|
+
if not sectitle:
|
724
|
+
sectitle = secname.replace("_", " ")
|
725
|
+
entry = {
|
726
|
+
"section": secname,
|
727
|
+
"material": secdesc,
|
728
|
+
"title": sectitle,
|
729
|
+
"laser": ltype,
|
730
|
+
"thickness": thick,
|
731
|
+
"note": note,
|
732
|
+
"opcount": count,
|
733
|
+
}
|
734
|
+
# entry = [secname, secdesc, count, ltype, thick, note]
|
735
|
+
self.material_list[secname] = entry
|
736
|
+
listidx = -1
|
737
|
+
self.display_list.clear()
|
738
|
+
display = []
|
739
|
+
|
740
|
+
if self.categorisation == 1:
|
741
|
+
# lasertype
|
742
|
+
sort_key_primary = "laser" # 3
|
743
|
+
sort_key_secondary = "material" # 1
|
744
|
+
sort_key_tertiary = "thickness" # 4
|
745
|
+
elif self.categorisation == 2:
|
746
|
+
# thickness
|
747
|
+
sort_key_primary = "thickness" # 4
|
748
|
+
sort_key_secondary = "material"
|
749
|
+
sort_key_tertiary = "laser" # 3
|
750
|
+
else:
|
751
|
+
# material
|
752
|
+
sort_key_primary = "material"
|
753
|
+
sort_key_secondary = "thickness" # 4
|
754
|
+
sort_key_tertiary = "laser" # 3
|
755
|
+
for key, entry in self.material_list.items():
|
756
|
+
listidx += 1
|
757
|
+
display.append((entry, listidx))
|
758
|
+
display.sort(
|
759
|
+
key=lambda e: (
|
760
|
+
e[0][sort_key_primary],
|
761
|
+
e[0][sort_key_secondary],
|
762
|
+
e[0][sort_key_tertiary],
|
763
|
+
)
|
764
|
+
)
|
765
|
+
|
766
|
+
busy = wx.BusyCursor()
|
767
|
+
tree = self.tree_library
|
768
|
+
tree.Freeze()
|
769
|
+
tree.DeleteAllItems()
|
770
|
+
tree_root = tree.AddRoot(_("Materials"))
|
771
|
+
# Save a delete all...
|
772
|
+
data_idx = self._add_deletion_method(0, None, None, None, None)
|
773
|
+
|
774
|
+
tree.SetItemData(tree_root, data_idx)
|
775
|
+
idx_primary = 0
|
776
|
+
idx_secondary = 0
|
777
|
+
newvalue = None
|
778
|
+
selected = None
|
779
|
+
first_item = None
|
780
|
+
selected_parent = None
|
781
|
+
last_category_primary = None
|
782
|
+
last_category_secondary = None
|
783
|
+
tree_primary = tree_root
|
784
|
+
tree_secondary = tree_root
|
785
|
+
visible_count = [0, 0] # All, subsections
|
786
|
+
for content in display:
|
787
|
+
entry = content[0]
|
788
|
+
listidx = content[1]
|
789
|
+
ltype = entry["laser"]
|
790
|
+
if ltype is None:
|
791
|
+
ltype = 0
|
792
|
+
if 0 <= ltype < len(self.laser_choices):
|
793
|
+
info = self.laser_choices[ltype]
|
794
|
+
else:
|
795
|
+
info = "???"
|
796
|
+
if sort_key_primary == "laser": # laser
|
797
|
+
this_category_primary = info
|
798
|
+
else:
|
799
|
+
this_category_primary = entry[sort_key_primary].replace("_", " ")
|
800
|
+
if sort_key_secondary == 3: # laser
|
801
|
+
this_category_secondary = info
|
802
|
+
else:
|
803
|
+
this_category_secondary = entry[sort_key_secondary].replace("_", " ")
|
804
|
+
if not this_category_primary:
|
805
|
+
# _("No laser")
|
806
|
+
# _("No material")
|
807
|
+
# _("No thickness")
|
808
|
+
this_category_primary = _("No " + sort_key_primary)
|
809
|
+
key = entry["section"]
|
810
|
+
if (
|
811
|
+
filtername is not None
|
812
|
+
and filtername.lower() not in entry["material"].lower()
|
813
|
+
):
|
814
|
+
continue
|
815
|
+
if filterthickness is not None and not entry[
|
816
|
+
"thickness"
|
817
|
+
].lower().startswith(filterthickness.lower()):
|
818
|
+
continue
|
819
|
+
if filterlaser is not None:
|
820
|
+
if filterlaser not in (0, entry["laser"]):
|
821
|
+
continue
|
822
|
+
self.display_list.append(entry)
|
823
|
+
visible_count[0] += 1
|
824
|
+
if last_category_primary != this_category_primary:
|
825
|
+
# New item
|
826
|
+
last_category_secondary = ""
|
827
|
+
idx_primary += 1
|
828
|
+
idx_secondary = 0
|
829
|
+
tree_primary = tree.AppendItem(tree_root, this_category_primary)
|
830
|
+
data_idx = self._add_deletion_method(1, sort_key_primary, this_category_primary, sort_key_secondary, "")
|
831
|
+
tree.SetItemData(tree_primary, data_idx)
|
832
|
+
|
833
|
+
tree_secondary = tree_primary
|
834
|
+
if last_category_secondary != this_category_secondary:
|
835
|
+
# new subitem
|
836
|
+
tree_secondary = tree.AppendItem(tree_primary, this_category_secondary)
|
837
|
+
data_idx = self._add_deletion_method(2, sort_key_primary, this_category_primary, sort_key_secondary, this_category_secondary)
|
838
|
+
tree.SetItemData(tree_secondary, data_idx)
|
839
|
+
visible_count[1] += 1
|
840
|
+
idx_secondary += 1
|
841
|
+
|
842
|
+
description = f"#{idx_primary}.{idx_secondary} - {entry['title']}, {entry['thickness']} ({info}, {entry['opcount']} ops)"
|
843
|
+
tree_id = tree.AppendItem(tree_secondary, description)
|
844
|
+
tree.SetItemData(tree_id, listidx)
|
845
|
+
if first_item is None:
|
846
|
+
first_item = tree_id
|
847
|
+
if key == setter:
|
848
|
+
newvalue = key
|
849
|
+
selected = tree_id
|
850
|
+
selected_parent = tree_primary
|
851
|
+
|
852
|
+
last_category_primary = this_category_primary
|
853
|
+
last_category_secondary = this_category_secondary
|
854
|
+
|
855
|
+
self.active_material = newvalue
|
856
|
+
tree.Expand(tree_root)
|
857
|
+
# if visible_count[0] <= 10:
|
858
|
+
# tree.ExpandAllChildren(tree_root)
|
859
|
+
if visible_count[1] == 1:
|
860
|
+
tree.ExpandAllChildren(tree_root)
|
861
|
+
elif visible_count[1] <= 10:
|
862
|
+
child, cookie = tree.GetFirstChild(tree_root)
|
863
|
+
while child.IsOk():
|
864
|
+
tree.Expand(child)
|
865
|
+
child, cookie = tree.GetNextChild(tree_root, cookie)
|
866
|
+
|
867
|
+
if selected is None:
|
868
|
+
if visible_count[0] == 1: # Just one, why don't we select it
|
869
|
+
self.tree_library.SelectItem(first_item)
|
870
|
+
else:
|
871
|
+
tree.ExpandAllChildren(selected_parent)
|
872
|
+
self.tree_library.SelectItem(selected)
|
873
|
+
tree.Thaw()
|
874
|
+
tree.Refresh()
|
875
|
+
del busy
|
876
|
+
|
877
|
+
@staticmethod
|
878
|
+
def get_nth_dict_entry(dictionary: dict, n=0):
|
879
|
+
if n < 0:
|
880
|
+
n += len(dictionary)
|
881
|
+
for i, key in enumerate(dictionary.keys()):
|
882
|
+
if i == n:
|
883
|
+
return key
|
884
|
+
return None
|
885
|
+
|
886
|
+
def get_nth_material(self, n=0):
|
887
|
+
return self.get_nth_dict_entry(self.material_list, n)
|
888
|
+
|
889
|
+
def get_nth_operation(self, n=0):
|
890
|
+
return self.get_nth_dict_entry(self.operation_list, n)
|
891
|
+
|
892
|
+
def on_share(self, event):
|
893
|
+
if self.active_material is None:
|
894
|
+
return
|
895
|
+
|
896
|
+
self.context.setting(str, "author", "")
|
897
|
+
last_author = self.context.author
|
898
|
+
if last_author is None:
|
899
|
+
last_author = ""
|
900
|
+
dlg = wx.TextEntryDialog(
|
901
|
+
self,
|
902
|
+
_(
|
903
|
+
"Thank you for your willingness to share your material setting with the MeerK40t community.\n"
|
904
|
+
+ "Please provide a name to honor your authorship."
|
905
|
+
),
|
906
|
+
caption=_("Share material setting"),
|
907
|
+
value=last_author,
|
908
|
+
)
|
909
|
+
dlg.SetValue(last_author)
|
910
|
+
res = dlg.ShowModal()
|
911
|
+
last_author = dlg.GetValue()
|
912
|
+
dlg.Destroy()
|
913
|
+
if res == wx.ID_CANCEL:
|
914
|
+
return
|
915
|
+
self.context.author = last_author
|
916
|
+
# We will store the relevant section in a separate file
|
917
|
+
oplist, opinfo = self.context.elements.load_persistent_op_list(
|
918
|
+
self.active_material,
|
919
|
+
use_settings=self.op_data,
|
920
|
+
)
|
921
|
+
if len(oplist) == 0:
|
922
|
+
return
|
923
|
+
opinfo["author"] = last_author
|
924
|
+
directory = self.context.kernel.os_information["WORKDIR"]
|
925
|
+
local_file = os.path.join(directory, "op_export.cfg")
|
926
|
+
if os.path.exists(local_file):
|
927
|
+
try:
|
928
|
+
os.remove(local_file)
|
929
|
+
except (OSError, PermissionError):
|
930
|
+
return
|
931
|
+
settings = Settings(
|
932
|
+
self.context.kernel.name, "op_export.cfg", ignore_settings=False
|
933
|
+
)
|
934
|
+
opsection = f"{self.active_material} info"
|
935
|
+
for key, value in opinfo.items():
|
936
|
+
settings.write_persistent(opsection, key, value)
|
937
|
+
|
938
|
+
def _save_tree(name, op_list):
|
939
|
+
for i, op in enumerate(op_list):
|
940
|
+
if hasattr(op, "allow_save"):
|
941
|
+
if not op.allow_save():
|
942
|
+
continue
|
943
|
+
if op.type == "reference":
|
944
|
+
# We do not save references.
|
945
|
+
continue
|
946
|
+
section = f"{name} {i:06d}"
|
947
|
+
settings.write_persistent(section, "type", op.type)
|
948
|
+
op.save(settings, section)
|
949
|
+
try:
|
950
|
+
_save_tree(section, op.children)
|
951
|
+
except AttributeError:
|
952
|
+
pass
|
953
|
+
|
954
|
+
_save_tree(opsection, oplist)
|
955
|
+
settings.write_configuration()
|
956
|
+
# print ("Sharing")
|
957
|
+
try:
|
958
|
+
with open(local_file, "r") as f:
|
959
|
+
data = f.read()
|
960
|
+
except (OSError, RuntimeError, PermissionError, FileNotFoundError):
|
961
|
+
return
|
962
|
+
self.send_data_to_community(local_file, data)
|
963
|
+
|
964
|
+
def send_data_to_community(self, filename, data):
|
965
|
+
"""
|
966
|
+
Sends the material setting to a server using rfc1341 7.2 The multipart Content-Type
|
967
|
+
https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
|
968
|
+
|
969
|
+
@param filename: filename to use when sending file
|
970
|
+
@param data: data to send
|
971
|
+
@return:
|
972
|
+
"""
|
973
|
+
import socket
|
974
|
+
|
975
|
+
MEERK40T_HOST = "dev.meerk40t.com"
|
976
|
+
|
977
|
+
host = MEERK40T_HOST # Replace with the actual host
|
978
|
+
port = 80 # Replace with the actual port
|
979
|
+
|
980
|
+
# Construct the HTTP request
|
981
|
+
boundary = "----------------meerk40t-material"
|
982
|
+
body = (
|
983
|
+
f"--{boundary}\r\n"
|
984
|
+
f'Content-Disposition: form-data; name="file"; filename="{filename}"\r\n'
|
985
|
+
f"Content-Type: text/plain\r\n"
|
986
|
+
"\r\n"
|
987
|
+
f"{data}\r\n"
|
988
|
+
f"--{boundary}--\r\n"
|
989
|
+
)
|
990
|
+
|
991
|
+
headers = (
|
992
|
+
f"POST /upload HTTP/1.1\r\n"
|
993
|
+
f"Host: {host}\r\n"
|
994
|
+
"User-Agent: meerk40t/1.0.0\r\n"
|
995
|
+
f"Content-Type: multipart/form-data; boundary={boundary}\r\n"
|
996
|
+
f"Content-Length: {len(body)}\r\n"
|
997
|
+
"\r\n"
|
998
|
+
)
|
999
|
+
|
1000
|
+
try:
|
1001
|
+
# Create a socket connection
|
1002
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
|
1003
|
+
client_socket.connect((host, port))
|
1004
|
+
|
1005
|
+
# Send the request
|
1006
|
+
request = f"{headers}{body}"
|
1007
|
+
client_socket.sendall(request.encode())
|
1008
|
+
|
1009
|
+
# Receive and print the response
|
1010
|
+
response = client_socket.recv(4096)
|
1011
|
+
response = response.decode("utf-8", errors="ignore")
|
1012
|
+
except Exception:
|
1013
|
+
response = ""
|
1014
|
+
|
1015
|
+
response_lines = response.split("\n")
|
1016
|
+
http_code = response_lines[0]
|
1017
|
+
|
1018
|
+
# print(response)
|
1019
|
+
|
1020
|
+
if http_code.startswith("HTTP/1.1 200 OK"):
|
1021
|
+
message = response_lines[-1]
|
1022
|
+
dlg = wx.MessageDialog(
|
1023
|
+
None,
|
1024
|
+
_("We got your file. Thank you for helping\n\n") + message,
|
1025
|
+
_("Thanks"),
|
1026
|
+
wx.OK,
|
1027
|
+
)
|
1028
|
+
dlg.ShowModal()
|
1029
|
+
dlg.Destroy()
|
1030
|
+
else:
|
1031
|
+
# print(response)
|
1032
|
+
dlg = wx.MessageDialog(
|
1033
|
+
None,
|
1034
|
+
_("We're sorry, that didn't work.\n\n") + "\n\n" + str(http_code),
|
1035
|
+
_("Thanks"),
|
1036
|
+
wx.OK,
|
1037
|
+
)
|
1038
|
+
dlg.ShowModal()
|
1039
|
+
dlg.Destroy()
|
1040
|
+
|
1041
|
+
def on_duplicate(self, event):
|
1042
|
+
if self.active_material is None:
|
1043
|
+
return
|
1044
|
+
op_list, op_info = self.context.elements.load_persistent_op_list(
|
1045
|
+
self.active_material,
|
1046
|
+
use_settings=self.op_data,
|
1047
|
+
)
|
1048
|
+
if len(op_list) == 0:
|
1049
|
+
return
|
1050
|
+
oldsection = self.active_material
|
1051
|
+
if oldsection.endswith(")"):
|
1052
|
+
idx = oldsection.rfind("(")
|
1053
|
+
if idx >= 0:
|
1054
|
+
oldsection = oldsection[:idx]
|
1055
|
+
if oldsection.endswith("_"):
|
1056
|
+
oldsection = oldsection[:-1]
|
1057
|
+
|
1058
|
+
counter = 0
|
1059
|
+
while True:
|
1060
|
+
counter += 1
|
1061
|
+
newsection = f"{oldsection}_({counter})"
|
1062
|
+
if newsection not in self.material_list:
|
1063
|
+
break
|
1064
|
+
|
1065
|
+
oldname = oldsection
|
1066
|
+
if "title" in op_info:
|
1067
|
+
oldname = op_info["title"]
|
1068
|
+
if oldname.endswith(")"):
|
1069
|
+
idx = oldname.rfind("(")
|
1070
|
+
if idx >= 0:
|
1071
|
+
oldname = oldname[:idx]
|
1072
|
+
newname = f"{oldname} ({counter})"
|
1073
|
+
op_info["title"] = newname
|
1074
|
+
self.context.elements.save_persistent_operations_list(
|
1075
|
+
newsection,
|
1076
|
+
oplist=op_list,
|
1077
|
+
opinfo=op_info,
|
1078
|
+
inform=False,
|
1079
|
+
use_settings=self.op_data,
|
1080
|
+
)
|
1081
|
+
self.op_data.write_configuration()
|
1082
|
+
self.retrieve_material_list(reload=True, setter=newsection)
|
1083
|
+
|
1084
|
+
def on_delete(self, event):
|
1085
|
+
if self.active_material is None:
|
1086
|
+
return
|
1087
|
+
if self.context.kernel.yesno(
|
1088
|
+
_("Do you really want to delete this entry? This can't be undone.")
|
1089
|
+
):
|
1090
|
+
self.context.elements.clear_persistent_operations(
|
1091
|
+
self.active_material,
|
1092
|
+
use_settings=self.op_data,
|
1093
|
+
)
|
1094
|
+
self.op_data.write_configuration()
|
1095
|
+
self.retrieve_material_list(reload=True)
|
1096
|
+
|
1097
|
+
def on_delete_all(self, event):
|
1098
|
+
self._delete_according_to_key(keytype=0, primary="", secondary="")
|
1099
|
+
|
1100
|
+
def on_delete_category(self, keytype:int, primary:any, secondary:any):
|
1101
|
+
def handler(event):
|
1102
|
+
self._delete_according_to_key(keytype=keytype, primary=primary, secondary=secondary)
|
1103
|
+
return handler
|
1104
|
+
|
1105
|
+
def _delete_according_to_key(self, keytype: int, primary:str, secondary:str):
|
1106
|
+
if self.categorisation == 1:
|
1107
|
+
# lasertype
|
1108
|
+
sort_key_primary = "laser" # 3
|
1109
|
+
sort_key_secondary = "material" # 1
|
1110
|
+
elif self.categorisation == 2:
|
1111
|
+
# thickness
|
1112
|
+
sort_key_primary = "thickness" # 4
|
1113
|
+
sort_key_secondary = "material"
|
1114
|
+
else:
|
1115
|
+
# material
|
1116
|
+
sort_key_primary = "material"
|
1117
|
+
sort_key_secondary = "thickness" # 4
|
1118
|
+
# Establish the amount of to be deleted entries
|
1119
|
+
amount = 0
|
1120
|
+
for entry in self.display_list:
|
1121
|
+
to_delete = False
|
1122
|
+
if keytype == 0:
|
1123
|
+
to_delete = True
|
1124
|
+
elif (
|
1125
|
+
keytype == 1 and
|
1126
|
+
entry[sort_key_primary].replace("_", " ") == primary
|
1127
|
+
):
|
1128
|
+
to_delete = True
|
1129
|
+
elif (
|
1130
|
+
keytype == 2 and
|
1131
|
+
entry[sort_key_primary].replace("_", " ") == primary and
|
1132
|
+
entry[sort_key_secondary].replace("_", " ") == secondary
|
1133
|
+
):
|
1134
|
+
to_delete = True
|
1135
|
+
if to_delete:
|
1136
|
+
amount += 1
|
1137
|
+
|
1138
|
+
if keytype == 0:
|
1139
|
+
question = _("Do you really want to delete all {num} visible entries? This can't be undone.").format(num=str(amount))
|
1140
|
+
else:
|
1141
|
+
criteria = f"{sort_key_primary}={'<empty>' if primary is None else primary}"
|
1142
|
+
if secondary is not None:
|
1143
|
+
criteria = criteria + f" & {sort_key_secondary}='{secondary}'"
|
1144
|
+
question = _("Do you really want to delete all {num} entries with {data}? This can't be undone.").format(data=criteria, num=str(amount))
|
1145
|
+
if self.context.kernel.yesno(question):
|
1146
|
+
busy = self.context.kernel.busyinfo
|
1147
|
+
busy.start(msg=_("Deleting data"))
|
1148
|
+
for idx, entry in enumerate(self.display_list):
|
1149
|
+
busy.change(msg=f"{idx+1}/{len(self.display_list)}", keep=1)
|
1150
|
+
|
1151
|
+
to_delete = False
|
1152
|
+
prim_key = entry[sort_key_primary].replace("_", " ") if entry[sort_key_primary] else _("No " + sort_key_primary)
|
1153
|
+
if keytype == 0:
|
1154
|
+
to_delete = True
|
1155
|
+
elif (
|
1156
|
+
keytype == 1 and
|
1157
|
+
prim_key == primary
|
1158
|
+
):
|
1159
|
+
to_delete = True
|
1160
|
+
elif (
|
1161
|
+
keytype == 2 and
|
1162
|
+
prim_key == primary and
|
1163
|
+
prim_key == secondary
|
1164
|
+
):
|
1165
|
+
to_delete = True
|
1166
|
+
|
1167
|
+
# print (f"Keytype={keytype}, primary: {prim_key} vs {primary}, secondary: {entry[sort_key_secondary].replace('_', ' ')} vs {secondary} -> {to_delete}")
|
1168
|
+
|
1169
|
+
if to_delete:
|
1170
|
+
material = entry["section"]
|
1171
|
+
self.context.elements.clear_persistent_operations(
|
1172
|
+
material, use_settings=self.op_data, flush=False
|
1173
|
+
)
|
1174
|
+
self.op_data.write_configuration()
|
1175
|
+
busy.end()
|
1176
|
+
self.on_reset(None)
|
1177
|
+
|
1178
|
+
|
1179
|
+
def invalid_file(self, filename):
|
1180
|
+
dlg = wx.MessageDialog(
|
1181
|
+
self,
|
1182
|
+
_("Unrecognized format in file {info}".format(info=filename)),
|
1183
|
+
_("Invalid file"),
|
1184
|
+
wx.OK | wx.ICON_WARNING,
|
1185
|
+
)
|
1186
|
+
dlg.ShowModal()
|
1187
|
+
dlg.Destroy()
|
1188
|
+
|
1189
|
+
def import_lightburn(self, info):
|
1190
|
+
# info = (fname, old_lens, new_lens, old_power, new_power, factor_from_lens, factor_from_power, factor, consolidate)
|
1191
|
+
filename = info[0]
|
1192
|
+
factor = info[7]
|
1193
|
+
join_entries = info[8]
|
1194
|
+
lens_info = info[2]
|
1195
|
+
if lens_info is None:
|
1196
|
+
lens_info = ""
|
1197
|
+
power_info = info[4]
|
1198
|
+
if power_info is None:
|
1199
|
+
power_info = ""
|
1200
|
+
|
1201
|
+
if not os.path.exists(filename):
|
1202
|
+
return False
|
1203
|
+
added = False
|
1204
|
+
try:
|
1205
|
+
tree = ET.parse(filename)
|
1206
|
+
except ET.ParseError:
|
1207
|
+
self.invalid_file(filename)
|
1208
|
+
return False
|
1209
|
+
|
1210
|
+
root = tree.getroot()
|
1211
|
+
if root.tag.lower() != "lightburnlibrary":
|
1212
|
+
self.invalid_file(filename)
|
1213
|
+
return False
|
1214
|
+
|
1215
|
+
# We need to have a new id...
|
1216
|
+
new_import_id = 0
|
1217
|
+
pattern = "import_"
|
1218
|
+
for section in self.op_data.section_set():
|
1219
|
+
if section.startswith(pattern):
|
1220
|
+
s = section[len(pattern) :]
|
1221
|
+
try:
|
1222
|
+
number = int(s)
|
1223
|
+
except ValueError:
|
1224
|
+
number = 0
|
1225
|
+
if number > new_import_id:
|
1226
|
+
new_import_id = number
|
1227
|
+
|
1228
|
+
# def traverse(node):
|
1229
|
+
# print(f"Node: {node.tag}")
|
1230
|
+
# for attr in node.attrib:
|
1231
|
+
# print(f"Attribute: {attr} = {node.attrib[attr]}")
|
1232
|
+
# for child in node:
|
1233
|
+
# traverse(child)
|
1234
|
+
# traverse(root)
|
1235
|
+
operation_ids = dict()
|
1236
|
+
|
1237
|
+
for material_node in root:
|
1238
|
+
material = material_node.attrib["name"]
|
1239
|
+
last_thickness = None
|
1240
|
+
for entry_node in material_node:
|
1241
|
+
thickness = entry_node.attrib.get("Thickness", "-1")
|
1242
|
+
try:
|
1243
|
+
thickness_value = float(thickness)
|
1244
|
+
except ValueError:
|
1245
|
+
thickness_value = -1
|
1246
|
+
if thickness_value < 0:
|
1247
|
+
thickness = ""
|
1248
|
+
desc = entry_node.attrib.get("Desc", "")
|
1249
|
+
title = entry_node.attrib.get("NoThickTitle", "")
|
1250
|
+
label = desc
|
1251
|
+
if last_thickness == thickness and join_entries:
|
1252
|
+
# We keep those together
|
1253
|
+
pass
|
1254
|
+
else:
|
1255
|
+
operation_ids.clear()
|
1256
|
+
operation_ids["op engrave"] = ["E", 0]
|
1257
|
+
operation_ids["op raster"] = ["R", 0]
|
1258
|
+
operation_ids["op cut"] = ["C", 0]
|
1259
|
+
operation_ids["op image"] = ["I", 0]
|
1260
|
+
new_import_id += 1
|
1261
|
+
sect_num = -1
|
1262
|
+
sect = f"{pattern}{new_import_id:0>4}"
|
1263
|
+
info_section_name = f"{sect} info"
|
1264
|
+
self.op_data.write_persistent(info_section_name, "title", title)
|
1265
|
+
self.op_data.write_persistent(
|
1266
|
+
info_section_name, "material", material
|
1267
|
+
)
|
1268
|
+
self.op_data.write_persistent(info_section_name, "laser", 0)
|
1269
|
+
self.op_data.write_persistent(
|
1270
|
+
info_section_name, "thickness", thickness
|
1271
|
+
)
|
1272
|
+
self.op_data.write_persistent(
|
1273
|
+
info_section_name, "power", power_info
|
1274
|
+
)
|
1275
|
+
self.op_data.write_persistent(info_section_name, "lens", lens_info)
|
1276
|
+
note = label
|
1277
|
+
last_thickness = thickness
|
1278
|
+
added = True
|
1279
|
+
for cutsetting_node in entry_node:
|
1280
|
+
powerval = None
|
1281
|
+
speedval = None
|
1282
|
+
sect_num += 1
|
1283
|
+
section_name = f"{sect} {sect_num:0>6}"
|
1284
|
+
cut_type = cutsetting_node.attrib.get("type", "Scan")
|
1285
|
+
if cut_type.lower() == "cut":
|
1286
|
+
op_type = "op engrave"
|
1287
|
+
elif cut_type.lower() == "scan":
|
1288
|
+
op_type = "op raster"
|
1289
|
+
elif cut_type.lower() == "image":
|
1290
|
+
op_type = "op image"
|
1291
|
+
else:
|
1292
|
+
op_type = "op engrave"
|
1293
|
+
if op_type in operation_ids:
|
1294
|
+
operation_ids[op_type][1] += 1
|
1295
|
+
else:
|
1296
|
+
operation_ids[op_type] = [op_type[3].upper(), 1]
|
1297
|
+
|
1298
|
+
self.op_data.write_persistent(section_name, "type", op_type)
|
1299
|
+
self.op_data.write_persistent(
|
1300
|
+
section_name,
|
1301
|
+
"id",
|
1302
|
+
f"{operation_ids[op_type][0]}{operation_ids[op_type][1]}",
|
1303
|
+
)
|
1304
|
+
self.op_data.write_persistent(section_name, "label", label)
|
1305
|
+
|
1306
|
+
numeric_value = 0
|
1307
|
+
for param_node in cutsetting_node:
|
1308
|
+
param = param_node.tag.lower()
|
1309
|
+
value = param_node.attrib.get("Value", "")
|
1310
|
+
if not value:
|
1311
|
+
continue
|
1312
|
+
try:
|
1313
|
+
numeric_value = float(value)
|
1314
|
+
except ValueError:
|
1315
|
+
continue
|
1316
|
+
|
1317
|
+
if param == "numpasses":
|
1318
|
+
if numeric_value != 0:
|
1319
|
+
self.op_data.write_persistent(
|
1320
|
+
section_name, "passes", numeric_value
|
1321
|
+
)
|
1322
|
+
self.op_data.write_persistent(
|
1323
|
+
section_name, "passes_custom", True
|
1324
|
+
)
|
1325
|
+
elif param == "speed":
|
1326
|
+
if numeric_value != 0:
|
1327
|
+
speedval = numeric_value
|
1328
|
+
elif param == "maxpower":
|
1329
|
+
if numeric_value != 0:
|
1330
|
+
powerval = numeric_value * 10
|
1331
|
+
elif param == "frequency":
|
1332
|
+
# khz
|
1333
|
+
if numeric_value != 0:
|
1334
|
+
self.op_data.write_persistent(
|
1335
|
+
section_name, "frequency", numeric_value / 1000.0
|
1336
|
+
)
|
1337
|
+
elif param == "jumpspeed":
|
1338
|
+
if numeric_value != 0:
|
1339
|
+
self.op_data.write_persistent(
|
1340
|
+
section_name, "rapid_enabled", True
|
1341
|
+
)
|
1342
|
+
self.op_data.write_persistent(
|
1343
|
+
section_name, "rapid_speed", numeric_value
|
1344
|
+
)
|
1345
|
+
else:
|
1346
|
+
# note += f"\\n{param} = {numeric_value}"
|
1347
|
+
pass
|
1348
|
+
# Ready, let's write power and speed
|
1349
|
+
if factor != 1:
|
1350
|
+
old_l = info[1]
|
1351
|
+
new_l = info[2]
|
1352
|
+
factor_l = info[5]
|
1353
|
+
if old_l is not None:
|
1354
|
+
note += f"\\n({cut_type}) Converted lens-size {old_l}mm -> {new_l}mm: {factor_l:.2}"
|
1355
|
+
old_l = info[3]
|
1356
|
+
new_l = info[4]
|
1357
|
+
factor_l = info[6]
|
1358
|
+
if old_l is not None:
|
1359
|
+
note += f"\\n({cut_type}) Converted power {old_l}W -> {new_l}W: {factor_l:.2}"
|
1360
|
+
if powerval * factor > 1000:
|
1361
|
+
# Too much, let's reduce speed instead
|
1362
|
+
if speedval:
|
1363
|
+
note += f"\\n({cut_type}) Needed to reduce speed {speedval:.1}mm/s -> {speedval / factor:.2}mm/s"
|
1364
|
+
speedval *= 1 / factor
|
1365
|
+
else:
|
1366
|
+
powerval *= factor
|
1367
|
+
self.op_data.write_persistent(section_name, "speed", speedval)
|
1368
|
+
self.op_data.write_persistent(section_name, "power", powerval)
|
1369
|
+
self.op_data.write_persistent(info_section_name, "note", note)
|
1370
|
+
|
1371
|
+
return added
|
1372
|
+
|
1373
|
+
def import_meerk40t(self, info):
|
1374
|
+
filename = info[0]
|
1375
|
+
factor = info[7]
|
1376
|
+
lens_info = info[2]
|
1377
|
+
if lens_info is None:
|
1378
|
+
lens_info = ""
|
1379
|
+
power_info = info[4]
|
1380
|
+
if power_info is None:
|
1381
|
+
power_info = ""
|
1382
|
+
if not os.path.exists(filename):
|
1383
|
+
return False
|
1384
|
+
elems = self.context.elements
|
1385
|
+
settings = Settings(None, filename, create_backup=False)
|
1386
|
+
added = False
|
1387
|
+
|
1388
|
+
# Load operation list from file and adjust power/speed if needed
|
1389
|
+
for section in settings.section_set():
|
1390
|
+
if section == "previous":
|
1391
|
+
continue
|
1392
|
+
target_section = section
|
1393
|
+
idx = 0
|
1394
|
+
ex_list = list(self.op_data.section_set())
|
1395
|
+
while target_section in ex_list:
|
1396
|
+
idx += 1
|
1397
|
+
target_section = f"{section}-{idx}"
|
1398
|
+
# Remember existing ids in operations....
|
1399
|
+
uid = {}
|
1400
|
+
oplist, opinfo = elems.load_persistent_op_list(
|
1401
|
+
section, use_settings=settings
|
1402
|
+
)
|
1403
|
+
note = ""
|
1404
|
+
for op in oplist:
|
1405
|
+
added = True
|
1406
|
+
powerval = 1000.0
|
1407
|
+
speedval = 10.0
|
1408
|
+
if hasattr(op, "power") and op.power is not None:
|
1409
|
+
try:
|
1410
|
+
powerval = float(op.power)
|
1411
|
+
except ValueError as e:
|
1412
|
+
if str(op.power).endswith("%"):
|
1413
|
+
try:
|
1414
|
+
powerval = 10.0 * float(str(op.power)[:-1])
|
1415
|
+
except ValueError:
|
1416
|
+
pass
|
1417
|
+
if hasattr(op, "speed") and op.speed is not None:
|
1418
|
+
try:
|
1419
|
+
speedval = float(op.speed)
|
1420
|
+
except ValueError:
|
1421
|
+
pass
|
1422
|
+
if factor != 1:
|
1423
|
+
old_l = info[1]
|
1424
|
+
new_l = info[2]
|
1425
|
+
factor_l = info[5]
|
1426
|
+
if old_l is not None:
|
1427
|
+
note += f"\\nConverted lens-size {old_l}mm -> {new_l}mm: {factor_l:.2}"
|
1428
|
+
old_l = info[3]
|
1429
|
+
new_l = info[4]
|
1430
|
+
factor_l = info[6]
|
1431
|
+
if old_l is not None:
|
1432
|
+
note += (
|
1433
|
+
f"\\nConverted power {old_l}W -> {new_l}W: {factor_l:.2}"
|
1434
|
+
)
|
1435
|
+
if powerval and powerval * factor > 1000:
|
1436
|
+
# Too much, let's reduce speed instead
|
1437
|
+
if speedval:
|
1438
|
+
note += f"\\nNeeded to reduce speed {speedval:.1}mm/s -> {speedval / factor:.2}mm/s"
|
1439
|
+
speedval *= 1 / factor
|
1440
|
+
else:
|
1441
|
+
if powerval:
|
1442
|
+
powerval *= factor
|
1443
|
+
if powerval:
|
1444
|
+
op.power = powerval
|
1445
|
+
if speedval:
|
1446
|
+
op.speed = speedval
|
1447
|
+
# op.note = note
|
1448
|
+
# Do we have a duplicate id?
|
1449
|
+
if op.id in uid or op.id is None or op.id == "":
|
1450
|
+
idx = 1
|
1451
|
+
pattern = op.type[3].upper()
|
1452
|
+
while f"{pattern}{idx}" in uid:
|
1453
|
+
idx += 1
|
1454
|
+
op.id = f"{pattern}{idx}"
|
1455
|
+
|
1456
|
+
# Add id to list of existing ids
|
1457
|
+
uid[op.id] = op
|
1458
|
+
elems.save_persistent_operations_list(
|
1459
|
+
target_section,
|
1460
|
+
oplist=oplist,
|
1461
|
+
opinfo=opinfo,
|
1462
|
+
inform=False,
|
1463
|
+
use_settings=self.op_data,
|
1464
|
+
)
|
1465
|
+
|
1466
|
+
return added
|
1467
|
+
|
1468
|
+
def import_ezcad(self, info):
|
1469
|
+
# info = (fname, old_lens, new_lens, old_power, new_power, factor_from_lens, factor_from_power, factor, consolidate)
|
1470
|
+
filename = info[0]
|
1471
|
+
factor = info[7]
|
1472
|
+
lens_info = info[2]
|
1473
|
+
if lens_info is None:
|
1474
|
+
lens_info = ""
|
1475
|
+
power_info = info[4]
|
1476
|
+
if power_info is None:
|
1477
|
+
power_info = ""
|
1478
|
+
if not os.path.exists(filename):
|
1479
|
+
return False
|
1480
|
+
added = False
|
1481
|
+
# We can safely assume this a fibre laser...
|
1482
|
+
laser_type = 0
|
1483
|
+
for idx, desc in enumerate(self.laser_choices):
|
1484
|
+
if "fibre" in desc.lower():
|
1485
|
+
laser_type = idx
|
1486
|
+
break
|
1487
|
+
try:
|
1488
|
+
with open(filename, "r") as f:
|
1489
|
+
# We need to have a new id...
|
1490
|
+
new_import_id = 0
|
1491
|
+
pattern = "import_"
|
1492
|
+
for section in self.op_data.section_set():
|
1493
|
+
if section.startswith(pattern):
|
1494
|
+
s = section[len(pattern) :]
|
1495
|
+
try:
|
1496
|
+
number = int(s)
|
1497
|
+
except ValueError:
|
1498
|
+
number = 0
|
1499
|
+
if number > new_import_id:
|
1500
|
+
new_import_id = number
|
1501
|
+
section_name = ""
|
1502
|
+
info_section_name = ""
|
1503
|
+
info_box = ""
|
1504
|
+
powerval = None
|
1505
|
+
speedval = None
|
1506
|
+
numeric_value = 0
|
1507
|
+
|
1508
|
+
while True:
|
1509
|
+
line = f.readline()
|
1510
|
+
if not line:
|
1511
|
+
break
|
1512
|
+
line = line.strip()
|
1513
|
+
if line.startswith("["):
|
1514
|
+
if info_box and info_section_name:
|
1515
|
+
self.op_data.write_persistent(
|
1516
|
+
info_section_name, "note", info_box
|
1517
|
+
)
|
1518
|
+
if powerval and section_name:
|
1519
|
+
if factor != 1:
|
1520
|
+
old_l = info[1]
|
1521
|
+
new_l = info[2]
|
1522
|
+
factor_l = info[5]
|
1523
|
+
if old_l is not None:
|
1524
|
+
if info_box:
|
1525
|
+
info_box += "\\n"
|
1526
|
+
info_box += f"Converted lens-size {old_l}mm -> {new_l}mm: {factor_l:.2}"
|
1527
|
+
old_l = info[3]
|
1528
|
+
new_l = info[4]
|
1529
|
+
factor_l = info[6]
|
1530
|
+
if old_l is not None:
|
1531
|
+
if info_box:
|
1532
|
+
info_box += "\\n"
|
1533
|
+
info_box += f"Converted power {old_l}W -> {new_l}W: {factor_l:.2}"
|
1534
|
+
if powerval * factor > 1000:
|
1535
|
+
# Too much, let's reduce speed instead
|
1536
|
+
if speedval:
|
1537
|
+
if info_box:
|
1538
|
+
info_box += "\\n"
|
1539
|
+
info_box += f"Needed to reduce speed {speedval:.1}mm/s -> {speedval / factor:.2}mm/s"
|
1540
|
+
speedval *= 1 / factor
|
1541
|
+
else:
|
1542
|
+
powerval *= factor
|
1543
|
+
self.op_data.write_persistent(
|
1544
|
+
section_name, "speed", speedval
|
1545
|
+
)
|
1546
|
+
self.op_data.write_persistent(
|
1547
|
+
section_name, "power", powerval
|
1548
|
+
)
|
1549
|
+
powerval = None
|
1550
|
+
speedval = None
|
1551
|
+
info_box = ""
|
1552
|
+
new_import_id += 1
|
1553
|
+
sect = f"{pattern}{new_import_id:0>4}"
|
1554
|
+
info_section_name = f"{sect} info"
|
1555
|
+
section_name = f"{sect} {0:0>6}"
|
1556
|
+
matname = line[1:-1]
|
1557
|
+
if matname.startswith("F "):
|
1558
|
+
matname = matname[2:]
|
1559
|
+
self.op_data.write_persistent(
|
1560
|
+
info_section_name, "material", matname
|
1561
|
+
)
|
1562
|
+
title = matname
|
1563
|
+
if power_info:
|
1564
|
+
title += f" {power_info}W"
|
1565
|
+
if lens_info:
|
1566
|
+
title += f" {lens_info}mm"
|
1567
|
+
self.op_data.write_persistent(info_section_name, "title", title)
|
1568
|
+
self.op_data.write_persistent(
|
1569
|
+
info_section_name, "power", power_info
|
1570
|
+
)
|
1571
|
+
self.op_data.write_persistent(
|
1572
|
+
info_section_name, "lens", lens_info
|
1573
|
+
)
|
1574
|
+
self.op_data.write_persistent(
|
1575
|
+
info_section_name, "laser", laser_type
|
1576
|
+
)
|
1577
|
+
self.op_data.write_persistent(
|
1578
|
+
section_name, "type", "op engrave"
|
1579
|
+
)
|
1580
|
+
self.op_data.write_persistent(section_name, "id", "F1")
|
1581
|
+
speed_factor = 1.0
|
1582
|
+
added = True
|
1583
|
+
else:
|
1584
|
+
if not section_name:
|
1585
|
+
continue
|
1586
|
+
idx = line.find("=")
|
1587
|
+
if idx <= 0:
|
1588
|
+
continue
|
1589
|
+
param = line[:idx].lower()
|
1590
|
+
value = line[idx + 1 :]
|
1591
|
+
try:
|
1592
|
+
numeric_value = float(value)
|
1593
|
+
except ValueError:
|
1594
|
+
numeric_value = 0
|
1595
|
+
if param == "loop":
|
1596
|
+
if numeric_value != 0:
|
1597
|
+
self.op_data.write_persistent(
|
1598
|
+
section_name, "passes", numeric_value
|
1599
|
+
)
|
1600
|
+
self.op_data.write_persistent(
|
1601
|
+
section_name, "passes_custom", True
|
1602
|
+
)
|
1603
|
+
elif param == "markspeed":
|
1604
|
+
if numeric_value != 0:
|
1605
|
+
speedval = numeric_value
|
1606
|
+
elif param == "powerratio":
|
1607
|
+
if numeric_value != 0:
|
1608
|
+
powerval = numeric_value * 10
|
1609
|
+
elif param == "freq":
|
1610
|
+
# khz
|
1611
|
+
if numeric_value != 0:
|
1612
|
+
self.op_data.write_persistent(
|
1613
|
+
section_name, "frequency", numeric_value / 1000.0
|
1614
|
+
)
|
1615
|
+
elif param == "jumpspeed":
|
1616
|
+
if numeric_value != 0:
|
1617
|
+
self.op_data.write_persistent(
|
1618
|
+
section_name, "rapid_enabled", True
|
1619
|
+
)
|
1620
|
+
self.op_data.write_persistent(
|
1621
|
+
section_name, "rapid_speed", numeric_value
|
1622
|
+
)
|
1623
|
+
elif param == "starttc":
|
1624
|
+
if numeric_value != 0:
|
1625
|
+
self.op_data.write_persistent(
|
1626
|
+
section_name, "timing_enabled", True
|
1627
|
+
)
|
1628
|
+
self.op_data.write_persistent(
|
1629
|
+
section_name, "delay_laser_on", numeric_value
|
1630
|
+
)
|
1631
|
+
elif param == "laserofftc":
|
1632
|
+
if numeric_value != 0:
|
1633
|
+
self.op_data.write_persistent(
|
1634
|
+
section_name, "timing_enabled", True
|
1635
|
+
)
|
1636
|
+
self.op_data.write_persistent(
|
1637
|
+
section_name, "delay_laser_off", numeric_value
|
1638
|
+
)
|
1639
|
+
elif param == "polytc":
|
1640
|
+
if numeric_value != 0:
|
1641
|
+
self.op_data.write_persistent(
|
1642
|
+
section_name, "timing_enabled", True
|
1643
|
+
)
|
1644
|
+
self.op_data.write_persistent(
|
1645
|
+
section_name, "delay_polygon", numeric_value
|
1646
|
+
)
|
1647
|
+
elif param == "qpulsewidth":
|
1648
|
+
if numeric_value != 0:
|
1649
|
+
self.op_data.write_persistent(
|
1650
|
+
section_name, "pulse_width_enabled", True
|
1651
|
+
)
|
1652
|
+
self.op_data.write_persistent(
|
1653
|
+
section_name, "pulse_width", numeric_value
|
1654
|
+
)
|
1655
|
+
else:
|
1656
|
+
# Unknown / unsupported - a significant amount of the parameters
|
1657
|
+
# would require the addition of different things like hatches,
|
1658
|
+
# wobbles or device specific settings
|
1659
|
+
if numeric_value != 0:
|
1660
|
+
if info_box:
|
1661
|
+
info_box += "\\n"
|
1662
|
+
info_box += f"{param} = {numeric_value}"
|
1663
|
+
# Residual information available?
|
1664
|
+
if powerval and section_name:
|
1665
|
+
if factor != 1:
|
1666
|
+
old_l = info[1]
|
1667
|
+
new_l = info[2]
|
1668
|
+
factor_l = info[5]
|
1669
|
+
if old_l is not None:
|
1670
|
+
if info_box:
|
1671
|
+
info_box += "\\n"
|
1672
|
+
info_box += f"Converted lens-size {old_l}mm -> {new_l}mm: {factor_l:.2}"
|
1673
|
+
old_l = info[3]
|
1674
|
+
new_l = info[4]
|
1675
|
+
factor_l = info[6]
|
1676
|
+
if old_l is not None:
|
1677
|
+
if info_box:
|
1678
|
+
info_box += "\\n"
|
1679
|
+
info_box += (
|
1680
|
+
f"Converted power {old_l}W -> {new_l}W: {factor_l:.2}"
|
1681
|
+
)
|
1682
|
+
if powerval * factor > 1000:
|
1683
|
+
# Too much, let's reduce speed instead
|
1684
|
+
if speedval:
|
1685
|
+
if info_box:
|
1686
|
+
info_box += "\\n"
|
1687
|
+
info_box += f"Needed to reduce speed {numeric_value:.1}mm/s -> {numeric_value * speed_factor:.2}mm/s"
|
1688
|
+
speedval *= 1 / factor
|
1689
|
+
else:
|
1690
|
+
powerval *= factor
|
1691
|
+
self.op_data.write_persistent(section_name, "speed", speedval)
|
1692
|
+
self.op_data.write_persistent(section_name, "power", powerval)
|
1693
|
+
if info_box and info_section_name:
|
1694
|
+
self.op_data.write_persistent(info_section_name, "note", info_box)
|
1695
|
+
|
1696
|
+
except (OSError, RuntimeError, PermissionError, FileNotFoundError):
|
1697
|
+
return False
|
1698
|
+
if added:
|
1699
|
+
self.op_data.write_configuration()
|
1700
|
+
return added
|
1701
|
+
|
1702
|
+
def on_import(self, event, filename=None):
|
1703
|
+
#
|
1704
|
+
info = None
|
1705
|
+
mydlg = ImportDialog(
|
1706
|
+
None, id=wx.ID_ANY, context=self.context, filename=filename
|
1707
|
+
)
|
1708
|
+
if mydlg.ShowModal() == wx.ID_OK:
|
1709
|
+
# This returns a Python list of files that were selected.
|
1710
|
+
info = mydlg.result()
|
1711
|
+
mydlg.Destroy()
|
1712
|
+
if info is None:
|
1713
|
+
return
|
1714
|
+
added = False
|
1715
|
+
myfile = info[0]
|
1716
|
+
if myfile.endswith(".clb"):
|
1717
|
+
added = self.import_lightburn(info)
|
1718
|
+
elif myfile.endswith(".lib") or myfile.endswith(".ini"):
|
1719
|
+
added = self.import_ezcad(info)
|
1720
|
+
elif myfile.endswith(".cfg"):
|
1721
|
+
added = self.import_meerk40t(info)
|
1722
|
+
else:
|
1723
|
+
self.invalid_file(myfile)
|
1724
|
+
|
1725
|
+
if added:
|
1726
|
+
self.on_reset(None)
|
1727
|
+
|
1728
|
+
def on_apply_statusbar(self, event):
|
1729
|
+
if self.active_material is None:
|
1730
|
+
return
|
1731
|
+
op_list, op_info = self.context.elements.load_persistent_op_list(
|
1732
|
+
self.active_material,
|
1733
|
+
use_settings=self.op_data,
|
1734
|
+
)
|
1735
|
+
if len(op_list) == 0:
|
1736
|
+
return
|
1737
|
+
self.context.elements.default_operations = list(op_list)
|
1738
|
+
self.context.signal("default_operations")
|
1739
|
+
|
1740
|
+
def on_apply_tree(self, event):
|
1741
|
+
if self.active_material is None:
|
1742
|
+
return
|
1743
|
+
op_list, op_info = self.context.elements.load_persistent_op_list(
|
1744
|
+
self.active_material,
|
1745
|
+
use_settings=self.op_data,
|
1746
|
+
)
|
1747
|
+
if len(op_list) == 0:
|
1748
|
+
return
|
1749
|
+
response = self.context.kernel.yesno(
|
1750
|
+
_("Do you want to remove all existing operations before loading this set?"),
|
1751
|
+
caption=_("Clear Operation-List"),
|
1752
|
+
)
|
1753
|
+
self.context.elements.load_persistent_operations(
|
1754
|
+
self.active_material, clear=response
|
1755
|
+
)
|
1756
|
+
self.context.signal("rebuild_tree")
|
1757
|
+
|
1758
|
+
def on_new(self, event):
|
1759
|
+
entry_txt = self.txt_material.GetValue()
|
1760
|
+
if entry_txt == "":
|
1761
|
+
entry_txt = "material"
|
1762
|
+
if entry_txt in self.material_list:
|
1763
|
+
idx = 0
|
1764
|
+
while True:
|
1765
|
+
idx += 1
|
1766
|
+
pattern = f"{entry_txt}_({idx})"
|
1767
|
+
if pattern not in self.material_list:
|
1768
|
+
break
|
1769
|
+
entry_txt = pattern
|
1770
|
+
# entry_type = self.combo_entry_type.GetSelection()
|
1771
|
+
# if entry_type < 0:
|
1772
|
+
# entry_type = 0
|
1773
|
+
# We need to create a new one...
|
1774
|
+
op_info = dict()
|
1775
|
+
op_info["material"] = _("New material")
|
1776
|
+
op_info["laser"] = 0
|
1777
|
+
op_info["thickness"] = "4mm"
|
1778
|
+
op_info["note"] = _("You can put additional operation instructions here.")
|
1779
|
+
section = entry_txt
|
1780
|
+
|
1781
|
+
if len(list(self.context.elements.ops())) == 0:
|
1782
|
+
op_list = self.context.elements.default_operations
|
1783
|
+
if len(op_list) == 0:
|
1784
|
+
return
|
1785
|
+
# op_list = None save op_branch
|
1786
|
+
self.context.elements.save_persistent_operations_list(
|
1787
|
+
section,
|
1788
|
+
oplist=op_list,
|
1789
|
+
opinfo=op_info,
|
1790
|
+
inform=False,
|
1791
|
+
use_settings=self.op_data,
|
1792
|
+
)
|
1793
|
+
else:
|
1794
|
+
# op_list = None save op_branch
|
1795
|
+
self.context.elements.save_persistent_operations_list(
|
1796
|
+
section,
|
1797
|
+
oplist=None,
|
1798
|
+
opinfo=op_info,
|
1799
|
+
inform=False,
|
1800
|
+
use_settings=self.op_data,
|
1801
|
+
)
|
1802
|
+
self.op_data.write_configuration()
|
1803
|
+
self.retrieve_material_list(reload=True, setter=section)
|
1804
|
+
|
1805
|
+
def on_use_current(self, event):
|
1806
|
+
section = None
|
1807
|
+
op_info = None
|
1808
|
+
if self.active_material is not None:
|
1809
|
+
if self.context.kernel.yesno(
|
1810
|
+
_(
|
1811
|
+
"Do you want to use the operations in the tree for the current entry?"
|
1812
|
+
)
|
1813
|
+
):
|
1814
|
+
section = self.active_material
|
1815
|
+
op_info = self.context.elements.load_persistent_op_info(
|
1816
|
+
self.active_material,
|
1817
|
+
use_settings=self.op_data,
|
1818
|
+
)
|
1819
|
+
if "material" not in op_info:
|
1820
|
+
op_info["material"] = "Operations List"
|
1821
|
+
if "laser" not in op_info:
|
1822
|
+
op_info["laser"] = 0
|
1823
|
+
|
1824
|
+
if section is None:
|
1825
|
+
entry_txt = self.txt_material.GetValue()
|
1826
|
+
if entry_txt == "":
|
1827
|
+
entry_txt = "material"
|
1828
|
+
if entry_txt in self.material_list:
|
1829
|
+
idx = 0
|
1830
|
+
while True:
|
1831
|
+
idx += 1
|
1832
|
+
pattern = f"{entry_txt}_({idx})"
|
1833
|
+
if pattern not in self.material_list:
|
1834
|
+
break
|
1835
|
+
entry_txt = pattern
|
1836
|
+
# entry_type = self.combo_entry_type.GetSelection()
|
1837
|
+
# if entry_type < 0:
|
1838
|
+
# entry_type = 0
|
1839
|
+
# We need to create a new one...
|
1840
|
+
op_info = dict()
|
1841
|
+
op_info["material"] = _("New material")
|
1842
|
+
op_info["laser"] = 0
|
1843
|
+
section = entry_txt
|
1844
|
+
|
1845
|
+
# op_list = None save op_branch
|
1846
|
+
self.context.elements.save_persistent_operations_list(
|
1847
|
+
section,
|
1848
|
+
oplist=None,
|
1849
|
+
opinfo=op_info,
|
1850
|
+
inform=False,
|
1851
|
+
use_settings=self.op_data,
|
1852
|
+
)
|
1853
|
+
self.op_data.write_configuration()
|
1854
|
+
self.retrieve_material_list(reload=True, setter=section)
|
1855
|
+
|
1856
|
+
def on_reset(self, event):
|
1857
|
+
self.no_reload = True
|
1858
|
+
self.txt_material.SetValue("")
|
1859
|
+
self.txt_thickness.SetValue("")
|
1860
|
+
self.combo_lasertype.SetSelection(0)
|
1861
|
+
self.no_reload = False
|
1862
|
+
self.update_list(reload=True)
|
1863
|
+
|
1864
|
+
def update_list(self, *args, **kwargs):
|
1865
|
+
if self.no_reload:
|
1866
|
+
return
|
1867
|
+
filter_txt = self.txt_material.GetValue()
|
1868
|
+
filter_thickness = self.txt_thickness.GetValue()
|
1869
|
+
filter_type = self.combo_lasertype.GetSelection()
|
1870
|
+
if filter_txt == "":
|
1871
|
+
filter_txt = None
|
1872
|
+
if filter_thickness == "":
|
1873
|
+
filter_thickness = None
|
1874
|
+
if filter_type < 0:
|
1875
|
+
filter_type = None
|
1876
|
+
reload = False
|
1877
|
+
if "reload" in kwargs:
|
1878
|
+
reload = kwargs["reload"]
|
1879
|
+
self.retrieve_material_list(
|
1880
|
+
filtername=filter_txt,
|
1881
|
+
filterlaser=filter_type,
|
1882
|
+
filterthickness=filter_thickness,
|
1883
|
+
reload=reload,
|
1884
|
+
setter=self.active_material,
|
1885
|
+
)
|
1886
|
+
|
1887
|
+
def update_lasertype_for_all(self, event):
|
1888
|
+
op_ltype = self.combo_entry_type.GetSelection()
|
1889
|
+
if op_ltype < 0:
|
1890
|
+
return
|
1891
|
+
changes = False
|
1892
|
+
for entry in self.display_list:
|
1893
|
+
material = entry["section"]
|
1894
|
+
section = f"{material} info"
|
1895
|
+
self.op_data.write_persistent(section, "laser", op_ltype)
|
1896
|
+
changes = True
|
1897
|
+
if changes:
|
1898
|
+
self.op_data.write_configuration()
|
1899
|
+
self.on_reset(None)
|
1900
|
+
|
1901
|
+
def update_entry(self, event):
|
1902
|
+
if self.active_material is None:
|
1903
|
+
return
|
1904
|
+
op_section = self.txt_entry_section.GetValue()
|
1905
|
+
for forbidden in " []":
|
1906
|
+
op_section = op_section.replace(forbidden, "_")
|
1907
|
+
ctrls = (
|
1908
|
+
self.txt_entry_title,
|
1909
|
+
self.txt_entry_material,
|
1910
|
+
self.txt_entry_thickness,
|
1911
|
+
self.txt_entry_power,
|
1912
|
+
self.txt_entry_lens,
|
1913
|
+
)
|
1914
|
+
fields = (
|
1915
|
+
"title",
|
1916
|
+
"material",
|
1917
|
+
"thickness",
|
1918
|
+
"power",
|
1919
|
+
"lens",
|
1920
|
+
)
|
1921
|
+
data = [(field, ctrl.GetValue()) for field, ctrl in zip(fields, ctrls)]
|
1922
|
+
|
1923
|
+
op_ltype = self.combo_entry_type.GetSelection()
|
1924
|
+
if op_ltype < 0:
|
1925
|
+
op_ltype = 0
|
1926
|
+
data.append(("laser", op_ltype))
|
1927
|
+
|
1928
|
+
# Note, convert linebreaks
|
1929
|
+
op_note = self.txt_entry_note.GetValue().replace("\n", "\\n")
|
1930
|
+
data.append(("note", op_note))
|
1931
|
+
|
1932
|
+
op_list, op_info = self.context.elements.load_persistent_op_list(
|
1933
|
+
self.active_material,
|
1934
|
+
use_settings=self.op_data,
|
1935
|
+
)
|
1936
|
+
if len(op_list) == 0:
|
1937
|
+
return
|
1938
|
+
to_save = False
|
1939
|
+
for entry in data:
|
1940
|
+
field, value = entry
|
1941
|
+
stored_value = op_info.get(field, "")
|
1942
|
+
if value != stored_value:
|
1943
|
+
op_info[field] = value
|
1944
|
+
to_save = True
|
1945
|
+
if to_save:
|
1946
|
+
if self.active_material != op_section:
|
1947
|
+
self.context.elements.clear_persistent_operations(
|
1948
|
+
self.active_material,
|
1949
|
+
use_settings=self.op_data,
|
1950
|
+
)
|
1951
|
+
self.active_material = op_section
|
1952
|
+
self.context.elements.save_persistent_operations_list(
|
1953
|
+
self.active_material,
|
1954
|
+
oplist=op_list,
|
1955
|
+
opinfo=op_info,
|
1956
|
+
inform=False,
|
1957
|
+
use_settings=self.op_data,
|
1958
|
+
)
|
1959
|
+
self.op_data.write_configuration()
|
1960
|
+
self.retrieve_material_list(reload=True, setter=self.active_material)
|
1961
|
+
|
1962
|
+
def on_list_selection(self, event):
|
1963
|
+
try:
|
1964
|
+
item = event.GetItem()
|
1965
|
+
if item and item.IsOk():
|
1966
|
+
listidx = self.tree_library.GetItemData(item)
|
1967
|
+
if listidx >= 0:
|
1968
|
+
info = self.get_nth_material(listidx)
|
1969
|
+
self.active_material = info
|
1970
|
+
else:
|
1971
|
+
self.active_material = None
|
1972
|
+
except RuntimeError:
|
1973
|
+
return
|
1974
|
+
|
1975
|
+
def fill_preview(self):
|
1976
|
+
|
1977
|
+
def get_key(op_type, op_color):
|
1978
|
+
return f"{op_type}-{str(op_color)}"
|
1979
|
+
|
1980
|
+
def populate_images() -> dict:
|
1981
|
+
COLORFUL_BACKGROUND = True
|
1982
|
+
iconsize = 30
|
1983
|
+
self.state_images.Destroy()
|
1984
|
+
self.state_images = wx.ImageList()
|
1985
|
+
self.state_images.Create(width=iconsize, height=iconsize)
|
1986
|
+
image_dict = {}
|
1987
|
+
if self.active_material is not None:
|
1988
|
+
for subsection in self.op_data.derivable(self.active_material):
|
1989
|
+
optype = self.op_data.read_persistent(str, subsection, "type", "")
|
1990
|
+
if optype is None or optype == "":
|
1991
|
+
continue
|
1992
|
+
opcolor = self.op_data.read_persistent(str, subsection, "color", "")
|
1993
|
+
if opcolor:
|
1994
|
+
opc = Color(opcolor)
|
1995
|
+
else:
|
1996
|
+
opc = None
|
1997
|
+
key = get_key(optype, opc)
|
1998
|
+
if key in image_dict:
|
1999
|
+
continue
|
2000
|
+
try:
|
2001
|
+
info = self.opinfo[optype]
|
2002
|
+
except KeyError:
|
2003
|
+
info = self.opinfo["generic"]
|
2004
|
+
if COLORFUL_BACKGROUND:
|
2005
|
+
if opc is None:
|
2006
|
+
opc = Color("black")
|
2007
|
+
fgcol = wx.BLACK if Color.distance(opc, "black") > Color.distance(opc, "white") else wx.WHITE
|
2008
|
+
forced_bg = (opc.red, opc.green, opc.blue, opc.alpha)
|
2009
|
+
bmap = info[1].GetBitmap(resize=(iconsize, iconsize), noadjustment=True, color=fgcol, forced_background=forced_bg)
|
2010
|
+
else:
|
2011
|
+
bmap = info[1].GetBitmap(resize=(iconsize, iconsize), noadjustment=True, color=opc)
|
2012
|
+
image_id = self.state_images.Add(bitmap=bmap)
|
2013
|
+
image_dict[key] = image_id
|
2014
|
+
|
2015
|
+
self.list_preview.AssignImageList(self.state_images, wx.IMAGE_LIST_SMALL)
|
2016
|
+
return image_dict
|
2017
|
+
|
2018
|
+
self._balor = False
|
2019
|
+
for obj, name, sname in self.context.find("dev_info"):
|
2020
|
+
if obj is not None and "balor" in sname.lower():
|
2021
|
+
self._balor = True
|
2022
|
+
break
|
2023
|
+
|
2024
|
+
self.list_preview.Freeze()
|
2025
|
+
self.list_preview.DeleteAllItems()
|
2026
|
+
icon_dict = populate_images()
|
2027
|
+
self.operation_list.clear()
|
2028
|
+
secdesc = ""
|
2029
|
+
thickness = ""
|
2030
|
+
info_power = ""
|
2031
|
+
info_lens = ""
|
2032
|
+
info_title = ""
|
2033
|
+
note = ""
|
2034
|
+
ltype = 0
|
2035
|
+
if self.active_material is not None:
|
2036
|
+
secdesc = ""
|
2037
|
+
idx = 0
|
2038
|
+
for subsection in self.op_data.derivable(self.active_material):
|
2039
|
+
if subsection.endswith(" info"):
|
2040
|
+
info_title = self.op_data.read_persistent(
|
2041
|
+
str, subsection, "title", ""
|
2042
|
+
)
|
2043
|
+
info_power = self.op_data.read_persistent(
|
2044
|
+
str, subsection, "power", ""
|
2045
|
+
)
|
2046
|
+
info_lens = self.op_data.read_persistent(
|
2047
|
+
str, subsection, "lens", ""
|
2048
|
+
)
|
2049
|
+
secdesc = self.op_data.read_persistent(
|
2050
|
+
str, subsection, "material", ""
|
2051
|
+
)
|
2052
|
+
thickness = self.op_data.read_persistent(
|
2053
|
+
str, subsection, "thickness", ""
|
2054
|
+
)
|
2055
|
+
ltype = self.op_data.read_persistent(int, subsection, "laser", 0)
|
2056
|
+
note = self.op_data.read_persistent(str, subsection, "note", "")
|
2057
|
+
# We need to replace stored linebreaks with real linebreaks
|
2058
|
+
note = note.replace("\\n", "\n")
|
2059
|
+
if ltype == 3:
|
2060
|
+
self._balor = True
|
2061
|
+
elif ltype != 0:
|
2062
|
+
self._balor = False
|
2063
|
+
continue
|
2064
|
+
optype = self.op_data.read_persistent(str, subsection, "type", "")
|
2065
|
+
if optype is None or optype == "":
|
2066
|
+
continue
|
2067
|
+
opcolor = self.op_data.read_persistent(str, subsection, "color", "")
|
2068
|
+
if opcolor:
|
2069
|
+
opc = Color(opcolor)
|
2070
|
+
else:
|
2071
|
+
opc = None
|
2072
|
+
idx += 1
|
2073
|
+
opid = self.op_data.read_persistent(str, subsection, "id", "")
|
2074
|
+
oplabel = self.op_data.read_persistent(str, subsection, "label", "")
|
2075
|
+
speed = self.op_data.read_persistent(str, subsection, "speed", "")
|
2076
|
+
power = self.op_data.read_persistent(str, subsection, "power", "")
|
2077
|
+
passes = self.op_data.read_persistent(str, subsection, "passes", "")
|
2078
|
+
frequency = self.op_data.read_persistent(
|
2079
|
+
str, subsection, "frequency", ""
|
2080
|
+
)
|
2081
|
+
if not self.is_balor:
|
2082
|
+
frequency = ""
|
2083
|
+
command = self.op_data.read_persistent(str, subsection, "command", "")
|
2084
|
+
if power == "" and optype.startswith("op "):
|
2085
|
+
power = "1000"
|
2086
|
+
if passes == "" and optype.startswith("op "):
|
2087
|
+
passes = "1"
|
2088
|
+
list_id = self.list_preview.InsertItem(
|
2089
|
+
self.list_preview.GetItemCount(), f"#{idx}"
|
2090
|
+
)
|
2091
|
+
try:
|
2092
|
+
info = self.opinfo[optype]
|
2093
|
+
except KeyError:
|
2094
|
+
info = self.opinfo["generic"]
|
2095
|
+
if command:
|
2096
|
+
if oplabel:
|
2097
|
+
oplabel += " "
|
2098
|
+
else:
|
2099
|
+
oplabel = ""
|
2100
|
+
oplabel += f"({command})"
|
2101
|
+
self.list_preview.SetItem(list_id, 1, info[0])
|
2102
|
+
self.list_preview.SetItem(list_id, 2, opid)
|
2103
|
+
self.list_preview.SetItem(list_id, 3, oplabel)
|
2104
|
+
self.list_preview.SetItem(list_id, 4, power)
|
2105
|
+
self.list_preview.SetItem(list_id, 5, speed)
|
2106
|
+
self.list_preview.SetItem(list_id, 6, frequency)
|
2107
|
+
self.list_preview.SetItem(list_id, 7, passes)
|
2108
|
+
key = get_key(optype, opc)
|
2109
|
+
if key in icon_dict:
|
2110
|
+
imgid = icon_dict[key]
|
2111
|
+
self.list_preview.SetItemImage(list_id, imgid)
|
2112
|
+
self.list_preview.SetItemData(list_id, idx - 1)
|
2113
|
+
self.operation_list[subsection] = (optype, opid, oplabel, power, speed)
|
2114
|
+
self.list_preview.Thaw()
|
2115
|
+
self.list_preview.Refresh()
|
2116
|
+
if self.active_material is None:
|
2117
|
+
actval = ""
|
2118
|
+
else:
|
2119
|
+
actval = self.active_material
|
2120
|
+
# print (f"id: '{actval}'\ntitle: '{info_title}'\nmaterial: '{secdesc}'")
|
2121
|
+
if not info_title:
|
2122
|
+
info_title = actval.replace("_", " ")
|
2123
|
+
self.txt_entry_section.SetValue(actval)
|
2124
|
+
self.txt_entry_material.SetValue(secdesc)
|
2125
|
+
self.txt_entry_thickness.SetValue(thickness)
|
2126
|
+
self.txt_entry_title.SetValue(info_title)
|
2127
|
+
self.txt_entry_power.SetValue(info_power)
|
2128
|
+
self.txt_entry_lens.SetValue(info_lens)
|
2129
|
+
self.txt_entry_note.SetValue(note)
|
2130
|
+
self.combo_entry_type.SetSelection(ltype)
|
2131
|
+
self.list_preview.resize_columns()
|
2132
|
+
|
2133
|
+
def on_preview_selection(self, event):
|
2134
|
+
event.Skip()
|
2135
|
+
|
2136
|
+
def on_library_rightclick(self, event):
|
2137
|
+
event.Skip()
|
2138
|
+
menu = wx.Menu()
|
2139
|
+
item = menu.Append(wx.ID_ANY, _("Add new"), "", wx.ITEM_NORMAL)
|
2140
|
+
self.Bind(wx.EVT_MENU, self.on_new, item)
|
2141
|
+
|
2142
|
+
item = menu.Append(wx.ID_ANY, _("Get current"), "", wx.ITEM_NORMAL)
|
2143
|
+
self.Bind(wx.EVT_MENU, self.on_use_current, item)
|
2144
|
+
|
2145
|
+
item = menu.Append(wx.ID_ANY, _("Load into Tree"), "", wx.ITEM_NORMAL)
|
2146
|
+
menu.Enable(item.GetId(), bool(self.active_material is not None))
|
2147
|
+
self.Bind(wx.EVT_MENU, self.on_apply_tree, item)
|
2148
|
+
|
2149
|
+
item = menu.Append(wx.ID_ANY, _("Use for statusbar"), "", wx.ITEM_NORMAL)
|
2150
|
+
menu.Enable(item.GetId(), bool(self.active_material is not None))
|
2151
|
+
self.Bind(wx.EVT_MENU, self.on_apply_statusbar, item)
|
2152
|
+
|
2153
|
+
item = menu.Append(wx.ID_ANY, _("Duplicate"), "", wx.ITEM_NORMAL)
|
2154
|
+
menu.Enable(item.GetId(), bool(self.active_material is not None))
|
2155
|
+
self.Bind(wx.EVT_MENU, self.on_duplicate, item)
|
2156
|
+
|
2157
|
+
# We delete all entries of the same kind.
|
2158
|
+
# mat_list_entry = self.tree_library.GetSelection()
|
2159
|
+
|
2160
|
+
if self.share_ready:
|
2161
|
+
menu.AppendSeparator()
|
2162
|
+
item = menu.Append(wx.ID_ANY, _("Share"), "", wx.ITEM_NORMAL)
|
2163
|
+
menu.Enable(item.GetId(), bool(self.active_material is not None))
|
2164
|
+
self.Bind(wx.EVT_MENU, self.on_share, item)
|
2165
|
+
|
2166
|
+
def create_minimal(event):
|
2167
|
+
section = "minimal"
|
2168
|
+
oplist = self.context.elements.create_minimal_op_list()
|
2169
|
+
opinfo = {"material": "Minimal list", "laser": 0}
|
2170
|
+
self.context.elements.save_persistent_operations_list(
|
2171
|
+
section,
|
2172
|
+
oplist,
|
2173
|
+
opinfo,
|
2174
|
+
False,
|
2175
|
+
use_settings=self.op_data,
|
2176
|
+
)
|
2177
|
+
self.op_data.write_configuration()
|
2178
|
+
self.retrieve_material_list(reload=True, setter=section)
|
2179
|
+
|
2180
|
+
def create_basic(event):
|
2181
|
+
section = "basic"
|
2182
|
+
oplist = self.context.elements.create_basic_op_list()
|
2183
|
+
opinfo = {"material": "Basic list", "laser": 0}
|
2184
|
+
self.context.elements.save_persistent_operations_list(
|
2185
|
+
section,
|
2186
|
+
oplist,
|
2187
|
+
opinfo,
|
2188
|
+
False,
|
2189
|
+
use_settings=self.op_data,
|
2190
|
+
)
|
2191
|
+
self.op_data.write_configuration()
|
2192
|
+
self.retrieve_material_list(reload=True, setter=section)
|
2193
|
+
|
2194
|
+
menu.AppendSeparator()
|
2195
|
+
item = menu.Append(wx.ID_ANY, _("Create minimal"), "", wx.ITEM_NORMAL)
|
2196
|
+
self.Bind(wx.EVT_MENU, create_minimal, item)
|
2197
|
+
item = menu.Append(wx.ID_ANY, _("Create basic"), "", wx.ITEM_NORMAL)
|
2198
|
+
self.Bind(wx.EVT_MENU, create_basic, item)
|
2199
|
+
menu.AppendSeparator()
|
2200
|
+
tree_item = self.tree_library.GetSelection()
|
2201
|
+
if tree_item.IsOk():
|
2202
|
+
listidx = self.tree_library.GetItemData(tree_item)
|
2203
|
+
if listidx >= 0:
|
2204
|
+
item = menu.Append(wx.ID_ANY, _("Delete"), "", wx.ITEM_NORMAL)
|
2205
|
+
menu.Enable(item.GetId(), bool(self.active_material is not None))
|
2206
|
+
self.Bind(wx.EVT_MENU, self.on_delete, item)
|
2207
|
+
else:
|
2208
|
+
deletion_info = self.deletion_methods[listidx]
|
2209
|
+
deletion_level, key1, value1, key2, value2 = deletion_info
|
2210
|
+
if deletion_level > 0: # First or second category
|
2211
|
+
criteria = f"{key1}='{value1}'"
|
2212
|
+
if deletion_level == 2:
|
2213
|
+
criteria += f" + {key2}='{value2}'"
|
2214
|
+
info = _("Delete all with {data}").format(data=criteria)
|
2215
|
+
item = menu.Append(wx.ID_ANY, info, "", wx.ITEM_NORMAL)
|
2216
|
+
self.Bind(wx.EVT_MENU, self.on_delete_category(deletion_level, value1, value2), item)
|
2217
|
+
|
2218
|
+
item = menu.Append(wx.ID_ANY, _("Delete all"), "", wx.ITEM_NORMAL)
|
2219
|
+
self.Bind(wx.EVT_MENU, self.on_delete_all, item)
|
2220
|
+
|
2221
|
+
menu.AppendSeparator()
|
2222
|
+
item = menu.Append(wx.ID_ANY, _("Sort by..."), "", wx.ITEM_NORMAL)
|
2223
|
+
menu.Enable(item.GetId(), False)
|
2224
|
+
|
2225
|
+
def set_sort_key(sortvalue):
|
2226
|
+
local_value = sortvalue
|
2227
|
+
|
2228
|
+
def sort_handler(event):
|
2229
|
+
self.categorisation = local_value
|
2230
|
+
self.retrieve_material_list(reload=False, setter=self.active_material)
|
2231
|
+
|
2232
|
+
return sort_handler
|
2233
|
+
|
2234
|
+
item = menu.Append(wx.ID_ANY, _("Material"), "", wx.ITEM_RADIO)
|
2235
|
+
item.Check(bool(self.categorisation == 0))
|
2236
|
+
self.Bind(wx.EVT_MENU, set_sort_key(0), item)
|
2237
|
+
item = menu.Append(wx.ID_ANY, _("Laser"), "", wx.ITEM_RADIO)
|
2238
|
+
item.Check(bool(self.categorisation == 1))
|
2239
|
+
self.Bind(wx.EVT_MENU, set_sort_key(1), item)
|
2240
|
+
item = menu.Append(wx.ID_ANY, _("Thickness"), "", wx.ITEM_RADIO)
|
2241
|
+
item.Check(bool(self.categorisation == 2))
|
2242
|
+
self.Bind(wx.EVT_MENU, set_sort_key(2), item)
|
2243
|
+
menu.AppendSeparator()
|
2244
|
+
|
2245
|
+
def on_expand(flag):
|
2246
|
+
def exp_handler(event):
|
2247
|
+
tree = self.tree_library
|
2248
|
+
tree_root = tree.GetRootItem()
|
2249
|
+
child, cookie = tree.GetFirstChild(tree_root)
|
2250
|
+
while child.IsOk():
|
2251
|
+
if local_flag:
|
2252
|
+
tree.ExpandAllChildren(child)
|
2253
|
+
else:
|
2254
|
+
tree.CollapseAllChildren(child)
|
2255
|
+
child, cookie = tree.GetNextChild(tree_root, cookie)
|
2256
|
+
|
2257
|
+
local_flag = flag
|
2258
|
+
return exp_handler
|
2259
|
+
|
2260
|
+
item = menu.Append(wx.ID_ANY, _("Expand all"), "", wx.ITEM_NORMAL)
|
2261
|
+
self.Bind(wx.EVT_MENU, on_expand(True), item)
|
2262
|
+
item = menu.Append(wx.ID_ANY, _("Collapse all"), "", wx.ITEM_NORMAL)
|
2263
|
+
self.Bind(wx.EVT_MENU, on_expand(False), item)
|
2264
|
+
|
2265
|
+
self.PopupMenu(menu)
|
2266
|
+
menu.Destroy()
|
2267
|
+
|
2268
|
+
def on_preview_rightclick(self, event):
|
2269
|
+
# A couple of basic operations
|
2270
|
+
def max_keynum(secname):
|
2271
|
+
maxfound = 0
|
2272
|
+
for subsection in self.op_data.derivable(secname):
|
2273
|
+
parts = subsection.split(" ")
|
2274
|
+
number = parts[-1]
|
2275
|
+
try:
|
2276
|
+
nr = int(number)
|
2277
|
+
if nr > maxfound:
|
2278
|
+
maxfound = nr
|
2279
|
+
except ValueError:
|
2280
|
+
pass
|
2281
|
+
return maxfound
|
2282
|
+
|
2283
|
+
def newkey():
|
2284
|
+
sect = self.active_material
|
2285
|
+
# fetch all section names...
|
2286
|
+
sect_num = max_keynum(sect) + 1
|
2287
|
+
section_name = f"{sect} {sect_num:0>6}"
|
2288
|
+
return section_name
|
2289
|
+
|
2290
|
+
event.Skip()
|
2291
|
+
if self.active_material is None:
|
2292
|
+
return
|
2293
|
+
key = None
|
2294
|
+
try:
|
2295
|
+
# main click
|
2296
|
+
listindex = event.Index
|
2297
|
+
if listindex >= 0:
|
2298
|
+
index = self.list_preview.GetItemData(listindex)
|
2299
|
+
key = self.get_nth_operation(index)
|
2300
|
+
except AttributeError:
|
2301
|
+
# Column click
|
2302
|
+
pass
|
2303
|
+
|
2304
|
+
menu = wx.Menu()
|
2305
|
+
|
2306
|
+
def on_menu_popup_recolor(coloroption, op_section):
|
2307
|
+
def color_handler(*args):
|
2308
|
+
def next_color(primary, secondary, tertiary, delta=32):
|
2309
|
+
r = primary
|
2310
|
+
b = secondary
|
2311
|
+
g = tertiary
|
2312
|
+
|
2313
|
+
b += delta
|
2314
|
+
if b > 255:
|
2315
|
+
b = 0
|
2316
|
+
r -= delta
|
2317
|
+
if r < 0:
|
2318
|
+
r = 255
|
2319
|
+
g += delta
|
2320
|
+
if g > 255:
|
2321
|
+
g = 0
|
2322
|
+
return r, b, g
|
2323
|
+
|
2324
|
+
colors = [0, 0, 0]
|
2325
|
+
primary = 0
|
2326
|
+
secondary = 1
|
2327
|
+
tertiary = 2
|
2328
|
+
if coloropt == "red":
|
2329
|
+
colors[0] = 255
|
2330
|
+
primary = 0
|
2331
|
+
secondary = 1
|
2332
|
+
tertiary = 2
|
2333
|
+
if coloropt == "blue":
|
2334
|
+
colors[1] = 255
|
2335
|
+
primary = 1
|
2336
|
+
secondary = 2
|
2337
|
+
tertiary = 0
|
2338
|
+
if coloropt == "green":
|
2339
|
+
colors[2] = 255
|
2340
|
+
primary = 2
|
2341
|
+
secondary = 1
|
2342
|
+
tertiary = 0
|
2343
|
+
if coloropt == "black":
|
2344
|
+
colors = [0, 0, 0]
|
2345
|
+
primary = 0
|
2346
|
+
secondary = 1
|
2347
|
+
tertiary = 2
|
2348
|
+
settings = self.op_data
|
2349
|
+
target_type = settings.read_persistent(str, key, "type", "")
|
2350
|
+
idx = 0
|
2351
|
+
for subsection in settings.derivable(self.active_material):
|
2352
|
+
if subsection.endswith(" info"):
|
2353
|
+
continue
|
2354
|
+
optype = settings.read_persistent(str, subsection, "type", "")
|
2355
|
+
if optype is None or optype != target_type:
|
2356
|
+
continue
|
2357
|
+
idx += 1
|
2358
|
+
opcolor = Color(red=colors[0], green=colors[2], blue=colors[1])
|
2359
|
+
settings.write_persistent(subsection, "color", str(opcolor))
|
2360
|
+
if coloropt=="black":
|
2361
|
+
colors[primary] += 32
|
2362
|
+
if colors[primary] > 255:
|
2363
|
+
colors[primary] = 0
|
2364
|
+
colors[secondary] = colors[primary]
|
2365
|
+
colors[tertiary] = colors[primary]
|
2366
|
+
else:
|
2367
|
+
colors[primary], colors[secondary], colors[tertiary] = next_color(colors[primary], colors[secondary], colors[tertiary], delta=64)
|
2368
|
+
|
2369
|
+
settings.write_configuration()
|
2370
|
+
self.fill_preview()
|
2371
|
+
|
2372
|
+
coloropt = coloroption.lower()
|
2373
|
+
key = op_section
|
2374
|
+
return color_handler
|
2375
|
+
|
2376
|
+
def on_menu_popup_delete(op_section):
|
2377
|
+
def remove_handler(*args):
|
2378
|
+
settings = self.op_data
|
2379
|
+
# print (f"Remove {sect}")
|
2380
|
+
settings.clear_persistent(sect)
|
2381
|
+
self.fill_preview()
|
2382
|
+
|
2383
|
+
sect = op_section
|
2384
|
+
return remove_handler
|
2385
|
+
|
2386
|
+
def on_menu_popup_duplicate(op_section):
|
2387
|
+
def dup_handler(*args):
|
2388
|
+
settings = self.op_data
|
2389
|
+
# print (f"Remove {sect}")
|
2390
|
+
nkey = newkey()
|
2391
|
+
for info in settings.keylist(sect):
|
2392
|
+
secdesc = settings.read_persistent(str, sect, info, "")
|
2393
|
+
if info == "id":
|
2394
|
+
continue
|
2395
|
+
if info == "label":
|
2396
|
+
idx = 0
|
2397
|
+
if secdesc.endswith(")") and secdesc[-2] in (
|
2398
|
+
str(i) for i in range(0, 10)
|
2399
|
+
):
|
2400
|
+
i = secdesc.rfind("(")
|
2401
|
+
if i >= 0:
|
2402
|
+
try:
|
2403
|
+
s = secdesc[i + 1 :]
|
2404
|
+
t = secdesc[:i]
|
2405
|
+
idx = int(s[:-1])
|
2406
|
+
secdesc = t
|
2407
|
+
except ValueError:
|
2408
|
+
pass
|
2409
|
+
secdesc += f"({idx + 1})"
|
2410
|
+
settings.write_persistent(nkey, info, secdesc)
|
2411
|
+
|
2412
|
+
on_menu_popup_missing()
|
2413
|
+
settings.write_configuration()
|
2414
|
+
self.fill_preview()
|
2415
|
+
|
2416
|
+
sect = op_section
|
2417
|
+
return dup_handler
|
2418
|
+
|
2419
|
+
def on_menu_popup_newop(op_dict):
|
2420
|
+
def add_handler(*args):
|
2421
|
+
settings = self.op_data
|
2422
|
+
# print (f"Remove {sect}")
|
2423
|
+
nkey = newkey()
|
2424
|
+
for key, value in opd.items():
|
2425
|
+
settings.write_persistent(nkey, key, value)
|
2426
|
+
|
2427
|
+
on_menu_popup_missing()
|
2428
|
+
settings.write_configuration()
|
2429
|
+
self.fill_preview()
|
2430
|
+
|
2431
|
+
opd = op_dict
|
2432
|
+
return add_handler
|
2433
|
+
|
2434
|
+
def on_menu_popup_apply_to_tree(op_section):
|
2435
|
+
def apply_to_tree_handler(*args):
|
2436
|
+
settings = self.op_data
|
2437
|
+
op_type = settings.read_persistent(str, sect, "type")
|
2438
|
+
op_attr = dict()
|
2439
|
+
for key in settings.keylist(sect):
|
2440
|
+
if key == "type":
|
2441
|
+
# We need to ignore it to avoid double attribute issues.
|
2442
|
+
continue
|
2443
|
+
content = settings.read_persistent(str, sect, key)
|
2444
|
+
op_attr[key] = content
|
2445
|
+
try:
|
2446
|
+
targetop = Node().create(type=op_type, **op_attr)
|
2447
|
+
except ValueError:
|
2448
|
+
# Attempted to create a non-bootstrapped node type.
|
2449
|
+
return
|
2450
|
+
op_id = targetop.id
|
2451
|
+
if op_id is None:
|
2452
|
+
# WTF, that should not be the case
|
2453
|
+
op_list = [targetop]
|
2454
|
+
self.context.elements.validate_ids(nodelist=op_list, generic=False)
|
2455
|
+
newone = True
|
2456
|
+
for op in self.context.elements.ops():
|
2457
|
+
# Already existing?
|
2458
|
+
if op.id == targetop.id:
|
2459
|
+
newone = False
|
2460
|
+
op_attr["type"] = targetop.type
|
2461
|
+
op.replace_node(keep_children=True, **op_attr)
|
2462
|
+
break
|
2463
|
+
if newone:
|
2464
|
+
try:
|
2465
|
+
self.context.elements.op_branch.add_node(targetop)
|
2466
|
+
except ValueError:
|
2467
|
+
# This happens when he have somehow lost sync with the node,
|
2468
|
+
# and we try to add a node that is already added...
|
2469
|
+
# In principle this should be covered by the check
|
2470
|
+
# above, but you never know
|
2471
|
+
pass
|
2472
|
+
self.context.signal("rebuild_tree")
|
2473
|
+
|
2474
|
+
sect = op_section
|
2475
|
+
return apply_to_tree_handler
|
2476
|
+
|
2477
|
+
def on_menu_popup_apply_to_statusbar(op_section):
|
2478
|
+
def apply_to_tree_handler(*args):
|
2479
|
+
settings = self.op_data
|
2480
|
+
op_type = settings.read_persistent(str, sect, "type")
|
2481
|
+
op_attr = dict()
|
2482
|
+
for key in settings.keylist(sect):
|
2483
|
+
if key == "type":
|
2484
|
+
# We need to ignore it to avoid double attribute issues.
|
2485
|
+
continue
|
2486
|
+
content = settings.read_persistent(str, sect, key)
|
2487
|
+
op_attr[key] = content
|
2488
|
+
try:
|
2489
|
+
targetop = Node().create(type=op_type, **op_attr)
|
2490
|
+
except ValueError:
|
2491
|
+
# Attempted to create a non-bootstrapped node type.
|
2492
|
+
return
|
2493
|
+
op_id = targetop.id
|
2494
|
+
if op_id is None:
|
2495
|
+
# WTF, that should not be the case
|
2496
|
+
op_list = [targetop]
|
2497
|
+
self.context.elements.validate_ids(nodelist=op_list, generic=False)
|
2498
|
+
newone = True
|
2499
|
+
for idx, op in enumerate(self.context.elements.default_operations):
|
2500
|
+
# Already existing?
|
2501
|
+
if op.id == targetop.id:
|
2502
|
+
newone = False
|
2503
|
+
self.context.elements.default_operations[idx] = targetop
|
2504
|
+
break
|
2505
|
+
if newone:
|
2506
|
+
self.context.elements.default_operations.append(targetop)
|
2507
|
+
self.context.signal("default_operations")
|
2508
|
+
|
2509
|
+
sect = op_section
|
2510
|
+
return apply_to_tree_handler
|
2511
|
+
|
2512
|
+
def on_menu_popup_missing(*args):
|
2513
|
+
if self.active_material is None:
|
2514
|
+
return
|
2515
|
+
op_list, op_info = self.context.elements.load_persistent_op_list(
|
2516
|
+
self.active_material,
|
2517
|
+
use_settings=self.op_data,
|
2518
|
+
)
|
2519
|
+
if len(op_list) == 0:
|
2520
|
+
return
|
2521
|
+
changes = False
|
2522
|
+
# Which ones do we have already?
|
2523
|
+
replace_mk_pattern = True
|
2524
|
+
mkpattern = "meerk40t:"
|
2525
|
+
uid = list()
|
2526
|
+
for op in op_list:
|
2527
|
+
if not hasattr(op, "id"):
|
2528
|
+
continue
|
2529
|
+
if not op.id:
|
2530
|
+
continue
|
2531
|
+
if replace_mk_pattern and op.id.startswith(mkpattern):
|
2532
|
+
continue
|
2533
|
+
uid.append(op.id)
|
2534
|
+
for op in op_list:
|
2535
|
+
if hasattr(op, "label") and op.label is None:
|
2536
|
+
pattern = op.type
|
2537
|
+
if pattern.startswith("op "):
|
2538
|
+
pattern = pattern[3].upper() + pattern[4:]
|
2539
|
+
s1 = ""
|
2540
|
+
s2 = ""
|
2541
|
+
if hasattr(op, "power"):
|
2542
|
+
s1 = "{percent}"
|
2543
|
+
if hasattr(op, "speed") and op.speed is not None:
|
2544
|
+
s2 = "{speed}mm/s"
|
2545
|
+
if s1 or s2:
|
2546
|
+
pattern += f" ({s1}{', ' if s1 and s2 else ''}{s2})"
|
2547
|
+
op.label = pattern
|
2548
|
+
changes = True
|
2549
|
+
replace_id = True
|
2550
|
+
if not hasattr(op, "id"):
|
2551
|
+
# oldid = "unknown"
|
2552
|
+
replace_id = False
|
2553
|
+
else:
|
2554
|
+
# oldid = op.id
|
2555
|
+
if op.id and not (
|
2556
|
+
replace_mk_pattern and op.id.startswith(mkpattern)
|
2557
|
+
):
|
2558
|
+
replace_id = False
|
2559
|
+
# print (oldid, replace_id)
|
2560
|
+
if replace_id:
|
2561
|
+
# oldid = op.id
|
2562
|
+
changes = True
|
2563
|
+
if op.type.startswith("op "):
|
2564
|
+
pattern = op.type[3].upper()
|
2565
|
+
else:
|
2566
|
+
pattern = op.type[0:2].upper()
|
2567
|
+
idx = 1
|
2568
|
+
while f"{pattern}{idx}" in uid:
|
2569
|
+
idx += 1
|
2570
|
+
op.id = f"{pattern}{idx}"
|
2571
|
+
# print (f"{oldid} -> {op.id}")
|
2572
|
+
uid.append(op.id)
|
2573
|
+
|
2574
|
+
if changes:
|
2575
|
+
self.context.elements.save_persistent_operations_list(
|
2576
|
+
self.active_material,
|
2577
|
+
op_list,
|
2578
|
+
op_info,
|
2579
|
+
flush=False,
|
2580
|
+
use_settings=self.op_data,
|
2581
|
+
)
|
2582
|
+
self.op_data.write_configuration()
|
2583
|
+
self.fill_preview()
|
2584
|
+
|
2585
|
+
op_dict = {
|
2586
|
+
"type": "op raster",
|
2587
|
+
"speed": "300",
|
2588
|
+
"power": "1000",
|
2589
|
+
"label": "Raster ({percent}, {speed}mm/s)",
|
2590
|
+
"color": "#000000",
|
2591
|
+
"passes": "1",
|
2592
|
+
}
|
2593
|
+
if self.is_balor:
|
2594
|
+
op_dict["frequency"] = "35"
|
2595
|
+
item = menu.Append(wx.ID_ANY, _("Add Raster"), "", wx.ITEM_NORMAL)
|
2596
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_newop(op_dict), item)
|
2597
|
+
|
2598
|
+
op_dict = {
|
2599
|
+
"type": "op image",
|
2600
|
+
"speed": "300",
|
2601
|
+
"power": "1000",
|
2602
|
+
"label": "Image ({percent}, {speed}mm/s)",
|
2603
|
+
"color": "#000000",
|
2604
|
+
}
|
2605
|
+
item = menu.Append(wx.ID_ANY, _("Add Image"), "", wx.ITEM_NORMAL)
|
2606
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_newop(op_dict), item)
|
2607
|
+
|
2608
|
+
op_dict = {
|
2609
|
+
"type": "op engrave",
|
2610
|
+
"speed": "50",
|
2611
|
+
"power": "1000",
|
2612
|
+
"label": "Engrave ({percent}, {speed}mm/s)",
|
2613
|
+
"color": "#0000FF",
|
2614
|
+
}
|
2615
|
+
if self.is_balor:
|
2616
|
+
op_dict["frequency"] = "35"
|
2617
|
+
item = menu.Append(wx.ID_ANY, _("Add Engrave"), "", wx.ITEM_NORMAL)
|
2618
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_newop(op_dict), item)
|
2619
|
+
op_dict = {
|
2620
|
+
"type": "op cut",
|
2621
|
+
"speed": "5",
|
2622
|
+
"power": "1000",
|
2623
|
+
"label": "Cut ({percent}, {speed}mm/s)",
|
2624
|
+
"color": "#FF0000",
|
2625
|
+
}
|
2626
|
+
if self.is_balor:
|
2627
|
+
op_dict["frequency"] = "35"
|
2628
|
+
item = menu.Append(wx.ID_ANY, _("Add Cut"), "", wx.ITEM_NORMAL)
|
2629
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_newop(op_dict), item)
|
2630
|
+
|
2631
|
+
if key:
|
2632
|
+
menu.AppendSeparator()
|
2633
|
+
item = menu.Append(wx.ID_ANY, _("Duplicate"), "", wx.ITEM_NORMAL)
|
2634
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_duplicate(key), item)
|
2635
|
+
item = menu.Append(wx.ID_ANY, _("Delete"), "", wx.ITEM_NORMAL)
|
2636
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_delete(key), item)
|
2637
|
+
|
2638
|
+
menu.AppendSeparator()
|
2639
|
+
|
2640
|
+
item = menu.Append(wx.ID_ANY, _("Load into Tree"), "", wx.ITEM_NORMAL)
|
2641
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_apply_to_tree(key), item)
|
2642
|
+
|
2643
|
+
settings = self.op_data
|
2644
|
+
op_type = settings.read_persistent(str, key, "type")
|
2645
|
+
if op_type.startswith("op "):
|
2646
|
+
item = menu.Append(
|
2647
|
+
wx.ID_ANY, _("Use for statusbar"), "", wx.ITEM_NORMAL
|
2648
|
+
)
|
2649
|
+
menu.Enable(item.GetId(), bool(self.active_material is not None))
|
2650
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_apply_to_statusbar(key), item)
|
2651
|
+
try:
|
2652
|
+
info = self.opinfo[op_type]
|
2653
|
+
except KeyError:
|
2654
|
+
info = self.opinfo["generic"]
|
2655
|
+
submenu = wx.Menu()
|
2656
|
+
for coloroption in ("Red", "Blue", "Green", "Black"):
|
2657
|
+
sitem = submenu.Append(wx.ID_ANY, _(coloroption), "", wx.ITEM_NORMAL)
|
2658
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_recolor(coloroption, key), sitem)
|
2659
|
+
menu.AppendSubMenu(submenu, _("Color all {type}").format(type=info[0]))
|
2660
|
+
|
2661
|
+
|
2662
|
+
if self.list_preview.GetItemCount() > 0:
|
2663
|
+
menu.AppendSeparator()
|
2664
|
+
|
2665
|
+
item = menu.Append(
|
2666
|
+
wx.ID_ANY, _("Fill missing ids/label"), "", wx.ITEM_NORMAL
|
2667
|
+
)
|
2668
|
+
self.Bind(wx.EVT_MENU, on_menu_popup_missing, item)
|
2669
|
+
|
2670
|
+
self.PopupMenu(menu)
|
2671
|
+
menu.Destroy()
|
2672
|
+
|
2673
|
+
def on_resize(self, event):
|
2674
|
+
size = self.GetClientSize()
|
2675
|
+
if size[0] != 0 and size[1] != 0:
|
2676
|
+
self.tree_library.SetMaxSize(wx.Size(-1, int(0.4 * size[1])))
|
2677
|
+
self.list_preview.SetMaxSize(wx.Size(-1, int(0.4 * size[1])))
|
2678
|
+
|
2679
|
+
# Resize the columns in the listctrl
|
2680
|
+
size = self.list_preview.GetSize()
|
2681
|
+
if size[0] == 0 or size[1] == 0:
|
2682
|
+
return
|
2683
|
+
remaining = size[0] * 0.8
|
2684
|
+
# 0 "#"
|
2685
|
+
# 1 "Operation"
|
2686
|
+
# 2 "Id"
|
2687
|
+
# 3 "Label"
|
2688
|
+
# 4 "Power"
|
2689
|
+
# 5 "Speed"
|
2690
|
+
# 6 "Frequency"
|
2691
|
+
# 7 "Passes"
|
2692
|
+
if self.is_balor:
|
2693
|
+
p1 = 0.15
|
2694
|
+
p2 = 0.35
|
2695
|
+
p3 = (1.0 - p1 - p2) / 5
|
2696
|
+
p4 = p3
|
2697
|
+
else:
|
2698
|
+
p1 = 0.15
|
2699
|
+
p2 = 0.40
|
2700
|
+
p3 = (1.0 - p1 - p2) / 4
|
2701
|
+
p4 = 0
|
2702
|
+
self.list_preview.SetColumnWidth(0, int(p3 * remaining))
|
2703
|
+
self.list_preview.SetColumnWidth(1, int(p1 * remaining))
|
2704
|
+
self.list_preview.SetColumnWidth(2, int(p1 * remaining))
|
2705
|
+
self.list_preview.SetColumnWidth(3, int(p2 * remaining))
|
2706
|
+
self.list_preview.SetColumnWidth(4, int(p3 * remaining))
|
2707
|
+
self.list_preview.SetColumnWidth(5, int(p3 * remaining))
|
2708
|
+
self.list_preview.SetColumnWidth(6, int(p4 * remaining))
|
2709
|
+
self.list_preview.SetColumnWidth(7, int(p3 * remaining))
|
2710
|
+
|
2711
|
+
def before_operation_update(self, event):
|
2712
|
+
list_id = event.GetIndex() # Get the current row
|
2713
|
+
if list_id < 0:
|
2714
|
+
event.Veto()
|
2715
|
+
return
|
2716
|
+
col_id = event.GetColumn() # Get the current column
|
2717
|
+
ok = True
|
2718
|
+
try:
|
2719
|
+
index = self.list_preview.GetItemData(list_id)
|
2720
|
+
key = self.get_nth_operation(index)
|
2721
|
+
entry = self.operation_list[key]
|
2722
|
+
if not entry[0].startswith("op "):
|
2723
|
+
ok = False
|
2724
|
+
except (AttributeError, KeyError):
|
2725
|
+
ok = False
|
2726
|
+
if col_id not in range(2, 7 + 1):
|
2727
|
+
ok = False
|
2728
|
+
if col_id == 6 and not self.is_balor:
|
2729
|
+
ok = False
|
2730
|
+
if ok:
|
2731
|
+
event.Allow()
|
2732
|
+
else:
|
2733
|
+
event.Veto()
|
2734
|
+
|
2735
|
+
def on_operation_update(self, event):
|
2736
|
+
list_id = event.GetIndex() # Get the current row
|
2737
|
+
col_id = event.GetColumn() # Get the current column
|
2738
|
+
new_data = event.GetLabel() # Get the changed data
|
2739
|
+
index = self.list_preview.GetItemData(list_id)
|
2740
|
+
key = self.get_nth_operation(index)
|
2741
|
+
|
2742
|
+
if list_id >= 0 and col_id in range(2, 7 + 1):
|
2743
|
+
if col_id == 2:
|
2744
|
+
# id
|
2745
|
+
self.op_data.write_persistent(key, "id", new_data)
|
2746
|
+
elif col_id == 3:
|
2747
|
+
# label
|
2748
|
+
self.op_data.write_persistent(key, "label", new_data)
|
2749
|
+
elif col_id == 4:
|
2750
|
+
# power
|
2751
|
+
try:
|
2752
|
+
if new_data.endswith("%"):
|
2753
|
+
new_data = float(new_data[:-1]) * 10.0
|
2754
|
+
else:
|
2755
|
+
new_data = float(new_data)
|
2756
|
+
self.op_data.write_persistent(key, "power", new_data)
|
2757
|
+
new_data = f"{new_data:.0f}"
|
2758
|
+
except ValueError:
|
2759
|
+
event.Veto()
|
2760
|
+
return
|
2761
|
+
elif col_id == 5:
|
2762
|
+
# speed
|
2763
|
+
try:
|
2764
|
+
new_data = float(new_data)
|
2765
|
+
self.op_data.write_persistent(key, "speed", new_data)
|
2766
|
+
new_data = f"{new_data:.1f}"
|
2767
|
+
except ValueError:
|
2768
|
+
event.Veto()
|
2769
|
+
return
|
2770
|
+
elif col_id == 6:
|
2771
|
+
# frequency
|
2772
|
+
try:
|
2773
|
+
new_data = float(new_data)
|
2774
|
+
self.op_data.write_persistent(key, "frequency", new_data)
|
2775
|
+
new_data = f"{new_data:.0f}"
|
2776
|
+
except ValueError:
|
2777
|
+
event.Veto()
|
2778
|
+
return
|
2779
|
+
elif col_id == 7:
|
2780
|
+
# Passes
|
2781
|
+
try:
|
2782
|
+
new_data = int(new_data)
|
2783
|
+
if new_data < 1:
|
2784
|
+
new_data = 1
|
2785
|
+
self.op_data.write_persistent(key, "passes_custom", bool(new_data != 1))
|
2786
|
+
self.op_data.write_persistent(key, "passes", new_data)
|
2787
|
+
new_data = f"{new_data}"
|
2788
|
+
except ValueError:
|
2789
|
+
event.Veto()
|
2790
|
+
return
|
2791
|
+
# Set the new data in the listctrl
|
2792
|
+
self.op_data.write_configuration()
|
2793
|
+
self.list_preview.SetItem(list_id, col_id, new_data)
|
2794
|
+
|
2795
|
+
def set_parent(self, par_panel):
|
2796
|
+
self.parent_panel = par_panel
|
2797
|
+
|
2798
|
+
def pane_show(self):
|
2799
|
+
self.update_list(reload=True)
|
2800
|
+
|
2801
|
+
def pane_hide(self):
|
2802
|
+
pass
|
2803
|
+
|
2804
|
+
|
2805
|
+
class ImportPanel(wx.Panel):
|
2806
|
+
"""
|
2807
|
+
Displays a how-to summary
|
2808
|
+
"""
|
2809
|
+
|
2810
|
+
def __init__(self, *args, context=None, **kwds):
|
2811
|
+
kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
|
2812
|
+
wx.Panel.__init__(self, *args, **kwds)
|
2813
|
+
self.context = context
|
2814
|
+
self.context.themes.set_window_colors(self)
|
2815
|
+
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
2816
|
+
label = wxStaticText(self, wx.ID_ANY, "UNDER CONSTRUCTION")
|
2817
|
+
main_sizer.Add(label, 0, wx.EXPAND, 0)
|
2818
|
+
self.SetSizer(main_sizer)
|
2819
|
+
|
2820
|
+
|
2821
|
+
class AboutPanel(wx.Panel):
|
2822
|
+
"""
|
2823
|
+
Displays a how-to summary
|
2824
|
+
"""
|
2825
|
+
|
2826
|
+
def __init__(self, *args, context=None, **kwds):
|
2827
|
+
kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
|
2828
|
+
wx.Panel.__init__(self, *args, **kwds)
|
2829
|
+
self.context = context
|
2830
|
+
self.context.themes.set_window_colors(self)
|
2831
|
+
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
2832
|
+
info_box = StaticBoxSizer(self, wx.ID_ANY, _("How to use..."), wx.VERTICAL)
|
2833
|
+
self.parent_panel = None
|
2834
|
+
s = self.context.asset("material_howto")
|
2835
|
+
info_label = TextCtrl(
|
2836
|
+
self, wx.ID_ANY, value=s, style=wx.TE_READONLY | wx.TE_MULTILINE
|
2837
|
+
)
|
2838
|
+
fsize = 16 if system() == "Darwin" else 10
|
2839
|
+
font = wx.Font(
|
2840
|
+
fsize,
|
2841
|
+
wx.FONTFAMILY_DEFAULT,
|
2842
|
+
wx.FONTSTYLE_NORMAL,
|
2843
|
+
wx.FONTWEIGHT_NORMAL,
|
2844
|
+
)
|
2845
|
+
info_label.SetFont(font)
|
2846
|
+
info_label.SetBackgroundColour(self.GetBackgroundColour())
|
2847
|
+
info_box.Add(info_label, 1, wx.EXPAND, 0)
|
2848
|
+
main_sizer.Add(info_box, 1, wx.EXPAND, 0)
|
2849
|
+
self.SetSizer(main_sizer)
|
2850
|
+
self.Layout()
|
2851
|
+
|
2852
|
+
def set_parent(self, par_panel):
|
2853
|
+
self.parent_panel = par_panel
|
2854
|
+
|
2855
|
+
|
2856
|
+
class MaterialManager(MWindow):
|
2857
|
+
def __init__(self, *args, **kwds):
|
2858
|
+
super().__init__(860, 800, *args, **kwds)
|
2859
|
+
|
2860
|
+
self.panel_library = MaterialPanel(self, wx.ID_ANY, context=self.context)
|
2861
|
+
# self.panel_import = ImportPanel(self, wx.ID_ANY, context=self.context)
|
2862
|
+
self.panel_about = AboutPanel(self, wx.ID_ANY, context=self.context)
|
2863
|
+
|
2864
|
+
self.panel_library.set_parent(self)
|
2865
|
+
# self.panel_import.set_parent(self)
|
2866
|
+
self.panel_about.set_parent(self)
|
2867
|
+
|
2868
|
+
_icon = wx.NullIcon
|
2869
|
+
_icon.CopyFromBitmap(icon_library.GetBitmap())
|
2870
|
+
self.SetIcon(_icon)
|
2871
|
+
self.notebook_main = wx.aui.AuiNotebook(
|
2872
|
+
self,
|
2873
|
+
-1,
|
2874
|
+
style=wx.aui.AUI_NB_TAB_EXTERNAL_MOVE
|
2875
|
+
| wx.aui.AUI_NB_SCROLL_BUTTONS
|
2876
|
+
| wx.aui.AUI_NB_TAB_SPLIT
|
2877
|
+
| wx.aui.AUI_NB_TAB_MOVE,
|
2878
|
+
)
|
2879
|
+
# ARGGH, the color setting via the ArtProvider does only work
|
2880
|
+
# if you set the tabs to the bottom! wx.aui.AUI_NB_BOTTOM
|
2881
|
+
|
2882
|
+
self.window_context.themes.set_window_colors(self.notebook_main)
|
2883
|
+
bg_std = self.window_context.themes.get("win_bg")
|
2884
|
+
bg_active = self.window_context.themes.get("highlight")
|
2885
|
+
self.notebook_main.GetArtProvider().SetColour(bg_std)
|
2886
|
+
self.notebook_main.GetArtProvider().SetActiveColour(bg_active)
|
2887
|
+
|
2888
|
+
self.sizer.Add(self.notebook_main, 1, wx.EXPAND, 0)
|
2889
|
+
self.notebook_main.AddPage(self.panel_library, _("Library"))
|
2890
|
+
# self.notebook_main.AddPage(self.panel_import, _("Import"))
|
2891
|
+
self.notebook_main.AddPage(self.panel_about, _("How to use"))
|
2892
|
+
# begin wxGlade: Keymap.__set_properties
|
2893
|
+
self.DragAcceptFiles(True)
|
2894
|
+
self.Bind(wx.EVT_DROP_FILES, self.on_drop_file)
|
2895
|
+
self.SetTitle(_("Material Library"))
|
2896
|
+
self.restore_aspect(honor_initial_values=True)
|
2897
|
+
|
2898
|
+
def on_drop_file(self, event):
|
2899
|
+
"""
|
2900
|
+
Drop file handler
|
2901
|
+
Accepts only a single file drop.
|
2902
|
+
"""
|
2903
|
+
for pathname in event.GetFiles():
|
2904
|
+
self.panel_library.on_import(None, filename=pathname)
|
2905
|
+
break
|
2906
|
+
|
2907
|
+
def delegates(self):
|
2908
|
+
yield self.panel_library
|
2909
|
+
# yield self.panel_import
|
2910
|
+
yield self.panel_about
|
2911
|
+
|
2912
|
+
@staticmethod
|
2913
|
+
def sub_register(kernel):
|
2914
|
+
kernel.register(
|
2915
|
+
"button/config/Material",
|
2916
|
+
{
|
2917
|
+
"label": _("Material Library"),
|
2918
|
+
"icon": icon_library,
|
2919
|
+
"tip": _("Manages Material Settings"),
|
2920
|
+
"help": "materialmanager",
|
2921
|
+
"action": lambda v: kernel.console("window toggle MatManager\n"),
|
2922
|
+
},
|
2923
|
+
)
|
2924
|
+
|
2925
|
+
def window_open(self):
|
2926
|
+
pass
|
2927
|
+
|
2928
|
+
def window_close(self):
|
2929
|
+
pass
|
2930
|
+
|
2931
|
+
def edit_message(self, msg):
|
2932
|
+
self.panel_library.edit_message(msg)
|
2933
|
+
|
2934
|
+
def populate_gui(self):
|
2935
|
+
self.panel_library.populate_gui()
|
2936
|
+
|
2937
|
+
@staticmethod
|
2938
|
+
def submenu():
|
2939
|
+
# Suppress to avoid double menu-appearance
|
2940
|
+
return "", "Material Library", True
|
2941
|
+
|
2942
|
+
@staticmethod
|
2943
|
+
def helptext():
|
2944
|
+
return _("Manage and choose material specific settings")
|