meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7010__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (445) hide show
  1. meerk40t/__init__.py +1 -1
  2. meerk40t/balormk/balor_params.py +167 -167
  3. meerk40t/balormk/clone_loader.py +457 -457
  4. meerk40t/balormk/controller.py +1566 -1512
  5. meerk40t/balormk/cylindermod.py +64 -0
  6. meerk40t/balormk/device.py +966 -1959
  7. meerk40t/balormk/driver.py +778 -591
  8. meerk40t/balormk/galvo_commands.py +1195 -0
  9. meerk40t/balormk/gui/balorconfig.py +237 -111
  10. meerk40t/balormk/gui/balorcontroller.py +191 -184
  11. meerk40t/balormk/gui/baloroperationproperties.py +116 -115
  12. meerk40t/balormk/gui/corscene.py +845 -0
  13. meerk40t/balormk/gui/gui.py +179 -147
  14. meerk40t/balormk/livelightjob.py +466 -382
  15. meerk40t/balormk/mock_connection.py +131 -109
  16. meerk40t/balormk/plugin.py +133 -135
  17. meerk40t/balormk/usb_connection.py +306 -301
  18. meerk40t/camera/__init__.py +1 -1
  19. meerk40t/camera/camera.py +514 -397
  20. meerk40t/camera/gui/camerapanel.py +1241 -1095
  21. meerk40t/camera/gui/gui.py +58 -58
  22. meerk40t/camera/plugin.py +441 -399
  23. meerk40t/ch341/__init__.py +27 -27
  24. meerk40t/ch341/ch341device.py +628 -628
  25. meerk40t/ch341/libusb.py +595 -589
  26. meerk40t/ch341/mock.py +171 -171
  27. meerk40t/ch341/windriver.py +157 -157
  28. meerk40t/constants.py +13 -0
  29. meerk40t/core/__init__.py +1 -1
  30. meerk40t/core/bindalias.py +550 -539
  31. meerk40t/core/core.py +47 -47
  32. meerk40t/core/cutcode/cubiccut.py +73 -73
  33. meerk40t/core/cutcode/cutcode.py +315 -312
  34. meerk40t/core/cutcode/cutgroup.py +141 -137
  35. meerk40t/core/cutcode/cutobject.py +192 -185
  36. meerk40t/core/cutcode/dwellcut.py +37 -37
  37. meerk40t/core/cutcode/gotocut.py +29 -29
  38. meerk40t/core/cutcode/homecut.py +29 -29
  39. meerk40t/core/cutcode/inputcut.py +34 -34
  40. meerk40t/core/cutcode/linecut.py +33 -33
  41. meerk40t/core/cutcode/outputcut.py +34 -34
  42. meerk40t/core/cutcode/plotcut.py +335 -335
  43. meerk40t/core/cutcode/quadcut.py +61 -61
  44. meerk40t/core/cutcode/rastercut.py +168 -148
  45. meerk40t/core/cutcode/waitcut.py +34 -34
  46. meerk40t/core/cutplan.py +1843 -1316
  47. meerk40t/core/drivers.py +330 -329
  48. meerk40t/core/elements/align.py +801 -669
  49. meerk40t/core/elements/branches.py +1844 -1507
  50. meerk40t/core/elements/clipboard.py +229 -219
  51. meerk40t/core/elements/element_treeops.py +4561 -2837
  52. meerk40t/core/elements/element_types.py +125 -105
  53. meerk40t/core/elements/elements.py +4329 -3617
  54. meerk40t/core/elements/files.py +117 -64
  55. meerk40t/core/elements/geometry.py +473 -224
  56. meerk40t/core/elements/grid.py +467 -316
  57. meerk40t/core/elements/materials.py +158 -94
  58. meerk40t/core/elements/notes.py +50 -38
  59. meerk40t/core/elements/offset_clpr.py +933 -912
  60. meerk40t/core/elements/offset_mk.py +963 -955
  61. meerk40t/core/elements/penbox.py +339 -267
  62. meerk40t/core/elements/placements.py +300 -83
  63. meerk40t/core/elements/render.py +785 -687
  64. meerk40t/core/elements/shapes.py +2618 -2092
  65. meerk40t/core/elements/trace.py +651 -563
  66. meerk40t/core/elements/tree_commands.py +415 -409
  67. meerk40t/core/elements/undo_redo.py +116 -58
  68. meerk40t/core/elements/wordlist.py +319 -200
  69. meerk40t/core/exceptions.py +9 -9
  70. meerk40t/core/laserjob.py +220 -220
  71. meerk40t/core/logging.py +63 -63
  72. meerk40t/core/node/blobnode.py +83 -86
  73. meerk40t/core/node/bootstrap.py +105 -103
  74. meerk40t/core/node/branch_elems.py +40 -31
  75. meerk40t/core/node/branch_ops.py +45 -38
  76. meerk40t/core/node/branch_regmark.py +48 -41
  77. meerk40t/core/node/cutnode.py +29 -32
  78. meerk40t/core/node/effect_hatch.py +375 -257
  79. meerk40t/core/node/effect_warp.py +398 -0
  80. meerk40t/core/node/effect_wobble.py +441 -309
  81. meerk40t/core/node/elem_ellipse.py +404 -309
  82. meerk40t/core/node/elem_image.py +1082 -801
  83. meerk40t/core/node/elem_line.py +358 -292
  84. meerk40t/core/node/elem_path.py +259 -201
  85. meerk40t/core/node/elem_point.py +129 -102
  86. meerk40t/core/node/elem_polyline.py +310 -246
  87. meerk40t/core/node/elem_rect.py +376 -286
  88. meerk40t/core/node/elem_text.py +445 -418
  89. meerk40t/core/node/filenode.py +59 -40
  90. meerk40t/core/node/groupnode.py +138 -74
  91. meerk40t/core/node/image_processed.py +777 -766
  92. meerk40t/core/node/image_raster.py +156 -113
  93. meerk40t/core/node/layernode.py +31 -31
  94. meerk40t/core/node/mixins.py +135 -107
  95. meerk40t/core/node/node.py +1427 -1304
  96. meerk40t/core/node/nutils.py +117 -114
  97. meerk40t/core/node/op_cut.py +462 -335
  98. meerk40t/core/node/op_dots.py +296 -251
  99. meerk40t/core/node/op_engrave.py +414 -311
  100. meerk40t/core/node/op_image.py +755 -369
  101. meerk40t/core/node/op_raster.py +787 -522
  102. meerk40t/core/node/place_current.py +37 -40
  103. meerk40t/core/node/place_point.py +329 -126
  104. meerk40t/core/node/refnode.py +58 -47
  105. meerk40t/core/node/rootnode.py +225 -219
  106. meerk40t/core/node/util_console.py +48 -48
  107. meerk40t/core/node/util_goto.py +84 -65
  108. meerk40t/core/node/util_home.py +61 -61
  109. meerk40t/core/node/util_input.py +102 -102
  110. meerk40t/core/node/util_output.py +102 -102
  111. meerk40t/core/node/util_wait.py +65 -65
  112. meerk40t/core/parameters.py +709 -707
  113. meerk40t/core/planner.py +875 -785
  114. meerk40t/core/plotplanner.py +656 -652
  115. meerk40t/core/space.py +120 -113
  116. meerk40t/core/spoolers.py +706 -705
  117. meerk40t/core/svg_io.py +1836 -1549
  118. meerk40t/core/treeop.py +534 -445
  119. meerk40t/core/undos.py +278 -124
  120. meerk40t/core/units.py +784 -680
  121. meerk40t/core/view.py +393 -322
  122. meerk40t/core/webhelp.py +62 -62
  123. meerk40t/core/wordlist.py +513 -504
  124. meerk40t/cylinder/cylinder.py +247 -0
  125. meerk40t/cylinder/gui/cylindersettings.py +41 -0
  126. meerk40t/cylinder/gui/gui.py +24 -0
  127. meerk40t/device/__init__.py +1 -1
  128. meerk40t/device/basedevice.py +322 -123
  129. meerk40t/device/devicechoices.py +50 -0
  130. meerk40t/device/dummydevice.py +163 -128
  131. meerk40t/device/gui/defaultactions.py +618 -602
  132. meerk40t/device/gui/effectspanel.py +114 -0
  133. meerk40t/device/gui/formatterpanel.py +253 -290
  134. meerk40t/device/gui/warningpanel.py +337 -260
  135. meerk40t/device/mixins.py +13 -13
  136. meerk40t/dxf/__init__.py +1 -1
  137. meerk40t/dxf/dxf_io.py +766 -554
  138. meerk40t/dxf/plugin.py +47 -35
  139. meerk40t/external_plugins.py +79 -79
  140. meerk40t/external_plugins_build.py +28 -28
  141. meerk40t/extra/cag.py +112 -116
  142. meerk40t/extra/coolant.py +403 -0
  143. meerk40t/extra/encode_detect.py +198 -0
  144. meerk40t/extra/ezd.py +1165 -1165
  145. meerk40t/extra/hershey.py +835 -340
  146. meerk40t/extra/imageactions.py +322 -316
  147. meerk40t/extra/inkscape.py +630 -622
  148. meerk40t/extra/lbrn.py +424 -424
  149. meerk40t/extra/outerworld.py +284 -0
  150. meerk40t/extra/param_functions.py +1542 -1556
  151. meerk40t/extra/potrace.py +257 -253
  152. meerk40t/extra/serial_exchange.py +118 -0
  153. meerk40t/extra/updater.py +602 -453
  154. meerk40t/extra/vectrace.py +147 -146
  155. meerk40t/extra/winsleep.py +83 -83
  156. meerk40t/extra/xcs_reader.py +597 -0
  157. meerk40t/fill/fills.py +781 -335
  158. meerk40t/fill/patternfill.py +1061 -1061
  159. meerk40t/fill/patterns.py +614 -567
  160. meerk40t/grbl/control.py +87 -87
  161. meerk40t/grbl/controller.py +990 -903
  162. meerk40t/grbl/device.py +1081 -768
  163. meerk40t/grbl/driver.py +989 -771
  164. meerk40t/grbl/emulator.py +532 -497
  165. meerk40t/grbl/gcodejob.py +783 -767
  166. meerk40t/grbl/gui/grblconfiguration.py +373 -298
  167. meerk40t/grbl/gui/grblcontroller.py +485 -271
  168. meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
  169. meerk40t/grbl/gui/grbloperationconfig.py +105 -0
  170. meerk40t/grbl/gui/gui.py +147 -116
  171. meerk40t/grbl/interpreter.py +44 -44
  172. meerk40t/grbl/loader.py +22 -22
  173. meerk40t/grbl/mock_connection.py +56 -56
  174. meerk40t/grbl/plugin.py +294 -264
  175. meerk40t/grbl/serial_connection.py +93 -88
  176. meerk40t/grbl/tcp_connection.py +81 -79
  177. meerk40t/grbl/ws_connection.py +112 -0
  178. meerk40t/gui/__init__.py +1 -1
  179. meerk40t/gui/about.py +2042 -296
  180. meerk40t/gui/alignment.py +1644 -1608
  181. meerk40t/gui/autoexec.py +199 -0
  182. meerk40t/gui/basicops.py +791 -670
  183. meerk40t/gui/bufferview.py +77 -71
  184. meerk40t/gui/busy.py +170 -133
  185. meerk40t/gui/choicepropertypanel.py +1673 -1469
  186. meerk40t/gui/consolepanel.py +706 -542
  187. meerk40t/gui/devicepanel.py +687 -581
  188. meerk40t/gui/dialogoptions.py +110 -107
  189. meerk40t/gui/executejob.py +316 -306
  190. meerk40t/gui/fonts.py +90 -90
  191. meerk40t/gui/functionwrapper.py +252 -0
  192. meerk40t/gui/gui_mixins.py +729 -0
  193. meerk40t/gui/guicolors.py +205 -182
  194. meerk40t/gui/help_assets/help_assets.py +218 -201
  195. meerk40t/gui/helper.py +154 -0
  196. meerk40t/gui/hersheymanager.py +1430 -846
  197. meerk40t/gui/icons.py +3422 -2747
  198. meerk40t/gui/imagesplitter.py +555 -508
  199. meerk40t/gui/keymap.py +354 -344
  200. meerk40t/gui/laserpanel.py +892 -806
  201. meerk40t/gui/laserrender.py +1470 -1232
  202. meerk40t/gui/lasertoolpanel.py +805 -793
  203. meerk40t/gui/magnetoptions.py +436 -0
  204. meerk40t/gui/materialmanager.py +2917 -0
  205. meerk40t/gui/materialtest.py +1722 -1694
  206. meerk40t/gui/mkdebug.py +646 -359
  207. meerk40t/gui/mwindow.py +163 -140
  208. meerk40t/gui/navigationpanels.py +2605 -2467
  209. meerk40t/gui/notes.py +143 -142
  210. meerk40t/gui/opassignment.py +414 -410
  211. meerk40t/gui/operation_info.py +310 -299
  212. meerk40t/gui/plugin.py +494 -328
  213. meerk40t/gui/position.py +714 -669
  214. meerk40t/gui/preferences.py +901 -650
  215. meerk40t/gui/propertypanels/attributes.py +1461 -1131
  216. meerk40t/gui/propertypanels/blobproperty.py +117 -114
  217. meerk40t/gui/propertypanels/consoleproperty.py +83 -80
  218. meerk40t/gui/propertypanels/gotoproperty.py +77 -0
  219. meerk40t/gui/propertypanels/groupproperties.py +223 -217
  220. meerk40t/gui/propertypanels/hatchproperty.py +489 -469
  221. meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
  222. meerk40t/gui/propertypanels/inputproperty.py +59 -58
  223. meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
  224. meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
  225. meerk40t/gui/propertypanels/outputproperty.py +59 -58
  226. meerk40t/gui/propertypanels/pathproperty.py +389 -380
  227. meerk40t/gui/propertypanels/placementproperty.py +1214 -383
  228. meerk40t/gui/propertypanels/pointproperty.py +140 -136
  229. meerk40t/gui/propertypanels/propertywindow.py +313 -181
  230. meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
  231. meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
  232. meerk40t/gui/propertypanels/textproperty.py +770 -755
  233. meerk40t/gui/propertypanels/waitproperty.py +56 -55
  234. meerk40t/gui/propertypanels/warpproperty.py +121 -0
  235. meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
  236. meerk40t/gui/ribbon.py +2468 -2210
  237. meerk40t/gui/scene/scene.py +1100 -1051
  238. meerk40t/gui/scene/sceneconst.py +22 -22
  239. meerk40t/gui/scene/scenepanel.py +439 -349
  240. meerk40t/gui/scene/scenespacewidget.py +365 -365
  241. meerk40t/gui/scene/widget.py +518 -505
  242. meerk40t/gui/scenewidgets/affinemover.py +215 -215
  243. meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
  244. meerk40t/gui/scenewidgets/bedwidget.py +120 -97
  245. meerk40t/gui/scenewidgets/elementswidget.py +137 -107
  246. meerk40t/gui/scenewidgets/gridwidget.py +785 -745
  247. meerk40t/gui/scenewidgets/guidewidget.py +765 -765
  248. meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
  249. meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
  250. meerk40t/gui/scenewidgets/nodeselector.py +28 -28
  251. meerk40t/gui/scenewidgets/rectselectwidget.py +589 -346
  252. meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
  253. meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
  254. meerk40t/gui/scenewidgets/selectionwidget.py +2952 -2756
  255. meerk40t/gui/simpleui.py +357 -333
  256. meerk40t/gui/simulation.py +2431 -2094
  257. meerk40t/gui/snapoptions.py +208 -203
  258. meerk40t/gui/spoolerpanel.py +1227 -1180
  259. meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
  260. meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
  261. meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
  262. meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
  263. meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
  264. meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
  265. meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
  266. meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
  267. meerk40t/gui/themes.py +200 -78
  268. meerk40t/gui/tips.py +591 -0
  269. meerk40t/gui/toolwidgets/circlebrush.py +35 -35
  270. meerk40t/gui/toolwidgets/toolcircle.py +248 -242
  271. meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
  272. meerk40t/gui/toolwidgets/tooldraw.py +97 -90
  273. meerk40t/gui/toolwidgets/toolellipse.py +219 -212
  274. meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
  275. meerk40t/gui/toolwidgets/toolline.py +39 -144
  276. meerk40t/gui/toolwidgets/toollinetext.py +79 -236
  277. meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
  278. meerk40t/gui/toolwidgets/toolmeasure.py +160 -216
  279. meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
  280. meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
  281. meerk40t/gui/toolwidgets/toolparameter.py +754 -668
  282. meerk40t/gui/toolwidgets/toolplacement.py +108 -108
  283. meerk40t/gui/toolwidgets/toolpoint.py +68 -59
  284. meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
  285. meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
  286. meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
  287. meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
  288. meerk40t/gui/toolwidgets/toolrect.py +211 -207
  289. meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
  290. meerk40t/gui/toolwidgets/toolribbon.py +598 -113
  291. meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
  292. meerk40t/gui/toolwidgets/tooltext.py +98 -89
  293. meerk40t/gui/toolwidgets/toolvector.py +213 -204
  294. meerk40t/gui/toolwidgets/toolwidget.py +39 -39
  295. meerk40t/gui/usbconnect.py +98 -91
  296. meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
  297. meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
  298. meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
  299. meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
  300. meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
  301. meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
  302. meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
  303. meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
  304. meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
  305. meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
  306. meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
  307. meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
  308. meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
  309. meerk40t/gui/wordlisteditor.py +985 -931
  310. meerk40t/gui/wxmeerk40t.py +1444 -1169
  311. meerk40t/gui/wxmmain.py +5578 -4112
  312. meerk40t/gui/wxmribbon.py +1591 -1076
  313. meerk40t/gui/wxmscene.py +1635 -1453
  314. meerk40t/gui/wxmtree.py +2410 -2089
  315. meerk40t/gui/wxutils.py +1769 -1099
  316. meerk40t/gui/zmatrix.py +102 -102
  317. meerk40t/image/__init__.py +1 -1
  318. meerk40t/image/dither.py +429 -0
  319. meerk40t/image/imagetools.py +2778 -2269
  320. meerk40t/internal_plugins.py +150 -130
  321. meerk40t/kernel/__init__.py +63 -12
  322. meerk40t/kernel/channel.py +259 -212
  323. meerk40t/kernel/context.py +538 -538
  324. meerk40t/kernel/exceptions.py +41 -41
  325. meerk40t/kernel/functions.py +463 -414
  326. meerk40t/kernel/jobs.py +100 -100
  327. meerk40t/kernel/kernel.py +3809 -3571
  328. meerk40t/kernel/lifecycles.py +71 -71
  329. meerk40t/kernel/module.py +49 -49
  330. meerk40t/kernel/service.py +147 -147
  331. meerk40t/kernel/settings.py +383 -343
  332. meerk40t/lihuiyu/controller.py +883 -876
  333. meerk40t/lihuiyu/device.py +1181 -1069
  334. meerk40t/lihuiyu/driver.py +1466 -1372
  335. meerk40t/lihuiyu/gui/gui.py +127 -106
  336. meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
  337. meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
  338. meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
  339. meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
  340. meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
  341. meerk40t/lihuiyu/interpreter.py +53 -53
  342. meerk40t/lihuiyu/laserspeed.py +450 -450
  343. meerk40t/lihuiyu/loader.py +90 -90
  344. meerk40t/lihuiyu/parser.py +404 -404
  345. meerk40t/lihuiyu/plugin.py +101 -102
  346. meerk40t/lihuiyu/tcp_connection.py +111 -109
  347. meerk40t/main.py +231 -165
  348. meerk40t/moshi/builder.py +788 -781
  349. meerk40t/moshi/controller.py +505 -499
  350. meerk40t/moshi/device.py +495 -442
  351. meerk40t/moshi/driver.py +862 -696
  352. meerk40t/moshi/gui/gui.py +78 -76
  353. meerk40t/moshi/gui/moshicontrollergui.py +538 -522
  354. meerk40t/moshi/gui/moshidrivergui.py +87 -75
  355. meerk40t/moshi/plugin.py +43 -43
  356. meerk40t/network/console_server.py +102 -57
  357. meerk40t/network/kernelserver.py +10 -9
  358. meerk40t/network/tcp_server.py +142 -140
  359. meerk40t/network/udp_server.py +103 -77
  360. meerk40t/network/web_server.py +390 -0
  361. meerk40t/newly/controller.py +1158 -1144
  362. meerk40t/newly/device.py +874 -732
  363. meerk40t/newly/driver.py +540 -412
  364. meerk40t/newly/gui/gui.py +219 -188
  365. meerk40t/newly/gui/newlyconfig.py +116 -101
  366. meerk40t/newly/gui/newlycontroller.py +193 -186
  367. meerk40t/newly/gui/operationproperties.py +51 -51
  368. meerk40t/newly/mock_connection.py +82 -82
  369. meerk40t/newly/newly_params.py +56 -56
  370. meerk40t/newly/plugin.py +1214 -1246
  371. meerk40t/newly/usb_connection.py +322 -322
  372. meerk40t/rotary/gui/gui.py +52 -46
  373. meerk40t/rotary/gui/rotarysettings.py +240 -232
  374. meerk40t/rotary/rotary.py +202 -98
  375. meerk40t/ruida/control.py +291 -91
  376. meerk40t/ruida/controller.py +138 -1088
  377. meerk40t/ruida/device.py +672 -231
  378. meerk40t/ruida/driver.py +534 -472
  379. meerk40t/ruida/emulator.py +1494 -1491
  380. meerk40t/ruida/exceptions.py +4 -4
  381. meerk40t/ruida/gui/gui.py +71 -76
  382. meerk40t/ruida/gui/ruidaconfig.py +239 -72
  383. meerk40t/ruida/gui/ruidacontroller.py +187 -184
  384. meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
  385. meerk40t/ruida/loader.py +54 -52
  386. meerk40t/ruida/mock_connection.py +57 -109
  387. meerk40t/ruida/plugin.py +124 -87
  388. meerk40t/ruida/rdjob.py +2084 -945
  389. meerk40t/ruida/serial_connection.py +116 -0
  390. meerk40t/ruida/tcp_connection.py +146 -0
  391. meerk40t/ruida/udp_connection.py +73 -0
  392. meerk40t/svgelements.py +9671 -9669
  393. meerk40t/tools/driver_to_path.py +584 -579
  394. meerk40t/tools/geomstr.py +5583 -4680
  395. meerk40t/tools/jhfparser.py +357 -292
  396. meerk40t/tools/kerftest.py +904 -890
  397. meerk40t/tools/livinghinges.py +1168 -1033
  398. meerk40t/tools/pathtools.py +987 -949
  399. meerk40t/tools/pmatrix.py +234 -0
  400. meerk40t/tools/pointfinder.py +942 -942
  401. meerk40t/tools/polybool.py +940 -940
  402. meerk40t/tools/rasterplotter.py +1660 -547
  403. meerk40t/tools/shxparser.py +989 -901
  404. meerk40t/tools/ttfparser.py +726 -446
  405. meerk40t/tools/zinglplotter.py +595 -593
  406. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/LICENSE +21 -21
  407. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/METADATA +150 -139
  408. meerk40t-0.9.7010.dist-info/RECORD +445 -0
  409. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/WHEEL +1 -1
  410. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/top_level.txt +0 -1
  411. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/zip-safe +1 -1
  412. meerk40t/balormk/elementlightjob.py +0 -159
  413. meerk40t-0.9.3001.dist-info/RECORD +0 -437
  414. test/bootstrap.py +0 -63
  415. test/test_cli.py +0 -12
  416. test/test_core_cutcode.py +0 -418
  417. test/test_core_elements.py +0 -144
  418. test/test_core_plotplanner.py +0 -397
  419. test/test_core_viewports.py +0 -312
  420. test/test_drivers_grbl.py +0 -108
  421. test/test_drivers_lihuiyu.py +0 -443
  422. test/test_drivers_newly.py +0 -113
  423. test/test_element_degenerate_points.py +0 -43
  424. test/test_elements_classify.py +0 -97
  425. test/test_elements_penbox.py +0 -22
  426. test/test_file_svg.py +0 -176
  427. test/test_fill.py +0 -155
  428. test/test_geomstr.py +0 -1523
  429. test/test_geomstr_nodes.py +0 -18
  430. test/test_imagetools_actualize.py +0 -306
  431. test/test_imagetools_wizard.py +0 -258
  432. test/test_kernel.py +0 -200
  433. test/test_laser_speeds.py +0 -3303
  434. test/test_length.py +0 -57
  435. test/test_lifecycle.py +0 -66
  436. test/test_operations.py +0 -251
  437. test/test_operations_hatch.py +0 -57
  438. test/test_ruida.py +0 -19
  439. test/test_spooler.py +0 -22
  440. test/test_tools_rasterplotter.py +0 -29
  441. test/test_wobble.py +0 -133
  442. test/test_zingl.py +0 -124
  443. {test → meerk40t/cylinder}/__init__.py +0 -0
  444. /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
  445. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/entry_points.txt +0 -0
meerk40t/gui/wxmribbon.py CHANGED
@@ -1,1076 +1,1591 @@
1
- """
2
- The WxmRibbon Bar is a core aspect of MeerK40t's interaction. To allow fully dynamic buttons we use the ribbonbar
3
- control widget built into meerk40t.
4
-
5
- Panels are created in a static fashion within this class. But the contents of those individual ribbon panels are defined
6
- in the kernel lookup.
7
-
8
- service.register(
9
- "button/control/Redlight",
10
- {
11
- ...<def>...
12
- },
13
- )
14
-
15
- This setup allows us to define a large series of buttons with a button prefix and the name of a panel it should be
16
- added to as well as a unique name. This defines within the meerk40t ecosystem a methodology of specifically creating
17
- dynamic buttons. When the service changes because of a switch in the device (for example) it will trigger the lookup
18
- listeners which will update their contents, triggering the update within this control.
19
- """
20
-
21
- import copy
22
- import math
23
- import platform
24
- import threading
25
-
26
- import wx
27
- from wx import aui
28
-
29
- from meerk40t.gui.icons import (
30
- STD_ICON_SIZE,
31
- get_default_icon_size,
32
- icon_add_new,
33
- icon_trash,
34
- icons8_down,
35
- icons8_opened_folder,
36
- icons8_up,
37
- )
38
- from meerk40t.gui.ribbon import RibbonBarPanel
39
- from meerk40t.gui.wxutils import StaticBoxSizer, dip_size
40
- from meerk40t.kernel import Settings, lookup_listener, signal_listener
41
-
42
- _ = wx.GetTranslation
43
-
44
-
45
- def register_panel_ribbon(window, context):
46
- iconsize = get_default_icon_size()
47
- minh = 3 * iconsize + 25
48
- pane_ribbon = (
49
- aui.AuiPaneInfo()
50
- .Name("ribbon")
51
- .Top()
52
- .BestSize(300, minh)
53
- .FloatingSize(640, minh)
54
- .Caption(_("Ribbon"))
55
- .CaptionVisible(not context.pane_lock)
56
- )
57
- pane_ribbon.dock_proportion = 640
58
- ribbon = MKRibbonBarPanel(
59
- window, wx.ID_ANY, context=context, pane=pane_ribbon, identifier="primary"
60
- )
61
- pane_ribbon.control = ribbon
62
-
63
- window.on_pane_create(pane_ribbon)
64
- context.register("pane/ribbon", pane_ribbon)
65
- context.register("ribbonbar/primary", ribbon)
66
-
67
- minh = int(1.5 * iconsize)
68
- pane_tool = (
69
- aui.AuiPaneInfo()
70
- .Name("tools")
71
- .Left()
72
- .BestSize(minh, 300)
73
- .FloatingSize(minh, 640)
74
- .Caption(_("Tools"))
75
- .CaptionVisible(not context.pane_lock)
76
- )
77
- pane_tool.dock_proportion = 640
78
- ribbon = MKRibbonBarPanel(
79
- window,
80
- wx.ID_ANY,
81
- context=context,
82
- pane=pane_tool,
83
- identifier="tools",
84
- orientation="auto",
85
- show_labels=False,
86
- )
87
- pane_tool.control = ribbon
88
-
89
- window.on_pane_create(pane_tool)
90
- context.register("pane/tools", pane_tool)
91
- context.register("ribbonbar/tools", ribbon)
92
-
93
- choices = [
94
- {
95
- "attr": "ribbon_show_labels",
96
- "object": context,
97
- "default": False,
98
- "type": bool,
99
- "label": _("Show the Ribbon Labels"),
100
- "tip": _(
101
- "Active: Show the labels for ribbonbar.\n"
102
- "Inactive: Hide the ribbon labels."
103
- ),
104
- "page": "Gui",
105
- "section": "Appearance",
106
- },
107
- ]
108
-
109
- context.kernel.register_choices("preferences", choices)
110
-
111
-
112
- class MKRibbonBarPanel(RibbonBarPanel):
113
- def __init__(
114
- self,
115
- parent,
116
- id,
117
- context=None,
118
- pane=None,
119
- identifier=None,
120
- orientation=None,
121
- show_labels=None,
122
- **kwds,
123
- ):
124
- RibbonBarPanel.__init__(self, parent, id, context, pane, **kwds)
125
- self.pane = pane
126
- self.identifier = identifier
127
- if orientation is not None:
128
- if orientation.lower().startswith("h"):
129
- self.art.orientation = self.art.RIBBON_ORIENTATION_HORIZONTAL
130
- elif orientation.lower().startswith("v"):
131
- self.art.orientation = self.art.RIBBON_ORIENTATION_VERTICAL
132
- elif orientation.lower().startswith("a"):
133
- self.art.orientation = self.art.RIBBON_ORIENTATION_AUTO
134
- if show_labels is not None:
135
- self.art.show_labels = show_labels
136
- # Make myself known in context
137
- if not hasattr(context, "_ribbons"):
138
- self.context._ribbons = dict()
139
- self.context._ribbons[self.identifier] = self
140
-
141
- self.storage = Settings(self.context.kernel.name, f"ribbon_{identifier}.cfg")
142
- self.storage.read_configuration()
143
-
144
- self.allow_labels = bool(show_labels is None or show_labels)
145
- # Layout properties.
146
- if self.allow_labels:
147
- self.toggle_show_labels(context.setting(bool, "ribbon_show_labels", True))
148
-
149
- self._pages = []
150
- self.set_default_pages()
151
- # Define Ribbon.
152
- self.__set_ribbonbar()
153
-
154
- def get_default_config(self):
155
- if self.identifier == "tools":
156
- config = [
157
- {
158
- "id": "tools", # identifier
159
- "label": "Tools", # Label
160
- "panels": [ # Panels to include
161
- {
162
- "id": "select",
163
- "label": "Select + Edit",
164
- "seq": 1,
165
- },
166
- {
167
- "id": "lasercontrol",
168
- "label": "Laser",
169
- "seq": 2,
170
- },
171
- {
172
- "id": "tool",
173
- "label": "Create",
174
- "seq": 3,
175
- },
176
- {
177
- "id": "extended_tools",
178
- "label": "Extended Tools",
179
- "seq": 4,
180
- },
181
- {
182
- "id": "group",
183
- "label": "Group",
184
- "seq": 5,
185
- },
186
- ],
187
- "seq": 1, # Sequence
188
- },
189
- ]
190
- elif self.identifier == "primary":
191
- config = [
192
- {
193
- "id": "home", # identifier
194
- "label": "Project", # Label
195
- "panels": [ # Panels to include
196
- {
197
- "id": "project",
198
- "label": "Project",
199
- "seq": 1,
200
- },
201
- {
202
- "id": "basicediting",
203
- "label": "Edit",
204
- "seq": 2,
205
- },
206
- {
207
- "id": "undo",
208
- "label": "Undo",
209
- "seq": 3,
210
- },
211
- {
212
- "id": "preparation",
213
- "label": "Prepare",
214
- "seq": 4,
215
- },
216
- {
217
- "id": "control",
218
- "label": "Control",
219
- "seq": 5,
220
- },
221
- {
222
- "id": "jobstart",
223
- "label": "Execute",
224
- "seq": 5,
225
- },
226
- ],
227
- "seq": 1, # Sequence
228
- },
229
- {
230
- "id": "modify",
231
- "label": "Modify",
232
- "panels": [
233
- {
234
- "id": "undo",
235
- "label": "Undo",
236
- "seq": 1,
237
- },
238
- {
239
- "id": "modify",
240
- "label": "Modification",
241
- "seq": 2,
242
- },
243
- {
244
- "id": "geometry",
245
- "label": "Geometry",
246
- "seq": 3,
247
- },
248
- {
249
- "id": "align",
250
- "label": "Alignment",
251
- "seq": 4,
252
- },
253
- ],
254
- "seq": 2,
255
- },
256
- {
257
- "id": "config",
258
- "label": "Config",
259
- "panels": [ # Panels to include
260
- {
261
- "id": "config",
262
- "label": "Configuration",
263
- "seq": 1,
264
- },
265
- {
266
- "id": "device",
267
- "label": "Devices",
268
- "seq": 1,
269
- },
270
- ],
271
- "seq": 3,
272
- },
273
- ]
274
- else:
275
- config = []
276
-
277
- return config
278
-
279
- def get_current_config(self):
280
- # Is the storage empty? Then we use the default config
281
- config = self.get_default_config()
282
- testid = self.storage.read_persistent(str, "Ribbon", "identifier", "")
283
- # print(f"testid='{testid}', should be: {self.identifier}")
284
- if testid != self.identifier:
285
- # Thats fishy...
286
- return config
287
- flag = self.storage.read_persistent(
288
- bool, "Ribbon", "show_labels", self.art.show_labels
289
- )
290
- self.art.show_labels = flag
291
- newconfig = []
292
- page_idx = 0
293
- while True:
294
- section = f"Page_{page_idx + 1}"
295
- info = self.storage.read_persistent_string_dict(section)
296
- if info is None or len(info) == 0:
297
- break
298
- # print (info)
299
- newpage = {"id": "id", "label": "Label", "seq": 1}
300
- k = f"{section}/id"
301
- if k in info:
302
- value = self.storage.read_persistent(str, section, "id", "")
303
- newpage["id"] = value
304
- k = f"{section}/label"
305
- if k in info:
306
- value = self.storage.read_persistent(str, section, "label", "")
307
- newpage["label"] = value
308
- k = f"{section}/seq"
309
- if k in info:
310
- value = self.storage.read_persistent(int, section, "seq", None)
311
- if value:
312
- try:
313
- newpage["seq"] = int(value)
314
- except ValueError:
315
- pass
316
- panel_list = []
317
- panel_idx = 0
318
- while True:
319
- k = f"panel_{panel_idx + 1}"
320
- panel_info = self.storage.read_persistent(tuple, section, k, None)
321
- # print (f"{k} : {panel_info}")
322
- if (
323
- panel_info is None
324
- or not isinstance(panel_info, (list, tuple))
325
- or len(panel_info) < 3
326
- ):
327
- break
328
- panel_dict = dict()
329
- panel_dict["id"] = panel_info[0]
330
- panel_dict["seq"] = int(panel_info[1])
331
- panel_dict["label"] = panel_info[2]
332
- panel_list.append(panel_dict)
333
-
334
- panel_idx += 1
335
-
336
- newpage["panels"] = panel_list
337
- if len(panel_list) > 0:
338
- newconfig.append(newpage)
339
-
340
- page_idx += 1
341
-
342
- if len(newconfig) > 0:
343
- config = newconfig
344
- return config
345
-
346
- def set_default_pages(self):
347
- self._pages = self.get_current_config()
348
- paths = []
349
- for page_entry in self._pages:
350
- for panel_entry in page_entry["panels"]:
351
- ppath = f"button/{panel_entry['id']}/*"
352
- if ppath not in paths:
353
- new_values = []
354
- for obj, kname, sname in self.context.kernel.find(ppath):
355
- new_values.append((obj, kname, sname))
356
- self.set_panel_buttons(panel_entry["id"], new_values)
357
- paths.append(ppath)
358
-
359
- def set_panel_buttons(self, key, new_values):
360
- found = 0
361
- for page_entry in self._pages:
362
- if "_object" not in page_entry:
363
- continue
364
- if "panels" not in page_entry:
365
- continue
366
- pageobj = page_entry["_object"]
367
- for panel_entry in page_entry["panels"]:
368
- if "_object" not in panel_entry:
369
- continue
370
- panelobj = panel_entry["_object"]
371
- if panel_entry["id"].lower() == key.lower():
372
- # print (key, page_entry["id"], panel_entry)
373
- panelobj.set_buttons(new_values)
374
- found += 1
375
- # if found == 0:
376
- # print (f"Did not find a panel for {key}")
377
-
378
- def restart(self):
379
- self.storage.read_configuration()
380
- self.art.establish_colors()
381
- self.pages = []
382
- self.set_default_pages()
383
- # Define Ribbon.
384
- self.__set_ribbonbar()
385
- # And now we query the kernel to recreate the buttons
386
- paths = []
387
- for page_entry in self._pages:
388
- for panel_entry in page_entry["panels"]:
389
- ppath = f"button/{panel_entry['id']}/*"
390
- if ppath not in paths:
391
- new_values = []
392
- for obj, kname, sname in self.context.kernel.find(ppath):
393
- new_values.append((obj, kname, sname))
394
- self.set_panel_buttons(panel_entry["id"], new_values)
395
- paths.append(ppath)
396
- self.art.current_page = self.first_page()
397
- self.modified()
398
-
399
- @signal_listener("ribbon_recreate")
400
- def on_recreate(self, origin, target, *args):
401
- if target is not None and target != self.identifier:
402
- return
403
- self.restart()
404
-
405
- @signal_listener("ribbon_show_labels")
406
- def on_show_labels_change(self, origin, value, *args):
407
- if self.allow_labels:
408
- self.toggle_show_labels(value)
409
-
410
- @lookup_listener("button/basicediting")
411
- def set_editing_buttons(self, new_values, old_values):
412
- self.set_panel_buttons("basicediting", new_values)
413
-
414
- @lookup_listener("button/undo")
415
- def set_undo_buttons(self, new_values, old_values):
416
- self.set_panel_buttons("undo", new_values)
417
-
418
- @lookup_listener("button/project")
419
- def set_project_buttons(self, new_values, old_values):
420
- self.set_panel_buttons("project", new_values)
421
-
422
- @lookup_listener("button/modify")
423
- def set_modify_buttons(self, new_values, old_values):
424
- self.set_panel_buttons("modify", new_values)
425
-
426
- @lookup_listener("button/geometry")
427
- def set_geometry_buttons(self, new_values, old_values):
428
- self.set_panel_buttons("geometry", new_values)
429
-
430
- @lookup_listener("button/preparation")
431
- def set_preparation_buttons(self, new_values, old_values):
432
- self.set_panel_buttons("preparation", new_values)
433
-
434
- @lookup_listener("button/jobstart")
435
- def set_jobstart_buttons(self, new_values, old_values):
436
- self.set_panel_buttons("jobstart", new_values)
437
-
438
- @lookup_listener("button/control")
439
- def set_control_buttons(self, new_values, old_values):
440
- self.set_panel_buttons("control", new_values)
441
-
442
- @lookup_listener("button/device")
443
- def set_device_buttons(self, new_values, old_values):
444
- self.set_panel_buttons("device", new_values)
445
-
446
- @lookup_listener("button/config")
447
- def set_config_buttons(self, new_values, old_values):
448
- self.set_panel_buttons("config", new_values)
449
-
450
- @lookup_listener("button/align")
451
- def set_align_buttons(self, new_values, old_values):
452
- self.set_panel_buttons("align", new_values)
453
-
454
- @lookup_listener("button/select")
455
- def set_select_buttons(self, new_values, old_values):
456
- self.set_panel_buttons("select", new_values)
457
-
458
- @lookup_listener("button/tool")
459
- def set_tool_buttons(self, new_values, old_values):
460
- self.set_panel_buttons("tool", new_values)
461
-
462
- @lookup_listener("button/lasercontrol")
463
- def set_lasercontrol_buttons(self, new_values, old_values):
464
- self.set_panel_buttons("lasercontrol", new_values)
465
-
466
- @lookup_listener("button/extended_tools")
467
- def set_tool_extended_buttons(self, new_values, old_values):
468
- self.set_panel_buttons("extended_tools", new_values)
469
-
470
- @lookup_listener("button/group")
471
- def set_group_buttons(self, new_values, old_values):
472
- self.set_panel_buttons("group", new_values)
473
-
474
- @signal_listener("emphasized")
475
- def on_emphasis_change(self, origin, *args):
476
- self.apply_enable_rules()
477
-
478
- @signal_listener("undoredo")
479
- def on_undostate_change(self, origin, *args):
480
- self.apply_enable_rules()
481
-
482
- @signal_listener("selected")
483
- def on_selected_change(self, origin, node=None, *args):
484
- self.apply_enable_rules()
485
-
486
- @signal_listener("icons")
487
- def on_requested_change(self, origin, node=None, *args):
488
- self.apply_enable_rules()
489
- self.redrawn()
490
-
491
- @signal_listener("tool_changed")
492
- def on_tool_changed(self, origin, newtool=None, *args):
493
- # Signal provides a tuple with (togglegroup, id)
494
- if newtool is None:
495
- return
496
- if isinstance(newtool, (list, tuple)):
497
- group = newtool[0].lower() if newtool[0] is not None else ""
498
- identifier = newtool[1].lower() if newtool[1] is not None else ""
499
- else:
500
- group = newtool
501
- identifier = ""
502
-
503
- for page in self.pages:
504
- for panel in page.panels:
505
- for button in panel.buttons:
506
- if button.group != group:
507
- continue
508
- button.set_button_toggle(button.identifier == identifier)
509
- self.apply_enable_rules()
510
- self.modified()
511
-
512
- def __set_ribbonbar(self):
513
- """
514
- GUI Specific creation of ribbonbar.
515
- @return:
516
- """
517
- for entry in sorted(self._pages, key=lambda d: d["seq"]):
518
- name_id = entry["id"]
519
- label = _(entry["label"])
520
- panel_list = entry["panels"]
521
- page = self.add_page(
522
- name_id,
523
- wx.ID_ANY,
524
- label,
525
- icons8_opened_folder.GetBitmap(resize=16),
526
- )
527
- entry["_object"] = page
528
- for pentry in sorted(panel_list, key=lambda d: d["seq"]):
529
- p_name_id = pentry["id"]
530
- p_label = _(pentry["label"])
531
- panel = self.add_panel(
532
- p_name_id,
533
- parent=page,
534
- id=wx.ID_ANY,
535
- label=p_label,
536
- icon=icons8_opened_folder.GetBitmap(),
537
- )
538
- pentry["_object"] = panel
539
-
540
- self.modified()
541
-
542
- def pane_show(self):
543
- pass
544
-
545
- def pane_hide(self):
546
- """
547
- On pane_hide all the listeners are disabled.
548
- @return:
549
- """
550
- for page in self.pages:
551
- for panel in page.panels:
552
- for key, listener in panel._registered_signals:
553
- self.context.unlisten(key, listener)
554
-
555
- @signal_listener("page")
556
- def on_page_signal(self, origin, pagename=None, *args):
557
- """
558
- Page listener to force the given active page on the triggering of a page signal.
559
- @param origin:
560
- @param pagename:
561
- @param args:
562
- @return:
563
- """
564
- if pagename is None:
565
- return
566
- pagename = pagename.lower()
567
- if pagename == "":
568
- pagename = "project"
569
- for p in self.pages:
570
- if p.label.lower() == pagename:
571
- self.art.current_page = p
572
- self.modified()
573
- if getattr(self.context.root, "_active_page", "") != pagename:
574
- setattr(self.context.root, "_active_page", pagename)
575
-
576
-
577
- class RibbonEditor(wx.Panel):
578
- """
579
- RibbonEditor is a panel that allows you to define the content
580
- of the ribbonbar on top of Mks main window
581
- """
582
-
583
- def __init__(self, *args, context=None, **kwds):
584
- # begin wxGlade: PassesPanel.__init__
585
- kwds["style"] = kwds.get("style", 0)
586
- wx.Panel.__init__(self, *args, **kwds)
587
-
588
- self.context = context
589
- self.ribbon_identifier = "primary"
590
-
591
- self.available_options = []
592
- self.available_labels = []
593
- self._config = None
594
- self.current_page = None
595
-
596
- sizer_main = wx.BoxSizer(wx.HORIZONTAL)
597
- sizer_ribbons = StaticBoxSizer(self, wx.ID_ANY, _("Ribbons"), wx.VERTICAL)
598
-
599
- sizer_pages = StaticBoxSizer(self, wx.ID_ANY, _("Pages"), wx.VERTICAL)
600
- sizer_panels = StaticBoxSizer(self, wx.ID_ANY, _("Panels"), wx.VERTICAL)
601
- sizer_available_panels = StaticBoxSizer(
602
- self, wx.ID_ANY, _("Available Panels"), wx.VERTICAL
603
- )
604
-
605
- choices = [v for v in self.context._ribbons]
606
- self.combo_ribbons = wx.ComboBox(
607
- self, wx.ID_ANY, choices=choices, style=wx.CB_DROPDOWN | wx.CB_READONLY
608
- )
609
- self.check_labels = wx.CheckBox(self, wx.ID_ANY, _("Show the Ribbon Labels"))
610
-
611
- sizer_ribbons.Add(self.combo_ribbons, 0, wx.EXPAND, 0)
612
- sizer_ribbons.Add(self.check_labels, 0, wx.EXPAND, 0)
613
- self.list_pages = wx.ListBox(self, wx.ID_ANY, style=wx.LB_SINGLE)
614
- self.text_param_page = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
615
-
616
- self.list_panels = wx.ListBox(self, wx.ID_ANY, style=wx.LB_SINGLE)
617
- self.button_add_page = wx.StaticBitmap(
618
- self, wx.ID_ANY, size=dip_size(self, 30, 30)
619
- )
620
- self.button_del_page = wx.StaticBitmap(
621
- self, wx.ID_ANY, size=dip_size(self, 30, 30)
622
- )
623
- self.button_up_page = wx.StaticBitmap(
624
- self, wx.ID_ANY, size=dip_size(self, 30, 30)
625
- )
626
- self.button_down_page = wx.StaticBitmap(
627
- self, wx.ID_ANY, size=dip_size(self, 30, 20)
628
- )
629
- testsize = dip_size(self, 20, 20)
630
- iconsize = testsize[0]
631
- self.button_add_page.SetBitmap(
632
- icon_add_new.GetBitmap(resize=iconsize, buffer=1)
633
- )
634
- self.button_del_page.SetBitmap(icon_trash.GetBitmap(resize=iconsize, buffer=1))
635
- self.button_up_page.SetBitmap(icons8_up.GetBitmap(resize=iconsize, buffer=1))
636
- self.button_down_page.SetBitmap(
637
- icons8_down.GetBitmap(resize=iconsize, buffer=1)
638
- )
639
-
640
- self.button_del_panel = wx.StaticBitmap(
641
- self, wx.ID_ANY, size=dip_size(self, 30, 30)
642
- )
643
- self.button_up_panel = wx.StaticBitmap(
644
- self, wx.ID_ANY, size=dip_size(self, 30, 30)
645
- )
646
- self.button_down_panel = wx.StaticBitmap(
647
- self, wx.ID_ANY, size=dip_size(self, 30, 30)
648
- )
649
- self.button_del_panel.SetBitmap(icon_trash.GetBitmap(resize=iconsize, buffer=1))
650
- self.button_up_panel.SetBitmap(icons8_up.GetBitmap(resize=iconsize, buffer=1))
651
- self.button_down_panel.SetBitmap(
652
- icons8_down.GetBitmap(resize=iconsize, buffer=1)
653
- )
654
-
655
- self.list_options = wx.ListBox(self, wx.ID_ANY, style=wx.LB_SINGLE)
656
- self.button_add_panel = wx.Button(self, wx.ID_ANY, _("Add to page"))
657
- self.button_apply = wx.Button(self, wx.ID_ANY, _("Apply"))
658
- self.button_reset = wx.Button(self, wx.ID_ANY, _("Reset to Default"))
659
-
660
- sizer_button = wx.BoxSizer(wx.VERTICAL)
661
- sizer_button.Add(self.button_add_panel, 1, wx.EXPAND, 0)
662
- sizer_button.Add(self.button_apply, 1, wx.EXPAND, 0)
663
- sizer_button.Add(self.button_reset, 1, wx.EXPAND, 0)
664
-
665
- sizer_available_panels.Add(self.list_options, 1, wx.EXPAND, 0)
666
- sizer_available_panels.Add(sizer_button, 0, wx.EXPAND, 0)
667
-
668
- hsizer_page = wx.BoxSizer(wx.HORIZONTAL)
669
- hsizer_page.Add(self.text_param_page, 1, wx.EXPAND, 0)
670
- hsizer_page.Add(self.button_add_page, 0, wx.EXPAND, 0)
671
- hsizer_page.Add(self.button_del_page, 0, wx.EXPAND, 0)
672
- hsizer_page.Add(self.button_up_page, 0, wx.EXPAND, 0)
673
- hsizer_page.Add(self.button_down_page, 0, wx.EXPAND, 0)
674
-
675
- sizer_pages.Add(self.list_pages, 1, wx.EXPAND, 0)
676
- sizer_pages.Add(hsizer_page, 0, wx.EXPAND, 0)
677
-
678
- hsizer_panel = wx.BoxSizer(wx.HORIZONTAL)
679
- hsizer_panel.Add(self.button_del_panel, 0, wx.EXPAND, 0)
680
- hsizer_panel.Add(self.button_up_panel, 0, wx.EXPAND, 0)
681
- hsizer_panel.Add(self.button_down_panel, 0, wx.EXPAND, 0)
682
- sizer_panels.Add(self.list_panels, 1, wx.EXPAND, 0)
683
- sizer_panels.Add(hsizer_panel, 0, wx.EXPAND, 0)
684
-
685
- sizer_active_config = wx.BoxSizer(wx.VERTICAL)
686
- sizer_active_config.Add(sizer_ribbons, 0, wx.EXPAND, 0)
687
- sizer_active_config.Add(sizer_pages, 1, wx.EXPAND, 0)
688
- sizer_active_config.Add(sizer_panels, 1, wx.EXPAND, 0)
689
-
690
- sizer_main.Add(sizer_active_config, 1, wx.EXPAND, 0)
691
- sizer_main.Add(sizer_available_panels, 1, wx.EXPAND, 0)
692
-
693
- # Explanatory tooltips
694
- self.button_add_panel.SetToolTip(
695
- _("Add the selected panel to the selected page")
696
- )
697
- self.button_apply.SetToolTip(_("Apply the configuration"))
698
- self.button_reset.SetToolTip(
699
- _("Reset the ribbon appearance to the default configuration")
700
- )
701
-
702
- self.button_add_page.SetToolTip(_("Add an additional page to the ribbon"))
703
- self.button_del_page.SetToolTip(_("Remove the selected page from the list"))
704
- self.button_down_page.SetToolTip(
705
- _("Decrease the position of the selected page")
706
- )
707
- self.button_up_page.SetToolTip(_("Increase the position of the selected page"))
708
- self.text_param_page.SetToolTip(_("Modify the label of the selected page"))
709
-
710
- self.button_del_panel.SetToolTip(_("Remove the selected panel from the list"))
711
- self.button_down_panel.SetToolTip(
712
- _("Decrease the position of the selected panel")
713
- )
714
- self.button_up_panel.SetToolTip(
715
- _("Increase the position of the selected panel")
716
- )
717
- self.check_labels.SetToolTip(
718
- _("Allow/suppress the display of labels beneath the icons in the ribbon")
719
- )
720
-
721
- self.fill_options()
722
- self.SetSizer(sizer_main)
723
- self.Layout()
724
- self.list_options.Bind(wx.EVT_LISTBOX, self.on_list_options_click)
725
- self.list_options.Bind(wx.EVT_LISTBOX_DCLICK, self.on_list_options_dclick)
726
-
727
- self.button_add_panel.Bind(wx.EVT_BUTTON, self.on_button_add_panel_click)
728
- self.button_apply.Bind(wx.EVT_BUTTON, self.on_button_apply_click)
729
- self.button_reset.Bind(wx.EVT_BUTTON, self.on_button_reset_click)
730
- self.combo_ribbons.Bind(wx.EVT_COMBOBOX, self.on_combo_ribbon)
731
-
732
- self.list_pages.Bind(wx.EVT_LISTBOX, self.on_list_pages_click)
733
- self.list_panels.Bind(wx.EVT_LISTBOX, self.on_list_panels_click)
734
-
735
- self.text_param_page.Bind(wx.EVT_TEXT, self.on_text_label)
736
- self.text_param_page.Bind(wx.EVT_TEXT_ENTER, self.on_text_label_enter)
737
-
738
- self.button_add_page.Bind(wx.EVT_LEFT_DOWN, self.on_button_add_page_click)
739
- self.button_del_page.Bind(wx.EVT_LEFT_DOWN, self.on_button_page_delete)
740
- self.button_up_page.Bind(wx.EVT_LEFT_DOWN, self.on_move_page_up)
741
- self.button_down_page.Bind(wx.EVT_LEFT_DOWN, self.on_move_page_down)
742
-
743
- self.button_del_panel.Bind(wx.EVT_LEFT_DOWN, self.on_button_panel_delete)
744
- self.button_up_panel.Bind(wx.EVT_LEFT_DOWN, self.on_move_panel_up)
745
- self.button_down_panel.Bind(wx.EVT_LEFT_DOWN, self.on_move_panel_down)
746
-
747
- self.combo_ribbons.SetSelection(0)
748
- self.on_combo_ribbon(None)
749
-
750
- # ---- Generic routines to access data
751
-
752
- def current_ribbon(self):
753
- result = None
754
- if hasattr(self.context, "_ribbons"):
755
- if self.ribbon_identifier in self.context._ribbons:
756
- result = self.context._ribbons[self.ribbon_identifier]
757
- return result
758
-
759
- def get_page(self, pageid=None):
760
- if pageid is None:
761
- pageid = self.current_page
762
- for p in self._config:
763
- if p["id"] == pageid:
764
- return p
765
- return None
766
-
767
- def update_page(self, pageid, newlabel=None, newseq=None):
768
- for p in self._config:
769
- if p["id"] == pageid:
770
- if newlabel is not None:
771
- p["label"] = newlabel
772
- if newseq is not None:
773
- p["seq"] = newseq
774
-
775
- # ---- Routines to fill listboxes
776
-
777
- def fill_pages(self, reload=False, reposition=None):
778
- self.list_pages.Clear()
779
- if reload is None:
780
- reload = False
781
- if reload:
782
- rib = self.current_ribbon()
783
- if rib is None:
784
- return
785
- self._config = rib.get_current_config()
786
- pages = []
787
- idx = 0
788
- for p_idx, page_entry in enumerate(
789
- sorted(self._config, key=lambda d: d["seq"])
790
- ):
791
- pages.append(f"{page_entry['id']} ({page_entry['label']})")
792
- if reposition is not None and page_entry["id"] == reposition:
793
- idx = p_idx
794
- self.list_pages.SetItems(pages)
795
- if len(pages) > 0:
796
- self.list_pages.SetSelection(idx)
797
- self.on_list_pages_click(None)
798
-
799
- def fill_panels(self, pageid=None, reposition=None):
800
- if pageid is None:
801
- pageid = self.current_page
802
- self.list_panels.Clear()
803
- panels = []
804
- tidx = 0
805
- cidx = 0
806
- for page_entry in self._config:
807
- if pageid == page_entry["id"]:
808
- panel_list = page_entry["panels"]
809
- for panel_entry in sorted(panel_list, key=lambda d: d["seq"]):
810
- panels.append(f"{panel_entry['id']}")
811
- if reposition is not None and reposition == panel_entry["id"]:
812
- tidx = cidx
813
- cidx += 1
814
-
815
- self.list_panels.SetItems(panels)
816
- if len(panels) > 0:
817
- self.list_panels.SetSelection(tidx)
818
-
819
- def fill_options(self):
820
- # Query all registered button sections
821
- self.available_options.clear()
822
- self.available_labels.clear()
823
- for d_name in self.context.kernel.match("button", suffix=False):
824
- secs = d_name.split("/")
825
- # print(f"{d_name} {secs}")
826
- if len(secs) > 2:
827
- section = secs[1]
828
- button = secs[2]
829
- try:
830
- idx = self.available_options.index(section)
831
- self.available_labels[idx] += f", {button}"
832
- except ValueError:
833
- self.available_options.append(section)
834
- self.available_labels.append(button)
835
-
836
- self.list_options.Clear()
837
- self.list_options.SetItems(self.available_options)
838
-
839
- # ---- Event Handler Routines
840
-
841
- def on_combo_ribbon(self, event):
842
- idx = self.combo_ribbons.GetSelection()
843
- if idx < 0:
844
- return
845
- rlist = list(self.context._ribbons.keys())
846
- if len(rlist) > 0:
847
- self.ribbon_identifier = rlist[idx]
848
- rib = self.current_ribbon()
849
- self.check_labels.SetValue(rib.art.show_labels)
850
- self.check_labels.Enable(rib.allow_labels)
851
- self.fill_pages(reload=True)
852
-
853
- def on_move_page_up(self, event):
854
- newconfig = []
855
- for p_idx, page_entry in enumerate(
856
- sorted(self._config, key=lambda d: d["seq"])
857
- ):
858
- if page_entry["id"] == self.current_page and p_idx > 0:
859
- newconfig.insert(p_idx - 1, page_entry)
860
- else:
861
- newconfig.append(page_entry)
862
- for p_idx, page_entry in enumerate(newconfig):
863
- page_entry["seq"] = p_idx + 1
864
- self._config = newconfig
865
- self.fill_pages(reposition=self.current_page)
866
-
867
- def on_move_page_down(self, event):
868
- newconfig = []
869
- for p_idx, page_entry in enumerate(
870
- sorted(self._config, key=lambda d: d["seq"], reverse=True)
871
- ):
872
- if page_entry["id"] == self.current_page and p_idx > 0:
873
- newconfig.insert(1, page_entry)
874
- else:
875
- newconfig.insert(0, page_entry)
876
- for p_idx, page_entry in enumerate(newconfig):
877
- page_entry["seq"] = p_idx + 1
878
- self._config = newconfig
879
- self.fill_pages(reposition=self.current_page)
880
-
881
- def on_list_pages_click(self, event):
882
- idx = self.list_pages.GetSelection()
883
- flag1 = idx >= 0
884
- self.button_del_page.Enable(flag1)
885
- self.button_down_page.Enable(flag1)
886
- self.button_up_page.Enable(flag1)
887
- self.button_add_page.Enable(True)
888
- page_label = ""
889
- if idx >= 0:
890
- term = self.list_pages.GetStringSelection()
891
- idx = term.find(" (")
892
- if idx >= 0:
893
- page = term[0:idx]
894
- self.current_page = page
895
- self.fill_panels()
896
- p = self.get_page(page)
897
- if p is not None:
898
- page_label = p["label"]
899
- self.text_param_page.SetValue(page_label)
900
-
901
- def on_button_page_delete(self, event):
902
- new_config = []
903
- for p in self._config:
904
- if p["id"] != self.current_page:
905
- new_config.append(p)
906
- if len(new_config) != self._config:
907
- self._config = new_config
908
- self.current_page = None
909
- self.fill_pages()
910
-
911
- def on_button_add_page_click(self, event):
912
- newid = f"page_{len(self._config) + 1}"
913
- newentry = {
914
- "id": newid,
915
- "label": f"Page {len(self._config) + 1}",
916
- "seq": len(self._config) + 1,
917
- "panels": [],
918
- }
919
- self._config.append(newentry)
920
- self.fill_pages(reposition=newid)
921
-
922
- def on_move_panel_up(self, event):
923
- idx = self.list_panels.GetSelection()
924
- if idx < 0:
925
- return
926
- panelid = self.list_panels.GetString(idx)
927
- for page in self._config:
928
- if page["id"] != self.current_page:
929
- continue
930
- panel_list = page["panels"]
931
- newconfig = []
932
- for p_idx, panel_entry in enumerate(
933
- sorted(panel_list, key=lambda d: d["seq"])
934
- ):
935
- if panel_entry["id"] == panelid and p_idx > 0:
936
- newconfig.insert(p_idx - 1, panel_entry)
937
- else:
938
- newconfig.append(panel_entry)
939
- for p_idx, panel_entry in enumerate(newconfig):
940
- panel_entry["seq"] = p_idx + 1
941
- page["panels"] = newconfig
942
- self.fill_panels(reposition=panelid)
943
-
944
- def on_move_panel_down(self, event):
945
- idx = self.list_panels.GetSelection()
946
- if idx < 0:
947
- return
948
- panelid = self.list_panels.GetString(idx)
949
- for page in self._config:
950
- if page["id"] != self.current_page:
951
- continue
952
- panelid = self.list_panels.GetString(idx)
953
- panel_list = page["panels"]
954
- newconfig = []
955
- for p_idx, panel_entry in enumerate(
956
- sorted(panel_list, key=lambda d: d["seq"], reverse=True)
957
- ):
958
- if panel_entry["id"] == panelid and p_idx > 0:
959
- newconfig.insert(1, panel_entry)
960
- else:
961
- newconfig.insert(0, panel_entry)
962
- for p_idx, panel_entry in enumerate(newconfig):
963
- panel_entry["seq"] = p_idx + 1
964
- page["panels"] = newconfig
965
- self.fill_panels(reposition=panelid)
966
-
967
- def on_button_panel_delete(self, event):
968
- idx = self.list_panels.GetSelection()
969
- if idx < 0:
970
- return
971
- panelid = self.list_panels.GetString(idx)
972
- for page in self._config:
973
- if page["id"] != self.current_page:
974
- continue
975
- panel_list = page["panels"]
976
- new_config = []
977
- seqnum = 1
978
- for panel in sorted(panel_list, key=lambda d: d["seq"]):
979
- if panel["id"] != panelid:
980
- panel["seq"] = seqnum
981
- new_config.append(panel)
982
- seqnum += 1
983
- page["panels"] = new_config
984
- self.fill_panels()
985
-
986
- def on_list_panels_click(self, event):
987
- idx = self.list_panels.GetSelection()
988
- flag1 = idx >= 0
989
- self.button_del_panel.Enable(flag1)
990
- self.button_down_panel.Enable(flag1)
991
- self.button_up_panel.Enable(flag1)
992
- if idx < 0:
993
- return
994
-
995
- def on_text_label(self, event):
996
- label = self.text_param_page.GetValue()
997
- self.update_page(self.current_page, newlabel=label)
998
-
999
- def on_text_label_enter(self, event):
1000
- self.fill_pages(reposition=self.current_page)
1001
-
1002
- def on_list_options_click(self, event):
1003
- ttip = ""
1004
- idx = self.list_options.GetSelection()
1005
- if idx >= 0:
1006
- self.button_add_panel.Enable(True)
1007
- ttip = f"{self.available_options[idx]}: {self.available_labels[idx]}"
1008
- else:
1009
- self.button_add_panel.Enable(False)
1010
- self.list_options.SetToolTip(ttip)
1011
-
1012
- def on_list_options_dclick(self, event):
1013
- self.on_button_add_panel_click(event)
1014
-
1015
- def on_button_add_panel_click(self, event):
1016
- idx = self.list_options.GetSelection()
1017
- if idx < 0:
1018
- return
1019
- panel = self.available_options[idx]
1020
- for page in self._config:
1021
- if page["id"] != self.current_page:
1022
- continue
1023
- if panel not in page["panels"]:
1024
- newpanel = {
1025
- "id": panel,
1026
- "label": panel,
1027
- "seq": len(page["panels"]) + 2,
1028
- }
1029
- page["panels"].append(newpanel)
1030
- self.fill_pages(reposition=self.current_page)
1031
-
1032
- def on_button_apply_click(self, event):
1033
- ob = self.current_ribbon()
1034
- if ob is None:
1035
- return
1036
- if len(self._config) == 0:
1037
- return
1038
- ob.storage.delete_all_persistent()
1039
- ob.storage.write_persistent("Ribbon", "identifier", self.ribbon_identifier)
1040
- show_labels = self.check_labels.GetValue()
1041
- ob.storage.write_persistent("Ribbon", "show_labels", show_labels)
1042
-
1043
- for p_idx, page_entry in enumerate(
1044
- sorted(self._config, key=lambda d: d["seq"])
1045
- ):
1046
- section = f"Page_{p_idx + 1}"
1047
- ob.storage.write_persistent(section, "id", page_entry["id"])
1048
- ob.storage.write_persistent(section, "label", page_entry["label"])
1049
- ob.storage.write_persistent(section, "seq", page_entry["seq"])
1050
- panel_list = page_entry["panels"]
1051
- for panel_idx, pentry in enumerate(
1052
- sorted(panel_list, key=lambda d: d["seq"])
1053
- ):
1054
- key = f"Panel_{panel_idx + 1}"
1055
- result = (
1056
- pentry["id"],
1057
- pentry["seq"],
1058
- pentry["label"],
1059
- )
1060
- ob.storage.write_persistent(section, key, result)
1061
- ob.storage.write_configuration()
1062
- ob.restart()
1063
- # self.context.signal("ribbon_recreate", self.ribbon_identifier)
1064
-
1065
- def on_button_reset_click(self, event):
1066
- rib = self.current_ribbon()
1067
- if rib is None:
1068
- return
1069
- self._config = rib.get_default_config()
1070
- self.fill_pages()
1071
-
1072
- def pane_hide(self):
1073
- pass
1074
-
1075
- def pane_show(self):
1076
- pass
1
+ """
2
+ The WxmRibbon Bar is a core aspect of MeerK40t's interaction. To allow fully dynamic buttons we use the ribbonbar
3
+ control widget built into meerk40t.
4
+
5
+ Panels are created in a static fashion within this class. But the contents of those individual ribbon panels are defined
6
+ in the kernel lookup.
7
+
8
+ service.register(
9
+ "button/control/Redlight",
10
+ {
11
+ ...<def>...
12
+ },
13
+ )
14
+
15
+ This setup allows us to define a large series of buttons with a button prefix and the name of a panel it should be
16
+ added to as well as a unique name. This defines within the meerk40t ecosystem a methodology of specifically creating
17
+ dynamic buttons. When the service changes because of a switch in the device (for example) it will trigger the lookup
18
+ listeners which will update their contents, triggering the update within this control.
19
+ """
20
+
21
+ import wx
22
+ from wx import aui
23
+
24
+ from meerk40t.gui.icons import (
25
+ get_default_icon_size,
26
+ icon_add_new,
27
+ icon_edit,
28
+ icon_trash,
29
+ icons8_down,
30
+ icons8_opened_folder,
31
+ icons8_up,
32
+ )
33
+ from meerk40t.gui.ribbon import RibbonBarPanel
34
+ from meerk40t.gui.wxutils import (
35
+ StaticBoxSizer,
36
+ TextCtrl,
37
+ dip_size,
38
+ wxButton,
39
+ wxCheckBox,
40
+ wxComboBox,
41
+ wxListBox,
42
+ wxStaticBitmap,
43
+ wxStaticText,
44
+ )
45
+ from meerk40t.kernel import Settings, lookup_listener, signal_listener
46
+
47
+ _ = wx.GetTranslation
48
+
49
+
50
+ def register_panel_ribbon(window, context):
51
+ iconsize = get_default_icon_size(context)
52
+ minh = 3 * iconsize + 25
53
+ pane_ribbon = (
54
+ aui.AuiPaneInfo()
55
+ .Name("ribbon")
56
+ .Top()
57
+ .BestSize(300, minh)
58
+ .FloatingSize(640, minh)
59
+ .Caption(_("Ribbon"))
60
+ .CaptionVisible(not context.pane_lock)
61
+ )
62
+ pane_ribbon.dock_proportion = 640
63
+ ribbon = MKRibbonBarPanel(
64
+ window, wx.ID_ANY, context=context, pane=pane_ribbon, identifier="primary"
65
+ )
66
+ pane_ribbon.control = ribbon
67
+ pane_ribbon.helptext = _("Toolbar with the main commands to control jobs and devices")
68
+
69
+ window.on_pane_create(pane_ribbon)
70
+ context.register("pane/ribbon", pane_ribbon)
71
+ context.register("ribbonbar/primary", ribbon)
72
+
73
+ minh = int(1.5 * iconsize)
74
+ pane_tool = (
75
+ aui.AuiPaneInfo()
76
+ .Name("tools")
77
+ .Left()
78
+ .BestSize(minh, 300)
79
+ .FloatingSize(minh, 640)
80
+ .Caption(_("Tools"))
81
+ .CaptionVisible(not context.pane_lock)
82
+ )
83
+ pane_tool.dock_proportion = 640
84
+ pane_tool.helptext = _("Icon-bar with the main object creation tools")
85
+ ribbon = MKRibbonBarPanel(
86
+ window,
87
+ wx.ID_ANY,
88
+ context=context,
89
+ pane=pane_tool,
90
+ identifier="tools",
91
+ orientation="auto",
92
+ show_labels=False,
93
+ )
94
+ pane_tool.control = ribbon
95
+
96
+ window.on_pane_create(pane_tool)
97
+ context.register("pane/tools", pane_tool)
98
+ context.register("ribbonbar/tools", ribbon)
99
+
100
+ pane_edittool = (
101
+ aui.AuiPaneInfo()
102
+ .Name("edittools")
103
+ .Left()
104
+ .BestSize(minh, 300)
105
+ .FloatingSize(minh, 640)
106
+ .Caption(_("Modify"))
107
+ .CaptionVisible(not context.pane_lock)
108
+ )
109
+ pane_edittool.dock_proportion = 640
110
+ pane_edittool.helptext = _("Icon-bar with object modification tools")
111
+ ribbon = MKRibbonBarPanel(
112
+ window,
113
+ wx.ID_ANY,
114
+ context=context,
115
+ pane=pane_edittool,
116
+ identifier="edittools",
117
+ orientation="auto",
118
+ show_labels=False,
119
+ )
120
+ pane_edittool.control = ribbon
121
+
122
+ window.on_pane_create(pane_edittool)
123
+ context.register("pane/edittools", pane_edittool)
124
+ context.register("ribbonbar/edittools", ribbon)
125
+
126
+ choices = [
127
+ {
128
+ "attr": "ribbon_show_labels",
129
+ "object": context,
130
+ "default": False,
131
+ "type": bool,
132
+ "label": _("Show the Ribbon Labels"),
133
+ "tip": _(
134
+ "Active: Show the labels for ribbonbar.\n"
135
+ "Inactive: Hide the ribbon labels."
136
+ ),
137
+ "page": "Gui",
138
+ "section": "Appearance",
139
+ },
140
+ ]
141
+
142
+ context.kernel.register_choices("preferences", choices)
143
+
144
+
145
+ class MKRibbonBarPanel(RibbonBarPanel):
146
+ def __init__(
147
+ self,
148
+ parent,
149
+ id,
150
+ context=None,
151
+ pane=None,
152
+ identifier=None,
153
+ orientation=None,
154
+ show_labels=None,
155
+ **kwds,
156
+ ):
157
+ RibbonBarPanel.__init__(self, parent, id, context, pane, **kwds)
158
+ self.pane = pane
159
+ self.identifier = identifier
160
+ self.userbuttons = []
161
+ if orientation is not None:
162
+ if orientation.lower().startswith("h"):
163
+ self.art.orientation = self.art.RIBBON_ORIENTATION_HORIZONTAL
164
+ elif orientation.lower().startswith("v"):
165
+ self.art.orientation = self.art.RIBBON_ORIENTATION_VERTICAL
166
+ elif orientation.lower().startswith("a"):
167
+ self.art.orientation = self.art.RIBBON_ORIENTATION_AUTO
168
+ if show_labels is not None:
169
+ self.art.show_labels = show_labels
170
+ # Make myself known in context
171
+ if not hasattr(context, "_ribbons"):
172
+ self.context._ribbons = {}
173
+ self.context._ribbons[self.identifier] = self
174
+
175
+ self.storage = Settings(
176
+ self.context.kernel.name, f"ribbon_{identifier}.cfg", create_backup=True
177
+ ) # keep backup
178
+ self.storage.read_configuration()
179
+
180
+ self.allow_labels = bool(show_labels is None or show_labels)
181
+ # Layout properties.
182
+ if self.allow_labels:
183
+ self.toggle_show_labels(context.setting(bool, "ribbon_show_labels", True))
184
+
185
+ self._pages = []
186
+ self.set_default_pages()
187
+ # Define Ribbon.
188
+ self.__set_ribbonbar()
189
+
190
+ def get_default_config(self):
191
+ button_config = []
192
+ if self.identifier == "tools":
193
+ ribbon_config = [
194
+ {
195
+ "id": "tools", # identifier
196
+ "label": "Tools", # Label
197
+ "panels": [ # Panels to include
198
+ {
199
+ "id": "select",
200
+ "label": "Select + Edit",
201
+ "seq": 1,
202
+ },
203
+ {
204
+ "id": "lasercontrol",
205
+ "label": "Laser",
206
+ "seq": 2,
207
+ },
208
+ {
209
+ "id": "tool",
210
+ "label": "Create",
211
+ "seq": 3,
212
+ },
213
+ {
214
+ "id": "extended_tools",
215
+ "label": "Extended Tools",
216
+ "seq": 4,
217
+ },
218
+ {
219
+ "id": "group",
220
+ "label": "Group",
221
+ "seq": 5,
222
+ },
223
+ ],
224
+ "seq": 1, # Sequence
225
+ },
226
+ ]
227
+ elif self.identifier == "primary":
228
+ ribbon_config = [
229
+ {
230
+ "id": "home", # identifier
231
+ "label": "Project", # Label
232
+ "panels": [ # Panels to include
233
+ {
234
+ "id": "project",
235
+ "label": "Project",
236
+ "seq": 1,
237
+ },
238
+ {
239
+ "id": "basicediting",
240
+ "label": "Edit",
241
+ "seq": 2,
242
+ },
243
+ {
244
+ "id": "undo",
245
+ "label": "Undo",
246
+ "seq": 3,
247
+ },
248
+ {
249
+ "id": "preparation",
250
+ "label": "Prepare",
251
+ "seq": 4,
252
+ },
253
+ {
254
+ "id": "control",
255
+ "label": "Control",
256
+ "seq": 5,
257
+ },
258
+ {
259
+ "id": "jobstart",
260
+ "label": "Execute",
261
+ "seq": 6,
262
+ },
263
+ {
264
+ "id": "user",
265
+ "label": "User",
266
+ "seq": 7,
267
+ },
268
+ ],
269
+ "seq": 1, # Sequence
270
+ },
271
+ {
272
+ "id": "modify",
273
+ "label": "Modify",
274
+ "panels": [
275
+ {
276
+ "id": "undo",
277
+ "label": "Undo",
278
+ "seq": 1,
279
+ },
280
+ {
281
+ "id": "modify",
282
+ "label": "Modification",
283
+ "seq": 2,
284
+ },
285
+ {
286
+ "id": "geometry",
287
+ "label": "Geometry",
288
+ "seq": 3,
289
+ },
290
+ {
291
+ "id": "align",
292
+ "label": "Alignment",
293
+ "seq": 4,
294
+ },
295
+ ],
296
+ "seq": 2,
297
+ },
298
+ {
299
+ "id": "config",
300
+ "label": "Config",
301
+ "panels": [ # Panels to include
302
+ {
303
+ "id": "config",
304
+ "label": "Configuration",
305
+ "seq": 1,
306
+ },
307
+ {
308
+ "id": "device",
309
+ "label": "Devices",
310
+ "seq": 1,
311
+ },
312
+ ],
313
+ "seq": 3,
314
+ },
315
+ ]
316
+ elif self.identifier == "edittools":
317
+ ribbon_config = [
318
+ {
319
+ "id": "modify",
320
+ "label": "Modify",
321
+ "panels": [
322
+ {
323
+ "id": "undo",
324
+ "label": "Undo",
325
+ "seq": 1,
326
+ },
327
+ {
328
+ "id": "group",
329
+ "label": "Group",
330
+ "seq": 2,
331
+ },
332
+ {
333
+ "id": "modify",
334
+ "label": "Modification",
335
+ "seq": 3,
336
+ },
337
+ {
338
+ "id": "geometry",
339
+ "label": "Geometry",
340
+ "seq": 4,
341
+ },
342
+ {
343
+ "id": "align",
344
+ "label": "Alignment",
345
+ "seq": 5,
346
+ },
347
+ ],
348
+ "seq": 1,
349
+ },
350
+ ]
351
+ else:
352
+ ribbon_config = []
353
+
354
+ return ribbon_config, button_config
355
+
356
+ def get_current_config(self):
357
+ import meerk40t.gui.icons as mkicons
358
+
359
+ icon_list = []
360
+ for entry in dir(mkicons):
361
+ # print (entry)
362
+ if entry.startswith("icon"):
363
+ s = getattr(mkicons, entry)
364
+ if isinstance(s, (mkicons.VectorIcon, mkicons.PyEmbeddedImage)):
365
+ icon_list.append(entry)
366
+
367
+ # Is the storage empty? Then we use the default config
368
+ ribbon_config, button_config = self.get_default_config()
369
+ testid = self.storage.read_persistent(str, "Ribbon", "identifier", "")
370
+ # print(f"testid='{testid}', should be: {self.identifier}")
371
+ if testid != self.identifier:
372
+ # Thats fishy...
373
+ return ribbon_config, button_config
374
+ flag = self.storage.read_persistent(
375
+ bool, "Ribbon", "show_labels", self.art.show_labels
376
+ )
377
+ self.art.show_labels = flag
378
+ newconfig = []
379
+ newbuttons = []
380
+ page_idx = 0
381
+ while True:
382
+ section = f"Page_{page_idx + 1}"
383
+ info = self.storage.read_persistent_string_dict(section)
384
+ if info is None or len(info) == 0:
385
+ break
386
+ # print (info)
387
+ newpage = {"id": "id", "label": "Label", "seq": 1}
388
+ k = f"{section}/id"
389
+ if k in info:
390
+ value = self.storage.read_persistent(str, section, "id", "")
391
+ newpage["id"] = value
392
+ k = f"{section}/label"
393
+ if k in info:
394
+ value = self.storage.read_persistent(str, section, "label", "")
395
+ newpage["label"] = value
396
+ k = f"{section}/seq"
397
+ if k in info:
398
+ value = self.storage.read_persistent(int, section, "seq", None)
399
+ if value:
400
+ try:
401
+ newpage["seq"] = int(value)
402
+ except ValueError:
403
+ pass
404
+ panel_list = []
405
+ panel_idx = 0
406
+ while True:
407
+ k = f"panel_{panel_idx + 1}"
408
+ panel_info = self.storage.read_persistent(tuple, section, k, None)
409
+ # print (f"{k} : {panel_info}")
410
+ if (
411
+ panel_info is None
412
+ or not isinstance(panel_info, (list, tuple))
413
+ or len(panel_info) < 3
414
+ ):
415
+ break
416
+ panel_dict = {}
417
+ panel_dict["id"] = panel_info[0]
418
+ panel_dict["seq"] = int(panel_info[1])
419
+ panel_dict["label"] = panel_info[2]
420
+ panel_list.append(panel_dict)
421
+
422
+ panel_idx += 1
423
+
424
+ newpage["panels"] = panel_list
425
+ if panel_list:
426
+ newconfig.append(newpage)
427
+
428
+ page_idx += 1
429
+
430
+ # ------ Read user defined buttons...
431
+ button_idx = 0
432
+
433
+ def delayed_command(command):
434
+ def handler(*args):
435
+ cmd = command.replace("\\n", "\n")
436
+ self.context(cmd + "\n")
437
+
438
+ return handler
439
+
440
+ while True:
441
+ section = f"Button_{button_idx + 1}"
442
+ info = self.storage.read_persistent_string_dict(section)
443
+ if info is None or len(info) == 0:
444
+ break
445
+ # print (info)
446
+ userbutton = {
447
+ "id": f"user_button_{button_idx+1}",
448
+ "label": "Label",
449
+ "action_left": "",
450
+ "action_right": "",
451
+ "enable": "always",
452
+ "visible": "always",
453
+ "tip": _("This could be your tooltip"),
454
+ "icon": "icon_edit",
455
+ "seq": 1,
456
+ }
457
+ for content in ("label", "tip", "action_left", "action_right"):
458
+ k = f"{section}/{content}"
459
+ if k in info:
460
+ value = self.storage.read_persistent(str, section, content, "")
461
+ userbutton[content] = value
462
+ for content in ("enable", "visible"):
463
+ k = f"{section}/{content}"
464
+ if k in info:
465
+ value = self.storage.read_persistent(str, section, content, "")
466
+ if value in ("always", "selected", "selected2"):
467
+ userbutton[content] = value
468
+
469
+ k = f"{section}/icon"
470
+ if k in info:
471
+ value = self.storage.read_persistent(str, section, "icon", "")
472
+ if value in icon_list:
473
+ userbutton["icon"] = value
474
+ k = f"{section}/seq"
475
+ if k in info:
476
+ value = self.storage.read_persistent(int, section, "seq", None)
477
+ if value:
478
+ try:
479
+ userbutton["seq"] = int(value)
480
+ except ValueError:
481
+ pass
482
+ d = {
483
+ "label": userbutton["label"],
484
+ "icon": getattr(mkicons, userbutton["icon"], None),
485
+ "tip": userbutton["tip"],
486
+ }
487
+ if userbutton["action_left"]:
488
+ d["action"] = delayed_command(userbutton["action_left"])
489
+ if userbutton["action_right"]:
490
+ d["action_right"] = delayed_command(userbutton["action_right"])
491
+ if userbutton["enable"] == "selected":
492
+ d["rule_enabled"] = (
493
+ lambda cond: len(list(self.context.elements.elems(emphasized=True)))
494
+ > 0
495
+ )
496
+ if userbutton["enable"] == "selected2":
497
+ d["rule_enabled"] = (
498
+ lambda cond: len(list(self.context.elements.elems(emphasized=True)))
499
+ > 1
500
+ )
501
+ if userbutton["visible"] == "selected":
502
+ d["rule_visible"] = (
503
+ lambda cond: len(list(self.context.elements.elems(emphasized=True)))
504
+ > 0
505
+ )
506
+ if userbutton["visible"] == "selected2":
507
+ d["rule_visible"] = (
508
+ lambda cond: len(list(self.context.elements.elems(emphasized=True)))
509
+ > 1
510
+ )
511
+
512
+ self.context.kernel.register(f"button/user/{userbutton['id']}", d)
513
+ button_config.append(userbutton)
514
+ button_idx += 1
515
+
516
+ if newconfig:
517
+ ribbon_config = newconfig
518
+ return ribbon_config, button_config
519
+
520
+ def set_default_pages(self):
521
+ self._pages, self.userbuttons = self.get_current_config()
522
+ paths = []
523
+ for page_entry in self._pages:
524
+ for panel_entry in page_entry["panels"]:
525
+ ppath = f"button/{panel_entry['id']}/*"
526
+ if ppath not in paths:
527
+ new_values = []
528
+ new_values.extend(
529
+ (obj, kname, sname)
530
+ for obj, kname, sname in self.context.kernel.find(ppath)
531
+ )
532
+ self.set_panel_buttons(panel_entry["id"], new_values)
533
+ paths.append(ppath)
534
+
535
+ def set_panel_buttons(self, key, new_values):
536
+ found = 0
537
+ for page_entry in self._pages:
538
+ if "_object" not in page_entry:
539
+ continue
540
+ if "panels" not in page_entry:
541
+ continue
542
+ for panel_entry in page_entry["panels"]:
543
+ if "_object" not in panel_entry:
544
+ continue
545
+ panelobj = panel_entry["_object"]
546
+ if panel_entry["id"].lower() == key.lower():
547
+ # print (key, page_entry["id"], panel_entry)
548
+ panelobj.set_buttons(new_values)
549
+ found += 1
550
+ # if found == 0:
551
+ # print (f"Did not find a panel for {key}")
552
+
553
+ def restart(self):
554
+ self.storage.read_configuration()
555
+ self.art.establish_colors()
556
+ self.pages = []
557
+ self.userbuttons = []
558
+ self.set_default_pages()
559
+ # Define Ribbon.
560
+ self.__set_ribbonbar()
561
+ # And now we query the kernel to recreate the buttons
562
+ paths = []
563
+ for page_entry in self._pages:
564
+ for panel_entry in page_entry["panels"]:
565
+ ppath = f"button/{panel_entry['id']}/*"
566
+ if ppath not in paths:
567
+ new_values = []
568
+ new_values.extend(
569
+ (obj, kname, sname)
570
+ for obj, kname, sname in self.context.kernel.find(ppath)
571
+ )
572
+ self.set_panel_buttons(panel_entry["id"], new_values)
573
+ paths.append(ppath)
574
+ self.art.current_page = self.first_page()
575
+ self.modified()
576
+
577
+ @signal_listener("ribbon_recreate")
578
+ def on_recreate(self, origin, target, *args):
579
+ if target is not None and target != self.identifier:
580
+ return
581
+ self.restart()
582
+
583
+ @signal_listener("ribbon_show_labels")
584
+ def on_show_labels_change(self, origin, value, *args):
585
+ if self.allow_labels:
586
+ self.toggle_show_labels(value)
587
+
588
+ # Notabene: the lookup listener design will look for partial fits!
589
+ # So a listener to "button/tool" would get changes from
590
+ # "button/tool", "button/tools" and "button/toolabcdefgh"
591
+ # So to add buttons of your own make sure you are using a distinct name!
592
+
593
+ @lookup_listener("button/basicediting")
594
+ def set_editing_buttons(self, new_values, old_values):
595
+ self.set_panel_buttons("basicediting", new_values)
596
+
597
+ @lookup_listener("button/undo")
598
+ def set_undo_buttons(self, new_values, old_values):
599
+ self.set_panel_buttons("undo", new_values)
600
+
601
+ @lookup_listener("button/project")
602
+ def set_project_buttons(self, new_values, old_values):
603
+ self.set_panel_buttons("project", new_values)
604
+
605
+ @lookup_listener("button/modify")
606
+ def set_modify_buttons(self, new_values, old_values):
607
+ self.set_panel_buttons("modify", new_values)
608
+
609
+ @lookup_listener("button/geometry")
610
+ def set_geometry_buttons(self, new_values, old_values):
611
+ self.set_panel_buttons("geometry", new_values)
612
+
613
+ @lookup_listener("button/preparation")
614
+ def set_preparation_buttons(self, new_values, old_values):
615
+ self.set_panel_buttons("preparation", new_values)
616
+
617
+ @lookup_listener("button/jobstart")
618
+ def set_jobstart_buttons(self, new_values, old_values):
619
+ self.set_panel_buttons("jobstart", new_values)
620
+
621
+ @lookup_listener("button/control")
622
+ def set_control_buttons(self, new_values, old_values):
623
+ self.set_panel_buttons("control", new_values)
624
+
625
+ @lookup_listener("button/device")
626
+ def set_device_buttons(self, new_values, old_values):
627
+ self.set_panel_buttons("device", new_values)
628
+
629
+ @lookup_listener("button/config")
630
+ def set_config_buttons(self, new_values, old_values):
631
+ self.set_panel_buttons("config", new_values)
632
+
633
+ @lookup_listener("button/align")
634
+ def set_align_buttons(self, new_values, old_values):
635
+ self.set_panel_buttons("align", new_values)
636
+
637
+ @lookup_listener("button/select")
638
+ def set_select_buttons(self, new_values, old_values):
639
+ self.set_panel_buttons("select", new_values)
640
+
641
+ @lookup_listener("button/tools")
642
+ def set_tool_buttons(self, new_values, old_values):
643
+ self.set_panel_buttons("tool", new_values)
644
+
645
+ @lookup_listener("button/lasercontrol")
646
+ def set_lasercontrol_buttons(self, new_values, old_values):
647
+ self.set_panel_buttons("lasercontrol", new_values)
648
+
649
+ @lookup_listener("button/extended_tools")
650
+ def set_tool_extended_buttons(self, new_values, old_values):
651
+ self.set_panel_buttons("extended_tools", new_values)
652
+
653
+ @lookup_listener("button/group")
654
+ def set_group_buttons(self, new_values, old_values):
655
+ self.set_panel_buttons("group", new_values)
656
+
657
+ @lookup_listener("button/user")
658
+ def set_user_buttons(self, new_values, old_values):
659
+ self.set_panel_buttons("user", new_values)
660
+
661
+ @signal_listener("emphasized")
662
+ def on_emphasis_change(self, origin, *args):
663
+ # self.context.elements.set_start_time("Ribbon rule")
664
+ self.apply_enable_rules()
665
+ # self.context.elements.set_end_time("Ribbon rule")
666
+
667
+ @signal_listener("undoredo")
668
+ def on_undostate_change(self, origin, *args):
669
+ self.apply_enable_rules()
670
+
671
+ @signal_listener("selected")
672
+ @signal_listener("element_property_update")
673
+ def on_selected_change(self, origin, node=None, *args):
674
+ self.apply_enable_rules()
675
+
676
+ @signal_listener("icons")
677
+ def on_requested_change(self, origin, node=None, *args):
678
+ self.apply_enable_rules()
679
+ self.redrawn()
680
+
681
+ @signal_listener("tool_changed")
682
+ def on_tool_changed(self, origin, newtool=None, *args):
683
+ # Signal provides a tuple with (togglegroup, id)
684
+ if newtool is None:
685
+ return
686
+ if isinstance(newtool, (list, tuple)):
687
+ group = newtool[0].lower() if newtool[0] is not None else ""
688
+ identifier = newtool[1].lower() if newtool[1] is not None else ""
689
+ else:
690
+ group = newtool
691
+ identifier = ""
692
+
693
+ for page in self.pages:
694
+ for panel in page.panels:
695
+ for button in panel.buttons:
696
+ if button.group != group:
697
+ continue
698
+ button.set_button_toggle(button.identifier == identifier)
699
+ self.apply_enable_rules()
700
+ self.modified()
701
+
702
+ def __set_ribbonbar(self):
703
+ """
704
+ GUI Specific creation of ribbonbar.
705
+ @return:
706
+ """
707
+ for entry in sorted(self._pages, key=lambda d: d["seq"]):
708
+ name_id = entry["id"]
709
+ label = _(entry["label"])
710
+ panel_list = entry["panels"]
711
+ page = self.add_page(
712
+ name_id,
713
+ wx.ID_ANY,
714
+ label,
715
+ icons8_opened_folder.GetBitmap(resize=16),
716
+ )
717
+ entry["_object"] = page
718
+ for pentry in sorted(panel_list, key=lambda d: d["seq"]):
719
+ p_name_id = pentry["id"]
720
+ p_label = _(pentry["label"])
721
+ panel = self.add_panel(
722
+ p_name_id,
723
+ parent=page,
724
+ id=wx.ID_ANY,
725
+ label=p_label,
726
+ icon=icons8_opened_folder.GetBitmap(),
727
+ )
728
+ pentry["_object"] = panel
729
+
730
+ self.modified()
731
+
732
+ def pane_show(self):
733
+ pass
734
+
735
+ def pane_hide(self):
736
+ """
737
+ On pane_hide all the listeners are disabled.
738
+ @return:
739
+ """
740
+ for page in self.pages:
741
+ for panel in page.panels:
742
+ for key, listener in panel._registered_signals:
743
+ self.context.unlisten(key, listener)
744
+
745
+ @signal_listener("page")
746
+ def on_page_signal(self, origin, pagename=None, *args):
747
+ """
748
+ Page listener to force the given active page on the triggering of a page signal.
749
+ @param origin:
750
+ @param pagename:
751
+ @param args:
752
+ @return:
753
+ """
754
+ if pagename is None:
755
+ return
756
+ pagename = pagename.lower()
757
+ if pagename == "":
758
+ pagename = "home"
759
+ for p in self.pages:
760
+ # print (f"compare '{p.reference.lower()}' to '{pagename}'")
761
+ if p.reference.lower() == pagename:
762
+ self.art.current_page = p
763
+ self.modified()
764
+ if getattr(self.context.root, "_active_page", "") != pagename:
765
+ setattr(self.context.root, "_active_page", pagename)
766
+
767
+
768
+ class RibbonEditor(wx.Panel):
769
+ """
770
+ RibbonEditor is a panel that allows you to define the content
771
+ of the ribbonbar on top of Mks main window
772
+ """
773
+
774
+ def __init__(self, *args, context=None, **kwds):
775
+ # begin wxGlade: PassesPanel.__init__
776
+ kwds["style"] = kwds.get("style", 0)
777
+ wx.Panel.__init__(self, *args, **kwds)
778
+
779
+ self.context = context
780
+ self.context.themes.set_window_colors(self)
781
+ self.SetHelpText("ribboneditor")
782
+ self.ribbon_identifier = "primary"
783
+
784
+ self.available_options = []
785
+ self.available_labels = []
786
+ self._config = None
787
+ self._config_buttons = None
788
+ self.current_page = None
789
+ self.current_button = None
790
+
791
+ sizer_main = wx.BoxSizer(wx.HORIZONTAL)
792
+ sizer_ribbons = StaticBoxSizer(self, wx.ID_ANY, _("Ribbons"), wx.VERTICAL)
793
+
794
+ sizer_pages = StaticBoxSizer(self, wx.ID_ANY, _("Pages"), wx.VERTICAL)
795
+ sizer_panels = StaticBoxSizer(self, wx.ID_ANY, _("Panels"), wx.VERTICAL)
796
+ sizer_available_panels = StaticBoxSizer(
797
+ self, wx.ID_ANY, _("Available Panels"), wx.VERTICAL
798
+ )
799
+
800
+ choices = [v for v in self.context._ribbons]
801
+ self.combo_ribbons = wxComboBox(
802
+ self, wx.ID_ANY, choices=choices, style=wx.CB_DROPDOWN | wx.CB_READONLY
803
+ )
804
+ self.check_labels = wxCheckBox(self, wx.ID_ANY, _("Show the Ribbon Labels"))
805
+
806
+ sizer_ribbons.Add(self.combo_ribbons, 0, wx.EXPAND, 0)
807
+ sizer_ribbons.Add(self.check_labels, 0, wx.EXPAND, 0)
808
+ self.list_pages = wxListBox(self, wx.ID_ANY, style=wx.LB_SINGLE)
809
+
810
+ self.text_param_page = TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
811
+
812
+ self.list_panels = wxListBox(self, wx.ID_ANY, style=wx.LB_SINGLE)
813
+ bsize = dip_size(self, 30, 30)
814
+ self.button_add_page = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
815
+ self.button_del_page = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
816
+ self.button_up_page = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
817
+ self.button_down_page = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
818
+
819
+ testsize = dip_size(self, 20, 20)
820
+ iconsize = testsize[0]
821
+ # Circumvent a WXPython bug at high resolutions under Windows
822
+ bmp = icon_trash.GetBitmap(resize=iconsize, buffer=1)
823
+ self.button_del_page.SetBitmap(bmp)
824
+ testsize = self.button_del_page.GetBitmap().Size
825
+ if testsize[0] != iconsize:
826
+ iconsize = int(iconsize * iconsize / testsize[0])
827
+
828
+ self.button_add_page.SetBitmap(icon_add_new.GetBitmap(resize=iconsize))
829
+ self.button_del_page.SetBitmap(icon_trash.GetBitmap(resize=iconsize, buffer=1))
830
+ self.button_up_page.SetBitmap(icons8_up.GetBitmap(resize=iconsize, buffer=1))
831
+ self.button_down_page.SetBitmap(
832
+ icons8_down.GetBitmap(resize=iconsize, buffer=1)
833
+ )
834
+
835
+ self.button_del_panel = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
836
+ self.button_up_panel = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
837
+ self.button_down_panel = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
838
+
839
+ self.button_del_panel.SetBitmap(icon_trash.GetBitmap(resize=iconsize, buffer=1))
840
+ self.button_up_panel.SetBitmap(icons8_up.GetBitmap(resize=iconsize, buffer=1))
841
+ self.button_down_panel.SetBitmap(
842
+ icons8_down.GetBitmap(resize=iconsize, buffer=1)
843
+ )
844
+
845
+ self.list_options = wxListBox(self, wx.ID_ANY, style=wx.LB_SINGLE)
846
+ self.button_add_panel = wxButton(self, wx.ID_ANY, _("Add to page"))
847
+ self.button_apply = wxButton(self, wx.ID_ANY, _("Apply"))
848
+ self.button_reset = wxButton(self, wx.ID_ANY, _("Reset to Default"))
849
+
850
+ sizer_button = wx.BoxSizer(wx.VERTICAL)
851
+ sizer_button.Add(self.button_add_panel, 1, wx.EXPAND, 0)
852
+ sizer_button.Add(self.button_apply, 1, wx.EXPAND, 0)
853
+ sizer_button.Add(self.button_reset, 1, wx.EXPAND, 0)
854
+
855
+ sizer_available_panels.Add(self.list_options, 1, wx.EXPAND, 0)
856
+ sizer_available_panels.Add(sizer_button, 0, wx.EXPAND, 0)
857
+
858
+ hsizer_page = wx.BoxSizer(wx.HORIZONTAL)
859
+ hsizer_page.Add(self.text_param_page, 1, wx.EXPAND, 0)
860
+ hsizer_page.Add(self.button_add_page, 0, wx.EXPAND, 0)
861
+ hsizer_page.Add(self.button_del_page, 0, wx.EXPAND, 0)
862
+ hsizer_page.Add(self.button_up_page, 0, wx.EXPAND, 0)
863
+ hsizer_page.Add(self.button_down_page, 0, wx.EXPAND, 0)
864
+
865
+ sizer_pages.Add(self.list_pages, 1, wx.EXPAND, 0)
866
+ sizer_pages.Add(hsizer_page, 0, wx.EXPAND, 0)
867
+
868
+ hsizer_panel = wx.BoxSizer(wx.HORIZONTAL)
869
+ hsizer_panel.Add(self.button_del_panel, 0, wx.EXPAND, 0)
870
+ hsizer_panel.Add(self.button_up_panel, 0, wx.EXPAND, 0)
871
+ hsizer_panel.Add(self.button_down_panel, 0, wx.EXPAND, 0)
872
+ sizer_panels.Add(self.list_panels, 1, wx.EXPAND, 0)
873
+ sizer_panels.Add(hsizer_panel, 0, wx.EXPAND, 0)
874
+
875
+ sizer_active_config = wx.BoxSizer(wx.VERTICAL)
876
+ sizer_active_config.Add(sizer_ribbons, 0, wx.EXPAND, 0)
877
+ sizer_active_config.Add(sizer_pages, 1, wx.EXPAND, 0)
878
+ sizer_active_config.Add(sizer_panels, 1, wx.EXPAND, 0)
879
+
880
+ sizer_main.Add(sizer_active_config, 1, wx.EXPAND, 0)
881
+
882
+ sizer_buttons = StaticBoxSizer(
883
+ self, wx.ID_ANY, _("User-defined buttons"), wx.VERTICAL
884
+ )
885
+ self.list_buttons = wxListBox(self, wx.ID_ANY, style=wx.LB_SINGLE)
886
+ sizer_button_buttons = wx.BoxSizer(wx.HORIZONTAL)
887
+
888
+ self.button_add_button = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
889
+ self.button_del_button = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
890
+ self.button_edit_button = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
891
+ self.button_up_button = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
892
+ self.button_down_button = wxStaticBitmap(self, wx.ID_ANY, size=bsize)
893
+
894
+ self.button_add_button.SetBitmap(icon_add_new.GetBitmap(resize=iconsize))
895
+ self.button_del_button.SetBitmap(
896
+ icon_trash.GetBitmap(resize=iconsize, buffer=1)
897
+ )
898
+ self.button_edit_button.SetBitmap(
899
+ icon_edit.GetBitmap(resize=iconsize, buffer=1)
900
+ )
901
+ self.button_up_button.SetBitmap(icons8_up.GetBitmap(resize=iconsize, buffer=1))
902
+ self.button_down_button.SetBitmap(
903
+ icons8_down.GetBitmap(resize=iconsize, buffer=1)
904
+ )
905
+
906
+ sizer_button_buttons.Add(self.button_add_button, 0, wx.EXPAND, 0)
907
+ sizer_button_buttons.Add(self.button_del_button, 0, wx.EXPAND, 0)
908
+ sizer_button_buttons.Add(self.button_edit_button, 0, wx.EXPAND, 0)
909
+ sizer_button_buttons.Add(self.button_up_button, 0, wx.EXPAND, 0)
910
+ sizer_button_buttons.Add(self.button_down_button, 0, wx.EXPAND, 0)
911
+
912
+ sizer_buttons.Add(self.list_buttons, 1, wx.EXPAND, 0)
913
+ sizer_buttons.Add(sizer_button_buttons, 0, wx.EXPAND, 0)
914
+
915
+ sizer_right = wx.BoxSizer(wx.VERTICAL)
916
+ sizer_right.Add(sizer_available_panels, 1, wx.EXPAND, 0)
917
+ sizer_right.Add(sizer_buttons, 1, wx.EXPAND, 0)
918
+ sizer_main.Add(sizer_right, 1, wx.EXPAND, 0)
919
+
920
+ # Explanatory tooltips
921
+ self.button_add_panel.SetToolTip(
922
+ _("Add the selected panel to the selected page")
923
+ )
924
+ self.button_apply.SetToolTip(_("Apply the configuration"))
925
+ self.button_reset.SetToolTip(
926
+ _("Reset the ribbon appearance to the default configuration")
927
+ )
928
+
929
+ self.button_add_page.SetToolTip(_("Add an additional page to the ribbon"))
930
+ self.button_del_page.SetToolTip(_("Remove the selected page from the list"))
931
+ self.button_down_page.SetToolTip(
932
+ _("Decrease the position of the selected page")
933
+ )
934
+ self.button_up_page.SetToolTip(_("Increase the position of the selected page"))
935
+ self.text_param_page.SetToolTip(_("Modify the label of the selected page"))
936
+
937
+ self.button_del_panel.SetToolTip(_("Remove the selected panel from the list"))
938
+ self.button_down_panel.SetToolTip(
939
+ _("Decrease the position of the selected panel")
940
+ )
941
+ self.button_up_panel.SetToolTip(
942
+ _("Increase the position of the selected panel")
943
+ )
944
+ self.check_labels.SetToolTip(
945
+ _("Allow/suppress the display of labels beneath the icons in the ribbon")
946
+ )
947
+
948
+ self.button_add_button.SetToolTip(_("Add an additional user-defined button"))
949
+ self.button_del_button.SetToolTip(
950
+ _("Remove the selected user-defined button from the list")
951
+ )
952
+ self.button_down_button.SetToolTip(
953
+ _("Decrease the position of the selected button")
954
+ )
955
+ self.button_up_button.SetToolTip(
956
+ _("Increase the position of the selected button")
957
+ )
958
+ self.button_edit_button.SetToolTip(
959
+ _("Modify the content of the selected button")
960
+ )
961
+
962
+ self.fill_options()
963
+ self.SetSizer(sizer_main)
964
+ self.Layout()
965
+ self.list_options.Bind(wx.EVT_LISTBOX, self.on_list_options_click)
966
+ self.list_options.Bind(wx.EVT_LISTBOX_DCLICK, self.on_list_options_dclick)
967
+
968
+ self.button_add_panel.Bind(wx.EVT_BUTTON, self.on_button_add_panel_click)
969
+ self.button_apply.Bind(wx.EVT_BUTTON, self.on_button_apply_click)
970
+ self.button_reset.Bind(wx.EVT_BUTTON, self.on_button_reset_click)
971
+ self.combo_ribbons.Bind(wx.EVT_COMBOBOX, self.on_combo_ribbon)
972
+
973
+ self.list_pages.Bind(wx.EVT_LISTBOX, self.on_list_pages_click)
974
+ self.list_panels.Bind(wx.EVT_LISTBOX, self.on_list_panels_click)
975
+
976
+ self.list_buttons.Bind(wx.EVT_LISTBOX, self.on_list_buttons_click)
977
+ self.list_buttons.Bind(wx.EVT_LISTBOX_DCLICK, self.on_button_edit_button)
978
+
979
+ self.text_param_page.Bind(wx.EVT_TEXT, self.on_text_label)
980
+ self.text_param_page.Bind(wx.EVT_TEXT_ENTER, self.on_text_label_enter)
981
+
982
+ self.button_add_page.Bind(wx.EVT_LEFT_DOWN, self.on_button_add_page_click)
983
+ self.button_del_page.Bind(wx.EVT_LEFT_DOWN, self.on_button_page_delete)
984
+ self.button_up_page.Bind(wx.EVT_LEFT_DOWN, self.on_move_page_up)
985
+ self.button_down_page.Bind(wx.EVT_LEFT_DOWN, self.on_move_page_down)
986
+
987
+ self.button_del_panel.Bind(wx.EVT_LEFT_DOWN, self.on_button_panel_delete)
988
+ self.button_up_panel.Bind(wx.EVT_LEFT_DOWN, self.on_move_panel_up)
989
+ self.button_down_panel.Bind(wx.EVT_LEFT_DOWN, self.on_move_panel_down)
990
+
991
+ self.button_add_button.Bind(wx.EVT_LEFT_DOWN, self.on_button_add_button)
992
+ self.button_del_button.Bind(wx.EVT_LEFT_DOWN, self.on_button_button_delete)
993
+ self.button_up_button.Bind(wx.EVT_LEFT_DOWN, self.on_move_button_up)
994
+ self.button_down_button.Bind(wx.EVT_LEFT_DOWN, self.on_move_button_down)
995
+ self.button_edit_button.Bind(wx.EVT_LEFT_DOWN, self.on_button_edit_button)
996
+
997
+ self.combo_ribbons.SetSelection(0)
998
+ self.on_combo_ribbon(None)
999
+ self.fill_button()
1000
+
1001
+ # ---- Generic routines to access data
1002
+
1003
+ def current_ribbon(self):
1004
+ return (
1005
+ self.context._ribbons[self.ribbon_identifier]
1006
+ if hasattr(self.context, "_ribbons")
1007
+ and self.ribbon_identifier in self.context._ribbons
1008
+ else None
1009
+ )
1010
+
1011
+ def get_page(self, pageid=None):
1012
+ if pageid is None:
1013
+ pageid = self.current_page
1014
+ for p in self._config:
1015
+ if p["id"] == pageid:
1016
+ return p
1017
+ return None
1018
+
1019
+ def update_page(self, pageid, newlabel=None, newseq=None):
1020
+ for p in self._config:
1021
+ if p["id"] == pageid:
1022
+ if newlabel is not None:
1023
+ p["label"] = newlabel
1024
+ if newseq is not None:
1025
+ p["seq"] = newseq
1026
+
1027
+ # ---- Routines to fill listboxes
1028
+
1029
+ def fill_pages(self, reload=False, reposition=None):
1030
+ self.list_pages.Clear()
1031
+ if reload is None:
1032
+ reload = False
1033
+ if reload:
1034
+ rib = self.current_ribbon()
1035
+ if rib is None:
1036
+ return
1037
+ self._config, self._config_buttons = rib.get_current_config()
1038
+ pages = []
1039
+ idx = 0
1040
+ for p_idx, page_entry in enumerate(
1041
+ sorted(self._config, key=lambda d: d["seq"])
1042
+ ):
1043
+ pages.append(f"{page_entry['id']} ({page_entry['label']})")
1044
+ if reposition is not None and page_entry["id"] == reposition:
1045
+ idx = p_idx
1046
+ self.list_pages.SetItems(pages)
1047
+ if pages:
1048
+ self.list_pages.SetSelection(idx)
1049
+ self.on_list_pages_click(None)
1050
+
1051
+ def fill_button(self, reload=False, reposition=None):
1052
+ self.list_buttons.Clear()
1053
+ buttons = []
1054
+ idx = 0
1055
+ for b_idx, button_entry in enumerate(
1056
+ sorted(self._config_buttons, key=lambda d: d["seq"])
1057
+ ):
1058
+ buttons.append(
1059
+ f"{button_entry['id']} ({button_entry['label']}): {button_entry['action_left']}"
1060
+ )
1061
+ if reposition is not None and button_entry["id"] == reposition:
1062
+ idx = b_idx
1063
+ self.list_buttons.SetItems(buttons)
1064
+ if buttons:
1065
+ self.list_buttons.SetSelection(idx)
1066
+ self.on_list_buttons_click(None)
1067
+
1068
+ def fill_panels(self, pageid=None, reposition=None):
1069
+ if pageid is None:
1070
+ pageid = self.current_page
1071
+ self.list_panels.Clear()
1072
+ panels = []
1073
+ tidx = 0
1074
+ cidx = 0
1075
+ for page_entry in self._config:
1076
+ if pageid == page_entry["id"]:
1077
+ panel_list = page_entry["panels"]
1078
+ for panel_entry in sorted(panel_list, key=lambda d: d["seq"]):
1079
+ panels.append(f"{panel_entry['id']}")
1080
+ if reposition is not None and reposition == panel_entry["id"]:
1081
+ tidx = cidx
1082
+ cidx += 1
1083
+
1084
+ self.list_panels.SetItems(panels)
1085
+ if panels:
1086
+ self.list_panels.SetSelection(tidx)
1087
+
1088
+ def fill_options(self):
1089
+ # Query all registered button sections
1090
+ self.available_options.clear()
1091
+ self.available_labels.clear()
1092
+ for d_name in self.context.kernel.match("button", suffix=False):
1093
+ secs = d_name.split("/")
1094
+ # print(f"{d_name} {secs}")
1095
+ if len(secs) > 2:
1096
+ section = secs[1]
1097
+ button = secs[2]
1098
+ try:
1099
+ idx = self.available_options.index(section)
1100
+ self.available_labels[idx] += f", {button}"
1101
+ except ValueError:
1102
+ self.available_options.append(section)
1103
+ self.available_labels.append(button)
1104
+
1105
+ self.list_options.Clear()
1106
+ self.list_options.SetItems(self.available_options)
1107
+
1108
+ # ---- Event Handler Routines
1109
+
1110
+ def on_combo_ribbon(self, event):
1111
+ idx = self.combo_ribbons.GetSelection()
1112
+ if idx < 0:
1113
+ return
1114
+ rlist = list(self.context._ribbons.keys())
1115
+ if len(rlist) > 0:
1116
+ self.ribbon_identifier = rlist[idx]
1117
+ rib = self.current_ribbon()
1118
+ self.check_labels.SetValue(rib.art.show_labels)
1119
+ self.check_labels.Enable(rib.allow_labels)
1120
+ self.fill_pages(reload=True)
1121
+
1122
+ def on_move_page_up(self, event):
1123
+ newconfig = []
1124
+ for p_idx, page_entry in enumerate(
1125
+ sorted(self._config, key=lambda d: d["seq"])
1126
+ ):
1127
+ if page_entry["id"] == self.current_page and p_idx > 0:
1128
+ newconfig.insert(p_idx - 1, page_entry)
1129
+ else:
1130
+ newconfig.append(page_entry)
1131
+ for p_idx, page_entry in enumerate(newconfig):
1132
+ page_entry["seq"] = p_idx + 1
1133
+ self._config = newconfig
1134
+ self.fill_pages(reposition=self.current_page)
1135
+
1136
+ def on_move_page_down(self, event):
1137
+ newconfig = []
1138
+ for p_idx, page_entry in enumerate(
1139
+ sorted(self._config, key=lambda d: d["seq"], reverse=True)
1140
+ ):
1141
+ if page_entry["id"] == self.current_page and p_idx > 0:
1142
+ newconfig.insert(1, page_entry)
1143
+ else:
1144
+ newconfig.insert(0, page_entry)
1145
+ for p_idx, page_entry in enumerate(newconfig):
1146
+ page_entry["seq"] = p_idx + 1
1147
+ self._config = newconfig
1148
+ self.fill_pages(reposition=self.current_page)
1149
+
1150
+ def on_list_pages_click(self, event):
1151
+ idx = self.list_pages.GetSelection()
1152
+ flag1 = idx >= 0
1153
+ self.button_del_page.Enable(flag1)
1154
+ self.button_down_page.Enable(flag1)
1155
+ self.button_up_page.Enable(flag1)
1156
+ self.button_add_page.Enable(True)
1157
+ page_label = ""
1158
+ if idx >= 0:
1159
+ term = self.list_pages.GetStringSelection()
1160
+ tidx = term.find(" (")
1161
+ if tidx >= 0:
1162
+ page = term[:tidx]
1163
+ self.current_page = page
1164
+ self.fill_panels()
1165
+ p = self.get_page(page)
1166
+ if p is not None:
1167
+ page_label = p["label"]
1168
+ self.text_param_page.SetValue(page_label)
1169
+
1170
+ def on_button_page_delete(self, event):
1171
+ new_config = []
1172
+ new_config.extend(p for p in self._config if p["id"] != self.current_page)
1173
+ if len(new_config) != self._config:
1174
+ self._config = new_config
1175
+ self.current_page = None
1176
+ self.fill_pages()
1177
+
1178
+ def on_button_add_page_click(self, event):
1179
+ newid = f"page_{len(self._config) + 1}"
1180
+ newentry = {
1181
+ "id": newid,
1182
+ "label": f"Page {len(self._config) + 1}",
1183
+ "seq": len(self._config) + 1,
1184
+ "panels": [],
1185
+ }
1186
+ self._config.append(newentry)
1187
+ self.fill_pages(reposition=newid)
1188
+
1189
+ def on_move_panel_up(self, event):
1190
+ idx = self.list_panels.GetSelection()
1191
+ if idx < 0:
1192
+ return
1193
+ panelid = self.list_panels.GetString(idx)
1194
+ for page in self._config:
1195
+ if page["id"] != self.current_page:
1196
+ continue
1197
+ panel_list = page["panels"]
1198
+ newconfig = []
1199
+ for p_idx, panel_entry in enumerate(
1200
+ sorted(panel_list, key=lambda d: d["seq"])
1201
+ ):
1202
+ if panel_entry["id"] == panelid and p_idx > 0:
1203
+ newconfig.insert(p_idx - 1, panel_entry)
1204
+ else:
1205
+ newconfig.append(panel_entry)
1206
+ for p_idx, panel_entry in enumerate(newconfig):
1207
+ panel_entry["seq"] = p_idx + 1
1208
+ page["panels"] = newconfig
1209
+ self.fill_panels(reposition=panelid)
1210
+
1211
+ def on_move_panel_down(self, event):
1212
+ idx = self.list_panels.GetSelection()
1213
+ if idx < 0:
1214
+ return
1215
+ panelid = self.list_panels.GetString(idx)
1216
+ for page in self._config:
1217
+ if page["id"] != self.current_page:
1218
+ continue
1219
+ panelid = self.list_panels.GetString(idx)
1220
+ panel_list = page["panels"]
1221
+ newconfig = []
1222
+ for p_idx, panel_entry in enumerate(
1223
+ sorted(panel_list, key=lambda d: d["seq"], reverse=True)
1224
+ ):
1225
+ if panel_entry["id"] == panelid and p_idx > 0:
1226
+ newconfig.insert(1, panel_entry)
1227
+ else:
1228
+ newconfig.insert(0, panel_entry)
1229
+ for p_idx, panel_entry in enumerate(newconfig):
1230
+ panel_entry["seq"] = p_idx + 1
1231
+ page["panels"] = newconfig
1232
+ self.fill_panels(reposition=panelid)
1233
+
1234
+ def on_button_panel_delete(self, event):
1235
+ idx = self.list_panels.GetSelection()
1236
+ if idx < 0:
1237
+ return
1238
+ panelid = self.list_panels.GetString(idx)
1239
+ for page in self._config:
1240
+ if page["id"] != self.current_page:
1241
+ continue
1242
+ panel_list = page["panels"]
1243
+ new_config = []
1244
+ seqnum = 1
1245
+ for panel in sorted(panel_list, key=lambda d: d["seq"]):
1246
+ if panel["id"] != panelid:
1247
+ panel["seq"] = seqnum
1248
+ new_config.append(panel)
1249
+ seqnum += 1
1250
+ page["panels"] = new_config
1251
+ self.fill_panels()
1252
+
1253
+ def on_list_panels_click(self, event):
1254
+ idx = self.list_panels.GetSelection()
1255
+ flag1 = idx >= 0
1256
+ self.button_del_panel.Enable(flag1)
1257
+ self.button_down_panel.Enable(flag1)
1258
+ self.button_up_panel.Enable(flag1)
1259
+ if idx < 0:
1260
+ return
1261
+
1262
+ def on_text_label(self, event):
1263
+ label = self.text_param_page.GetValue()
1264
+ self.update_page(self.current_page, newlabel=label)
1265
+
1266
+ def on_text_label_enter(self, event):
1267
+ self.fill_pages(reposition=self.current_page)
1268
+
1269
+ def on_list_options_click(self, event):
1270
+ ttip = ""
1271
+ idx = self.list_options.GetSelection()
1272
+ if idx >= 0:
1273
+ self.button_add_panel.Enable(True)
1274
+ ttip = f"{self.available_options[idx]}: {self.available_labels[idx]}"
1275
+ else:
1276
+ self.button_add_panel.Enable(False)
1277
+ self.list_options.SetToolTip(ttip)
1278
+
1279
+ def on_list_options_dclick(self, event):
1280
+ self.on_button_add_panel_click(event)
1281
+
1282
+ def on_button_add_panel_click(self, event):
1283
+ idx = self.list_options.GetSelection()
1284
+ if idx < 0:
1285
+ return
1286
+ panel = self.available_options[idx]
1287
+ for page in self._config:
1288
+ if page["id"] != self.current_page:
1289
+ continue
1290
+ if panel not in page["panels"]:
1291
+ newpanel = {
1292
+ "id": panel,
1293
+ "label": panel,
1294
+ "seq": len(page["panels"]) + 2,
1295
+ }
1296
+ page["panels"].append(newpanel)
1297
+ self.fill_pages(reposition=self.current_page)
1298
+
1299
+ def on_button_apply_click(self, event):
1300
+ ob = self.current_ribbon()
1301
+ if ob is None:
1302
+ return
1303
+ if len(self._config) == 0:
1304
+ return
1305
+ ob.storage.delete_all_persistent()
1306
+ ob.storage.write_persistent("Ribbon", "identifier", self.ribbon_identifier)
1307
+ show_labels = self.check_labels.GetValue()
1308
+ ob.storage.write_persistent("Ribbon", "show_labels", show_labels)
1309
+
1310
+ for p_idx, page_entry in enumerate(
1311
+ sorted(self._config, key=lambda d: d["seq"])
1312
+ ):
1313
+ section = f"Page_{p_idx + 1}"
1314
+ ob.storage.write_persistent(section, "id", page_entry["id"])
1315
+ ob.storage.write_persistent(section, "label", page_entry["label"])
1316
+ ob.storage.write_persistent(section, "seq", page_entry["seq"])
1317
+ panel_list = page_entry["panels"]
1318
+ for panel_idx, pentry in enumerate(
1319
+ sorted(panel_list, key=lambda d: d["seq"])
1320
+ ):
1321
+ key = f"Panel_{panel_idx + 1}"
1322
+ result = (
1323
+ pentry["id"],
1324
+ pentry["seq"],
1325
+ pentry["label"],
1326
+ )
1327
+ ob.storage.write_persistent(section, key, result)
1328
+ # Write all user-defined buttons
1329
+ for idx, entry in enumerate(
1330
+ sorted(self._config_buttons, key=lambda d: d["seq"])
1331
+ ):
1332
+ section = f"Button_{idx + 1}"
1333
+ ob.storage.write_persistent(section, "id", entry["id"])
1334
+ ob.storage.write_persistent(section, "label", entry["label"])
1335
+ ob.storage.write_persistent(section, "tip", entry["tip"])
1336
+ ob.storage.write_persistent(section, "action_left", entry["action_left"])
1337
+ ob.storage.write_persistent(section, "action_right", entry["action_right"])
1338
+ ob.storage.write_persistent(section, "enable", entry["enable"])
1339
+ ob.storage.write_persistent(section, "visible", entry["visible"])
1340
+ ob.storage.write_persistent(section, "icon", entry["icon"])
1341
+ ob.storage.write_persistent(section, "seq", entry["seq"])
1342
+
1343
+ ob.storage.write_configuration()
1344
+ ob.restart()
1345
+ # self.context.signal("ribbon_recreate", self.ribbon_identifier)
1346
+
1347
+ def on_button_reset_click(self, event):
1348
+ rib = self.current_ribbon()
1349
+ if rib is None:
1350
+ return
1351
+ self._config, self._config_buttons = rib.get_default_config()
1352
+ self.fill_pages()
1353
+ self.fill_button()
1354
+
1355
+ def on_list_buttons_click(self, event):
1356
+ idx = self.list_buttons.GetSelection()
1357
+ self.current_button = None if idx < 0 else self._config_buttons[idx]["id"]
1358
+ flag1 = idx >= 0
1359
+ self.button_del_button.Enable(flag1)
1360
+ self.button_down_button.Enable(flag1)
1361
+ self.button_up_button.Enable(flag1)
1362
+ self.button_edit_button.Enable(flag1)
1363
+ if idx < 0:
1364
+ return
1365
+
1366
+ def on_move_button_up(self, event):
1367
+ newconfig = []
1368
+ for idx, entry in enumerate(
1369
+ sorted(self._config_buttons, key=lambda d: d["seq"])
1370
+ ):
1371
+ if entry["id"] == self.current_button and idx > 0:
1372
+ newconfig.insert(idx - 1, entry)
1373
+ else:
1374
+ newconfig.append(entry)
1375
+ for idx, entry in enumerate(newconfig):
1376
+ entry["seq"] = idx + 1
1377
+ self._config_buttons = newconfig
1378
+ self.fill_button(reposition=self.current_button)
1379
+
1380
+ def on_move_button_down(self, event):
1381
+ newconfig = []
1382
+ for idx, entry in enumerate(
1383
+ sorted(self._config_buttons, key=lambda d: d["seq"], reverse=True)
1384
+ ):
1385
+ if entry["id"] == self.current_button and idx > 0:
1386
+ newconfig.insert(1, entry)
1387
+ else:
1388
+ newconfig.insert(0, entry)
1389
+ for idx, entry in enumerate(newconfig):
1390
+ entry["seq"] = idx + 1
1391
+ self._config_buttons = newconfig
1392
+ self.fill_button(reposition=self.current_button)
1393
+
1394
+ def on_button_button_delete(self, event):
1395
+ new_config = []
1396
+ new_config.extend(
1397
+ p for p in self._config_buttons if p["id"] != self.current_button
1398
+ )
1399
+ if len(new_config) != self._config_buttons:
1400
+ self._config_buttons = new_config
1401
+ self.current_button = None
1402
+ self.fill_button()
1403
+ self.fill_panels()
1404
+
1405
+ def on_button_add_button(self, event):
1406
+ button_idx = len(self._config_buttons) + 1
1407
+ newid = f"user_button_{button_idx}"
1408
+ newentry = {
1409
+ "id": newid,
1410
+ "label": f"Button {button_idx}",
1411
+ "enable": "always",
1412
+ "visible": "always",
1413
+ "action_left": "echo Enter your command here",
1414
+ "action_right": "",
1415
+ "icon": "icon_edit",
1416
+ "tip": _("This could be your tooltip"),
1417
+ "seq": button_idx,
1418
+ }
1419
+ self._config_buttons.append(newentry)
1420
+ self.current_button = newid
1421
+ self.fill_button(reposition=newid)
1422
+ self.fill_panels()
1423
+ # Edit it immediately...
1424
+ self.on_button_edit_button(None)
1425
+
1426
+ def edit_a_button(self, entry):
1427
+ # We edit the thing in place if needed...
1428
+ dlg = wx.Dialog(
1429
+ self,
1430
+ wx.ID_ANY,
1431
+ _("Edit Button"),
1432
+ style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
1433
+ )
1434
+ self.context.themes.set_window_colors(dlg)
1435
+ sizer = wx.BoxSizer(wx.VERTICAL)
1436
+
1437
+ line1 = wx.BoxSizer(wx.HORIZONTAL)
1438
+ label1 = wxStaticText(dlg, wx.ID_ANY, _("Label"))
1439
+ txt_label = TextCtrl(dlg, wx.ID_ANY, value=entry["label"])
1440
+ line1.Add(label1, 0, wx.EXPAND, 0)
1441
+ line1.Add(txt_label, 1, wx.EXPAND, 0)
1442
+
1443
+ line2 = wx.BoxSizer(wx.HORIZONTAL)
1444
+ label2 = wxStaticText(dlg, wx.ID_ANY, _("Tooltip"))
1445
+ txt_tip = TextCtrl(dlg, wx.ID_ANY, value=entry["tip"])
1446
+ line2.Add(label2, 0, wx.EXPAND, 0)
1447
+ line2.Add(txt_tip, 1, wx.EXPAND, 0)
1448
+
1449
+ line3 = wx.BoxSizer(wx.HORIZONTAL)
1450
+ label3 = wxStaticText(dlg, wx.ID_ANY, _("Action left click"))
1451
+ txt_action_left = TextCtrl(dlg, wx.ID_ANY, value=entry["action_left"])
1452
+ line3.Add(label3, 0, wx.EXPAND, 0)
1453
+ line3.Add(txt_action_left, 1, wx.EXPAND, 0)
1454
+
1455
+ line4 = wx.BoxSizer(wx.HORIZONTAL)
1456
+ label4 = wxStaticText(dlg, wx.ID_ANY, _("Action right click"))
1457
+ txt_action_right = TextCtrl(dlg, wx.ID_ANY, value=entry["action_right"])
1458
+ line4.Add(label4, 0, wx.EXPAND, 0)
1459
+ line4.Add(txt_action_right, 1, wx.EXPAND, 0)
1460
+
1461
+ rule_options = (
1462
+ _("Always"),
1463
+ _("When at least one element selected"),
1464
+ _("When at least two elemente selected"),
1465
+ )
1466
+ line5 = wx.BoxSizer(wx.HORIZONTAL)
1467
+ label5 = wxStaticText(dlg, wx.ID_ANY, _("Rule to enable"))
1468
+ combo_enable = wxComboBox(
1469
+ dlg, wx.ID_ANY, choices=rule_options, style=wx.CB_DROPDOWN | wx.CB_READONLY
1470
+ )
1471
+ if entry["enable"] == "selected2":
1472
+ idx = 2
1473
+ elif entry["enable"] == "selected":
1474
+ idx = 1
1475
+ else:
1476
+ idx = 0
1477
+ combo_enable.SetSelection(idx)
1478
+ line5.Add(label5, 0, wx.EXPAND, 0)
1479
+ line5.Add(combo_enable, 1, wx.EXPAND, 0)
1480
+
1481
+ line6 = wx.BoxSizer(wx.HORIZONTAL)
1482
+ label6 = wxStaticText(dlg, wx.ID_ANY, _("Rule to display"))
1483
+ combo_visible = wxComboBox(
1484
+ dlg, wx.ID_ANY, choices=rule_options, style=wx.CB_DROPDOWN | wx.CB_READONLY
1485
+ )
1486
+ if entry["visible"] == "selected2":
1487
+ idx = 2
1488
+ elif entry["visible"] == "selected":
1489
+ idx = 1
1490
+ else:
1491
+ idx = 0
1492
+ combo_visible.SetSelection(idx)
1493
+ line6.Add(label6, 0, wx.EXPAND, 0)
1494
+ line6.Add(combo_visible, 1, wx.EXPAND, 0)
1495
+
1496
+ import meerk40t.gui.icons as mkicons
1497
+
1498
+ icon_list = []
1499
+ for icon in dir(mkicons):
1500
+ # print (entry)
1501
+ if icon.startswith("icon"):
1502
+ s = getattr(mkicons, icon)
1503
+ if isinstance(s, (mkicons.VectorIcon, mkicons.PyEmbeddedImage)):
1504
+ icon_list.append(icon)
1505
+
1506
+ line7 = wx.BoxSizer(wx.HORIZONTAL)
1507
+ label7 = wxStaticText(dlg, wx.ID_ANY, _("Icon"))
1508
+ combo_icon = wxComboBox(
1509
+ dlg, wx.ID_ANY, choices=icon_list, style=wx.CB_DROPDOWN | wx.CB_READONLY
1510
+ )
1511
+ preview = wxStaticBitmap(dlg, wx.ID_ANY, size=dip_size(self, 30, 30))
1512
+ if entry["icon"] in icon_list:
1513
+ combo_icon.SetValue(entry["icon"])
1514
+ line7.Add(label7, 0, wx.EXPAND, 0)
1515
+ line7.Add(combo_icon, 1, wx.EXPAND, 0)
1516
+
1517
+ line0 = wx.BoxSizer(wx.HORIZONTAL)
1518
+ line0.AddStretchSpacer(1)
1519
+ line0.Add(preview, 0, wx.EXPAND, 0)
1520
+
1521
+ def on_icon(event):
1522
+ icon_name = combo_icon.GetValue()
1523
+ ps = preview.GetSize()
1524
+ if icon_name:
1525
+ icon = getattr(mkicons, icon_name, None)
1526
+ if icon:
1527
+ icon_edit.GetBitmap
1528
+ preview.SetBitmap(icon.GetBitmap(resize=ps[0]))
1529
+
1530
+ combo_icon.Bind(wx.EVT_COMBOBOX, on_icon)
1531
+ on_icon(None)
1532
+
1533
+ button_sizer = dlg.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL)
1534
+ sizer.Add(line0, 0, wx.EXPAND, 0)
1535
+ sizer.Add(line1, 0, wx.EXPAND, 0)
1536
+ sizer.Add(line2, 0, wx.EXPAND, 0)
1537
+ sizer.Add(line3, 0, wx.EXPAND, 0)
1538
+ sizer.Add(line4, 0, wx.EXPAND, 0)
1539
+ sizer.Add(line5, 0, wx.EXPAND, 0)
1540
+ sizer.Add(line6, 0, wx.EXPAND, 0)
1541
+ sizer.Add(line7, 0, wx.EXPAND, 0)
1542
+ sizer.Add(button_sizer, 1, wx.EXPAND, 0)
1543
+
1544
+ xmax = 0
1545
+ ymax = 0
1546
+ labels = (label1, label2, label3, label4, label5, label6, label7)
1547
+ for ctrl in labels:
1548
+ ls = ctrl.GetSize()
1549
+ if ls[0] > xmax:
1550
+ xmax = ls[0]
1551
+ if ls[1] > ymax:
1552
+ ymax = ls[1]
1553
+ for ctrl in labels:
1554
+ ctrl.SetMinSize(wx.Size(xmax, ymax))
1555
+ for s in (line1, line2, line3, line4, line5, line6, line7):
1556
+ s.Layout()
1557
+
1558
+ dlg.SetSizer(sizer)
1559
+ dlg.Layout()
1560
+ dlg.CenterOnParent()
1561
+ res = dlg.ShowModal()
1562
+ if res == wx.ID_OK:
1563
+ entry["label"] = txt_label.GetValue()
1564
+ entry["tip"] = txt_tip.GetValue()
1565
+ entry["action_left"] = txt_action_left.GetValue()
1566
+ entry["action_right"] = txt_action_right.GetValue()
1567
+ ruler = ("always", "selected", "selected2")
1568
+ idx = combo_enable.GetSelection()
1569
+ idx = min(2, max(0, idx)) # between 0 and 2
1570
+ entry["enable"] = ruler[idx]
1571
+ idx = combo_visible.GetSelection()
1572
+ idx = min(2, max(0, idx)) # between 0 and 2
1573
+ entry["visible"] = ruler[idx]
1574
+ entry["icon"] = combo_icon.GetValue()
1575
+
1576
+ dlg.Destroy()
1577
+
1578
+ def on_button_edit_button(self, event):
1579
+ if self.current_button is None:
1580
+ return
1581
+ for entry in self._config_buttons:
1582
+ if entry["id"] == self.current_button:
1583
+ self.edit_a_button(entry)
1584
+ break
1585
+ self.fill_button(reposition=self.current_button)
1586
+
1587
+ def pane_hide(self):
1588
+ pass
1589
+
1590
+ def pane_show(self):
1591
+ pass