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,403 @@
|
|
1
|
+
"""
|
2
|
+
This module provides interfaces to coolants (airassist or others).
|
3
|
+
External modules can register the existence of an airassist.
|
4
|
+
Devices can then claim ownership of such a registered device
|
5
|
+
and react on device specific coolant commands
|
6
|
+
"""
|
7
|
+
|
8
|
+
|
9
|
+
class Coolants:
|
10
|
+
"""
|
11
|
+
Base class
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, kernel):
|
15
|
+
self.kernel = kernel
|
16
|
+
self._coolants = []
|
17
|
+
# {
|
18
|
+
# "id": cool_id,
|
19
|
+
# "label": label,
|
20
|
+
# "function": cool_function,
|
21
|
+
# "config": config_function,
|
22
|
+
# "devices": [],
|
23
|
+
# "constraints": constraints,
|
24
|
+
# }
|
25
|
+
|
26
|
+
def remove_coolant_method(self, cool_id):
|
27
|
+
cool_id = cool_id.lower()
|
28
|
+
if cool_id in ("popup", "gcode_m7", "gcode_m8"):
|
29
|
+
# builtin...
|
30
|
+
return 0
|
31
|
+
to_be_deleted = []
|
32
|
+
for idx, cool in enumerate(self._coolants):
|
33
|
+
if cool_id == cool["id"]:
|
34
|
+
to_be_deleted.insert(0, idx)
|
35
|
+
for idx in to_be_deleted:
|
36
|
+
self._coolants.pop(idx)
|
37
|
+
return len(to_be_deleted)
|
38
|
+
|
39
|
+
def register_coolant_method(
|
40
|
+
self, cool_id, cool_function, config_function=None, label=None, constraints=None
|
41
|
+
):
|
42
|
+
"""
|
43
|
+
Announces the availability of a new coolant method.
|
44
|
+
Mainly used from plugins.
|
45
|
+
Args:
|
46
|
+
cool_id ([str]): unique identifier for the method. There are two predefined
|
47
|
+
methods generated from within meerk40t:
|
48
|
+
a) 'popup' which just popups an instruction message
|
49
|
+
to turn on / off the airassist
|
50
|
+
b) 'grbl' which uses the internal M7 / M9 commands ofa grbl compatible
|
51
|
+
device. So it has a 'grbl' constraint (see below)
|
52
|
+
cool_function ([function]): The routine to call when coolant needs
|
53
|
+
to be activated / deactivated. This routine expects two parameters:
|
54
|
+
def coolant_method (device, flag)
|
55
|
+
device: device that uses the routine
|
56
|
+
flag: indicator to turn it on (True) or off (False)
|
57
|
+
config_function ([function], optional): The routine to call, if you want
|
58
|
+
to edit parameters for this method. It expects one
|
59
|
+
Defaults to None, indicating it has no such function.
|
60
|
+
label ([str], optional): A description of the method.
|
61
|
+
Defaults to None which will use the cool_id as label
|
62
|
+
constraints ([str], optional): [description].
|
63
|
+
Defaults to None, so available to all devices.
|
64
|
+
Returns:
|
65
|
+
[type]: [description]
|
66
|
+
"""
|
67
|
+
cool_id = cool_id.lower()
|
68
|
+
|
69
|
+
for cool in self._coolants:
|
70
|
+
# A coolant method with that id had already been registered
|
71
|
+
# so we just update it. Honestly this should not happen.
|
72
|
+
if cool_id == cool["id"]:
|
73
|
+
cool["label"] = label
|
74
|
+
cool["function"] = cool_function
|
75
|
+
cool["config"] = config_function
|
76
|
+
cool["constraints"] = constraints
|
77
|
+
return True
|
78
|
+
self._coolants.append(
|
79
|
+
{
|
80
|
+
"id": cool_id,
|
81
|
+
"label": label,
|
82
|
+
"function": cool_function,
|
83
|
+
"config": config_function,
|
84
|
+
"devices": [],
|
85
|
+
"constraints": constraints,
|
86
|
+
"current_state": [],
|
87
|
+
}
|
88
|
+
)
|
89
|
+
return True
|
90
|
+
|
91
|
+
def coolant_on(self, device):
|
92
|
+
for cool in self._coolants:
|
93
|
+
if device in cool["devices"]:
|
94
|
+
idx = cool["devices"].index(device)
|
95
|
+
if not cool["current_state"][idx]:
|
96
|
+
cool["current_state"][idx] = True
|
97
|
+
routine = cool["function"]
|
98
|
+
routine(device, True)
|
99
|
+
self.kernel.signal("coolant_set", device.label, True)
|
100
|
+
return True
|
101
|
+
return False
|
102
|
+
|
103
|
+
def coolant_off(self, device):
|
104
|
+
for cool in self._coolants:
|
105
|
+
if device in cool["devices"]:
|
106
|
+
idx = cool["devices"].index(device)
|
107
|
+
if cool["current_state"][idx]:
|
108
|
+
cool["current_state"][idx] = False
|
109
|
+
routine = cool["function"]
|
110
|
+
routine(device, False)
|
111
|
+
self.kernel.signal("coolant_set", device.label, False)
|
112
|
+
return True
|
113
|
+
return False
|
114
|
+
|
115
|
+
def coolant_toggle(self, device):
|
116
|
+
for cool in self._coolants:
|
117
|
+
if device in cool["devices"]:
|
118
|
+
idx = cool["devices"].index(device)
|
119
|
+
new_state = not cool["current_state"][idx]
|
120
|
+
cool["current_state"][idx] = new_state
|
121
|
+
routine = cool["function"]
|
122
|
+
routine(device, new_state)
|
123
|
+
self.kernel.signal("coolant_set", device.label, new_state)
|
124
|
+
return True
|
125
|
+
return False
|
126
|
+
|
127
|
+
def coolant_state(self, device):
|
128
|
+
for cool in self._coolants:
|
129
|
+
if device in cool["devices"]:
|
130
|
+
idx = cool["devices"].index(device)
|
131
|
+
return cool["current_state"][idx]
|
132
|
+
# Nothing found
|
133
|
+
return False
|
134
|
+
|
135
|
+
def coolant_on_by_id(self, identifier):
|
136
|
+
# Caveat, this will be executed independently of devices registered!
|
137
|
+
cool_id = identifier.lower()
|
138
|
+
for cool in self._coolants:
|
139
|
+
if cool_id == cool["id"]:
|
140
|
+
routine = cool["function"]
|
141
|
+
routine(None, True)
|
142
|
+
return True
|
143
|
+
return False
|
144
|
+
|
145
|
+
def coolant_off_by_id(self, identifier):
|
146
|
+
# Caveat, this will be executed independently of devices registered!
|
147
|
+
cool_id = identifier.lower()
|
148
|
+
for cool in self._coolants:
|
149
|
+
if cool_id == cool["id"]:
|
150
|
+
routine = cool["function"]
|
151
|
+
routine(None, False)
|
152
|
+
return True
|
153
|
+
return False
|
154
|
+
|
155
|
+
def registered_coolants(self):
|
156
|
+
"""
|
157
|
+
Returns the dictionary of all registered coolants
|
158
|
+
"""
|
159
|
+
return self._coolants
|
160
|
+
|
161
|
+
def claim_coolant(self, device, coolant):
|
162
|
+
found = None
|
163
|
+
if coolant is None:
|
164
|
+
coolant = ""
|
165
|
+
devname = device.name.lower()
|
166
|
+
# print (f"Claim: {device.label} ({devname}): {coolant}")
|
167
|
+
for cool in self._coolants:
|
168
|
+
relevant = True
|
169
|
+
if cool["constraints"]:
|
170
|
+
allowed = cool["constraints"].split(",")
|
171
|
+
relevant = any(candidate.lower() in devname for candidate in allowed)
|
172
|
+
if not relevant:
|
173
|
+
# Skipped as not relevant...
|
174
|
+
continue
|
175
|
+
if cool["id"] == coolant.lower():
|
176
|
+
found = cool["function"]
|
177
|
+
if device not in cool["devices"]:
|
178
|
+
cool["devices"].append(device)
|
179
|
+
cool["current_state"].append(False)
|
180
|
+
else:
|
181
|
+
try:
|
182
|
+
idx = cool["devices"].index(device)
|
183
|
+
cool["devices"].pop(idx)
|
184
|
+
cool["current_state"].pop(idx)
|
185
|
+
except ValueError:
|
186
|
+
# wasn't inside
|
187
|
+
pass
|
188
|
+
|
189
|
+
return found
|
190
|
+
|
191
|
+
def get_device_function(self, device, coolant=None):
|
192
|
+
found = None
|
193
|
+
for cool in self._coolants:
|
194
|
+
if coolant is None:
|
195
|
+
if device in cool["devices"]:
|
196
|
+
found = cool["function"]
|
197
|
+
break
|
198
|
+
elif cool["id"] == coolant.lower():
|
199
|
+
found = cool["function"]
|
200
|
+
break
|
201
|
+
|
202
|
+
return found
|
203
|
+
|
204
|
+
def get_device_coolant(self, device, coolant=None):
|
205
|
+
found = None
|
206
|
+
for cool in self._coolants:
|
207
|
+
if coolant is None:
|
208
|
+
if device in cool["devices"]:
|
209
|
+
found = cool
|
210
|
+
break
|
211
|
+
elif cool["id"] == coolant.lower():
|
212
|
+
found = cool
|
213
|
+
break
|
214
|
+
|
215
|
+
return found
|
216
|
+
|
217
|
+
def get_devices_using(self, coolant_id):
|
218
|
+
dev_str = ""
|
219
|
+
multiple = False
|
220
|
+
for cool in self._coolants:
|
221
|
+
if cool["id"] == coolant_id.lower():
|
222
|
+
for idx, dev in enumerate(cool["devices"]):
|
223
|
+
if multiple:
|
224
|
+
dev_str += ", "
|
225
|
+
multiple = True
|
226
|
+
dev_str += (
|
227
|
+
f"{dev.label} [{'on' if cool['current_state'][idx] else 'off'}]"
|
228
|
+
)
|
229
|
+
break
|
230
|
+
|
231
|
+
return dev_str
|
232
|
+
|
233
|
+
def coolant_choice_helper(self, device):
|
234
|
+
"""
|
235
|
+
Sets the choices and display of the coolant values dynamically
|
236
|
+
@param choice_dict:
|
237
|
+
@return:
|
238
|
+
"""
|
239
|
+
|
240
|
+
def update(choice_dict):
|
241
|
+
_ = self.kernel.translation
|
242
|
+
devname = device.name.lower()
|
243
|
+
choices = []
|
244
|
+
display = []
|
245
|
+
choices.append("")
|
246
|
+
display.append(_("Nothing"))
|
247
|
+
for cool in self._coolants:
|
248
|
+
relevant = True
|
249
|
+
if cool["constraints"]:
|
250
|
+
relevant = False
|
251
|
+
allowed = cool["constraints"].split(",")
|
252
|
+
for candidate in allowed:
|
253
|
+
if candidate.lower() in devname:
|
254
|
+
relevant = True
|
255
|
+
break
|
256
|
+
if not relevant:
|
257
|
+
continue
|
258
|
+
choices.append(cool["id"])
|
259
|
+
if cool["label"]:
|
260
|
+
display.append(cool["label"])
|
261
|
+
else:
|
262
|
+
display.append(cool["id"])
|
263
|
+
choice_dict["choices"] = choices
|
264
|
+
choice_dict["display"] = display
|
265
|
+
|
266
|
+
return update
|
267
|
+
|
268
|
+
|
269
|
+
def plugin(kernel, lifecycle):
|
270
|
+
if lifecycle == "register":
|
271
|
+
_ = kernel.translation
|
272
|
+
context = kernel.root
|
273
|
+
context.coolant = Coolants(kernel)
|
274
|
+
|
275
|
+
def base_coolant_popup(context, mode):
|
276
|
+
if hasattr(context, "label"):
|
277
|
+
lasername = context.label
|
278
|
+
else:
|
279
|
+
lasername = "your laser"
|
280
|
+
if mode:
|
281
|
+
msg = _("Please switch the airassist for {laser} on").format(
|
282
|
+
laser=lasername
|
283
|
+
)
|
284
|
+
else:
|
285
|
+
msg = _("Please switch the airassist for {laser} off").format(
|
286
|
+
laser=lasername
|
287
|
+
)
|
288
|
+
context.kernel.yesno(
|
289
|
+
msg, caption=_("Air-Assist"), option_yes=_("OK"), option_no=_("OK")
|
290
|
+
)
|
291
|
+
|
292
|
+
def base_coolant_grbl_m7(context, mode):
|
293
|
+
if mode:
|
294
|
+
context("gcode M7\n")
|
295
|
+
else:
|
296
|
+
context("gcode M9\n")
|
297
|
+
|
298
|
+
def base_coolant_grbl_m8(context, mode):
|
299
|
+
if mode:
|
300
|
+
context("gcode M8\n")
|
301
|
+
else:
|
302
|
+
context("gcode M9\n")
|
303
|
+
|
304
|
+
context.coolant.register_coolant_method(
|
305
|
+
"popup", base_coolant_popup, config_function=None, label=_("Warnmessage")
|
306
|
+
)
|
307
|
+
context.coolant.register_coolant_method(
|
308
|
+
"gcode_m7",
|
309
|
+
base_coolant_grbl_m7,
|
310
|
+
config_function=None,
|
311
|
+
label=_("GCode M7/M9"),
|
312
|
+
constraints="grbl",
|
313
|
+
)
|
314
|
+
context.coolant.register_coolant_method(
|
315
|
+
"gcode_m8",
|
316
|
+
base_coolant_grbl_m8,
|
317
|
+
config_function=None,
|
318
|
+
label=_("GCode M8/M9"),
|
319
|
+
constraints="grbl",
|
320
|
+
)
|
321
|
+
|
322
|
+
@context.console_command(
|
323
|
+
("coolants", "vents"), help=_("displays registered coolant methods")
|
324
|
+
)
|
325
|
+
def display_coolant(command, channel, _, **kwargs):
|
326
|
+
# elements = context.elements
|
327
|
+
coolant = kernel.root.coolant
|
328
|
+
cool = coolant.registered_coolants()
|
329
|
+
if len(cool):
|
330
|
+
channel(_("Registered coolant-interfaces:"))
|
331
|
+
for cool_instance in cool:
|
332
|
+
c_name = cool_instance["id"]
|
333
|
+
c_label = cool_instance["label"]
|
334
|
+
claimed = coolant.get_devices_using(c_name)
|
335
|
+
if c_label:
|
336
|
+
c_name += " - " + c_label
|
337
|
+
if claimed == "":
|
338
|
+
claimed = _("Not used")
|
339
|
+
|
340
|
+
channel(_("{name}: {devices}").format(name=c_name, devices=claimed))
|
341
|
+
|
342
|
+
else:
|
343
|
+
channel(_("There are no coolant-interfaces known to MeerK40t"))
|
344
|
+
|
345
|
+
@context.console_command(
|
346
|
+
("coolant_on", "vent_on"), help=_("Turns on the coolant for the active device")
|
347
|
+
)
|
348
|
+
def turn_coolant_on(command, channel, _, **kwargs):
|
349
|
+
try:
|
350
|
+
device = context.device
|
351
|
+
except AttributeError:
|
352
|
+
channel("No active device found")
|
353
|
+
return
|
354
|
+
|
355
|
+
coolant = kernel.root.coolant
|
356
|
+
if coolant.coolant_on(device):
|
357
|
+
channel("Coolant turned on")
|
358
|
+
else:
|
359
|
+
channel("Active device does not support coolant")
|
360
|
+
|
361
|
+
@context.console_command(
|
362
|
+
("coolant_off", "vent_off"), help=_("Turns off the coolant for the active device")
|
363
|
+
)
|
364
|
+
def turn_coolant_off(command, channel, _, **kwargs):
|
365
|
+
try:
|
366
|
+
device = context.device
|
367
|
+
except AttributeError:
|
368
|
+
channel("No active device found")
|
369
|
+
return
|
370
|
+
|
371
|
+
coolant = kernel.root.coolant
|
372
|
+
if coolant.coolant_off(device):
|
373
|
+
channel("Coolant turned off")
|
374
|
+
else:
|
375
|
+
channel("Active device does not support coolant")
|
376
|
+
|
377
|
+
@context.console_argument("id", type=str)
|
378
|
+
@context.console_command(
|
379
|
+
("coolant_on_by_id", "vent_on_by_id"), help=_("Turns the coolant on using the given method")
|
380
|
+
)
|
381
|
+
def turn_coolant_on_by_id(command, channel, _, id=None, **kwargs):
|
382
|
+
if id is None:
|
383
|
+
channel("You need to provide an identifier")
|
384
|
+
return
|
385
|
+
coolant = kernel.root.coolant
|
386
|
+
if coolant.coolant_on_by_id(id):
|
387
|
+
channel(f"Coolant {id} turned on")
|
388
|
+
else:
|
389
|
+
channel(f"Method {id} could not be found")
|
390
|
+
|
391
|
+
@context.console_argument("id", type=str)
|
392
|
+
@context.console_command(
|
393
|
+
("coolant_off_by_id", "vent_off_by_id"), help=_("Turns the coolant off using the given method")
|
394
|
+
)
|
395
|
+
def turn_coolant_off_by_id(command, channel, _, id=None, **kwargs):
|
396
|
+
if id is None:
|
397
|
+
channel("You need to provide an identifier")
|
398
|
+
return
|
399
|
+
coolant = kernel.root.coolant
|
400
|
+
if coolant.coolant_off_by_id(id):
|
401
|
+
channel(f"Coolant {id} turned off")
|
402
|
+
else:
|
403
|
+
channel(f"Method {id} could not be found")
|
@@ -0,0 +1,204 @@
|
|
1
|
+
"""
|
2
|
+
Try to detect some common encoding patterns.
|
3
|
+
Based on https://github.com/magnetikonline/py-encoding-detect
|
4
|
+
|
5
|
+
It will detect the following encodings of a text file:
|
6
|
+
|
7
|
+
ASCII
|
8
|
+
UTF-8
|
9
|
+
UTF-16BE
|
10
|
+
UTF-16LE
|
11
|
+
|
12
|
+
"""
|
13
|
+
|
14
|
+
|
15
|
+
class EncodingDetectFile:
|
16
|
+
ENCODING_ASCII = "ascii"
|
17
|
+
ENCODING_CP1252 = "cp1252"
|
18
|
+
ENCODING_UTF_8 = "utf_8"
|
19
|
+
ENCODING_UTF_16_BE = "utf_16_be"
|
20
|
+
ENCODING_UTF_16_LE = "utf_16_le"
|
21
|
+
|
22
|
+
# http://unicode.org/faq/utf_bom.html#BOM
|
23
|
+
BOM_UTF_8 = "\xef\xbb\xbf"
|
24
|
+
BOM_UTF_16_BE = "\xfe\xff"
|
25
|
+
BOM_UTF_16_LE = "\xff\xfe"
|
26
|
+
|
27
|
+
BYTE_EOL = (13, 10) # \r\n
|
28
|
+
|
29
|
+
UTF_16_NULL_PERCENT_POSITIVE = 0.7
|
30
|
+
UTF_16_NULL_PERCENT_NEGATIVE = 0.1
|
31
|
+
|
32
|
+
def _detect_bom(self, fh):
|
33
|
+
def result(encoding, bom_marker):
|
34
|
+
return (encoding, bom_marker, None)
|
35
|
+
|
36
|
+
# test 2 byte UTF-16 BOMs
|
37
|
+
file_data = fh.read(2)
|
38
|
+
# print(f"first 2 bytes: {file_data}")
|
39
|
+
if file_data == EncodingDetectFile.BOM_UTF_16_BE:
|
40
|
+
return result(
|
41
|
+
EncodingDetectFile.ENCODING_UTF_16_BE, EncodingDetectFile.BOM_UTF_16_BE
|
42
|
+
)
|
43
|
+
|
44
|
+
if file_data == EncodingDetectFile.BOM_UTF_16_LE:
|
45
|
+
return result(
|
46
|
+
EncodingDetectFile.ENCODING_UTF_16_LE, EncodingDetectFile.BOM_UTF_16_LE
|
47
|
+
)
|
48
|
+
|
49
|
+
# test 3 byte UTF-8 BOM
|
50
|
+
file_data += fh.read(1)
|
51
|
+
# print(f"first 3 bytes: {file_data}")
|
52
|
+
if file_data == EncodingDetectFile.BOM_UTF_8:
|
53
|
+
return result(
|
54
|
+
EncodingDetectFile.ENCODING_UTF_8, EncodingDetectFile.BOM_UTF_8
|
55
|
+
)
|
56
|
+
|
57
|
+
# no BOM marker - return bytes read so far
|
58
|
+
return False, False, file_data
|
59
|
+
|
60
|
+
def _detect_ascii_utf8(self, file_data):
|
61
|
+
ascii_chars_only = True
|
62
|
+
|
63
|
+
byte_follow = 0
|
64
|
+
for file_byte in file_data:
|
65
|
+
# process additional character byte(s)
|
66
|
+
if byte_follow:
|
67
|
+
if 128 <= file_byte <= 191:
|
68
|
+
byte_follow -= 1
|
69
|
+
ascii_chars_only = False
|
70
|
+
continue
|
71
|
+
|
72
|
+
# not ASCII or UTF-8
|
73
|
+
return False
|
74
|
+
|
75
|
+
# determine byte length of character
|
76
|
+
# https://en.wikipedia.org/wiki/UTF-8#Codepage_layout
|
77
|
+
if 1 <= file_byte <= 127:
|
78
|
+
# single byte
|
79
|
+
continue
|
80
|
+
|
81
|
+
if 194 <= file_byte <= 223:
|
82
|
+
# one byte follows
|
83
|
+
byte_follow = 1
|
84
|
+
continue
|
85
|
+
|
86
|
+
if 224 <= file_byte <= 239:
|
87
|
+
# two bytes follow
|
88
|
+
byte_follow = 2
|
89
|
+
continue
|
90
|
+
|
91
|
+
if 240 <= file_byte <= 244:
|
92
|
+
# three bytes follow
|
93
|
+
byte_follow = 3
|
94
|
+
continue
|
95
|
+
|
96
|
+
# not ASCII or UTF-8
|
97
|
+
return EncodingDetectFile.ENCODING_CP1252
|
98
|
+
|
99
|
+
# end of file data [byte_follow] must be zero to ensure last character was consumed
|
100
|
+
if byte_follow:
|
101
|
+
return False
|
102
|
+
|
103
|
+
# success - return ASCII or UTF-8 result
|
104
|
+
return (
|
105
|
+
EncodingDetectFile.ENCODING_ASCII
|
106
|
+
if (ascii_chars_only)
|
107
|
+
else EncodingDetectFile.ENCODING_UTF_8
|
108
|
+
)
|
109
|
+
|
110
|
+
def _detect_utf16(self, file_data):
|
111
|
+
null_byte_odd, null_byte_even = 0, 0
|
112
|
+
eol_odd, eol_even = 0, 0
|
113
|
+
|
114
|
+
odd_byte = None
|
115
|
+
for file_byte in file_data:
|
116
|
+
# build pairs of bytes
|
117
|
+
if odd_byte is None:
|
118
|
+
odd_byte = file_byte
|
119
|
+
continue
|
120
|
+
|
121
|
+
# look for odd/even null byte and check other byte for EOL
|
122
|
+
if odd_byte == 0:
|
123
|
+
null_byte_odd += 1
|
124
|
+
if file_byte in EncodingDetectFile.BYTE_EOL:
|
125
|
+
eol_even += 1
|
126
|
+
|
127
|
+
elif file_byte == 0:
|
128
|
+
null_byte_even += 1
|
129
|
+
if odd_byte in EncodingDetectFile.BYTE_EOL:
|
130
|
+
eol_odd += 1
|
131
|
+
|
132
|
+
odd_byte = None
|
133
|
+
|
134
|
+
# attempt detection based on line endings
|
135
|
+
if (not eol_odd) and eol_even:
|
136
|
+
return EncodingDetectFile.ENCODING_UTF_16_BE
|
137
|
+
|
138
|
+
if eol_odd and (not eol_even):
|
139
|
+
return EncodingDetectFile.ENCODING_UTF_16_LE
|
140
|
+
|
141
|
+
# can't detect on line endings - evaluate ratio of null bytes in odd/even positions
|
142
|
+
# this will give an indication of how much ASCII (1-127) level text is present
|
143
|
+
data_size_half = len(file_data) / 2
|
144
|
+
threshold_positive = int(
|
145
|
+
data_size_half * EncodingDetectFile.UTF_16_NULL_PERCENT_POSITIVE
|
146
|
+
)
|
147
|
+
threshold_negative = int(
|
148
|
+
data_size_half * EncodingDetectFile.UTF_16_NULL_PERCENT_NEGATIVE
|
149
|
+
)
|
150
|
+
|
151
|
+
# must have enough file data to have value ([threshold_positive] must be non-zero)
|
152
|
+
if threshold_positive:
|
153
|
+
if (null_byte_odd > threshold_positive) and (
|
154
|
+
null_byte_even < threshold_negative
|
155
|
+
):
|
156
|
+
return EncodingDetectFile.ENCODING_UTF_16_BE
|
157
|
+
|
158
|
+
if (null_byte_odd < threshold_negative) and (
|
159
|
+
null_byte_even > threshold_positive
|
160
|
+
):
|
161
|
+
return EncodingDetectFile.ENCODING_UTF_16_LE
|
162
|
+
|
163
|
+
# not UTF-16 - or insufficient data to determine with confidence
|
164
|
+
return False
|
165
|
+
|
166
|
+
def load(self, file_path):
|
167
|
+
# open file
|
168
|
+
try:
|
169
|
+
fh = open(file_path, "rb")
|
170
|
+
except Exception:
|
171
|
+
return False
|
172
|
+
|
173
|
+
# detect a byte order mark (BOM)
|
174
|
+
file_encoding, bom_marker, file_data = self._detect_bom(fh)
|
175
|
+
if file_encoding:
|
176
|
+
# file has a BOM - decode everything past it
|
177
|
+
try:
|
178
|
+
decode = fh.read().decode(file_encoding)
|
179
|
+
except UnicodeDecodeError:
|
180
|
+
return False
|
181
|
+
# print(f"decoded: {decode}")
|
182
|
+
fh.close()
|
183
|
+
|
184
|
+
return (file_encoding, bom_marker, decode)
|
185
|
+
|
186
|
+
# no BOM - read remaining file data
|
187
|
+
file_data += fh.read()
|
188
|
+
# print(f"All bytes: {file_data}")
|
189
|
+
fh.close()
|
190
|
+
|
191
|
+
# test for ASCII/UTF-8
|
192
|
+
file_encoding = self._detect_ascii_utf8(file_data)
|
193
|
+
if file_encoding:
|
194
|
+
# file is ASCII or UTF-8 (without BOM)
|
195
|
+
return (file_encoding, None, file_data.decode(file_encoding))
|
196
|
+
|
197
|
+
# test for UTF-16
|
198
|
+
file_encoding = self._detect_utf16(file_data)
|
199
|
+
if file_encoding:
|
200
|
+
# file is UTF-16(-like) (without BOM)
|
201
|
+
return (file_encoding, None, file_data.decode(file_encoding))
|
202
|
+
|
203
|
+
# can't determine encoding
|
204
|
+
return False
|