Browse Source

Merge remote-tracking branch 'um/3.2'

tags/v3.2.1
Timofey Kostennov 1 year ago
parent
commit
e97d8659cf
379 changed files with 22118 additions and 3319 deletions
  1. 36
    0
      .github/ISSUE_TEMPLATE.md
  2. 12
    8
      .gitignore
  3. 0
    173
      CHANGES
  4. 35
    33
      Jenkinsfile
  5. 18
    33
      README.md
  6. 13
    6
      cmake/CuraTests.cmake
  7. 19
    8
      cura/Arranging/Arrange.py
  8. 154
    0
      cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py
  9. 3
    4
      cura/Arranging/ArrangeObjectsJob.py
  10. 8
    5
      cura/Arranging/ShapeArray.py
  11. 0
    0
      cura/Arranging/__init__.py
  12. 53
    0
      cura/BuildPlateModel.py
  13. 17
    6
      cura/BuildVolume.py
  14. 14
    11
      cura/CrashHandler.py
  15. 43
    3
      cura/CuraActions.py
  16. 238
    150
      cura/CuraApplication.py
  17. 5
    18
      cura/MachineAction.py
  18. 6
    11
      cura/MultiplyObjectsJob.py
  19. 63
    0
      cura/ObjectsModel.py
  20. 2
    1
      cura/OneAtATimeIterator.py
  21. 0
    0
      cura/Operations/PlatformPhysicsOperation.py
  22. 29
    0
      cura/Operations/SetBuildPlateNumberOperation.py
  23. 0
    0
      cura/Operations/SetParentOperation.py
  24. 0
    0
      cura/Operations/__init__.py
  25. 34
    17
      cura/PlatformPhysics.py
  26. 104
    44
      cura/PrintInformation.py
  27. 13
    41
      cura/PrinterOutputDevice.py
  28. 55
    47
      cura/QualityManager.py
  29. 0
    0
      cura/Scene/BlockSlicingDecorator.py
  30. 26
    0
      cura/Scene/BuildPlateDecorator.py
  31. 1
    1
      cura/Scene/ConvexHullDecorator.py
  32. 1
    2
      cura/Scene/ConvexHullNode.py
  33. 113
    0
      cura/Scene/CuraSceneController.py
  34. 43
    0
      cura/Scene/CuraSceneNode.py
  35. 0
    0
      cura/Scene/GCodeListDecorator.py
  36. 0
    0
      cura/Scene/SliceableObjectDecorator.py
  37. 0
    0
      cura/Scene/ZOffsetDecorator.py
  38. 0
    0
      cura/Scene/__init__.py
  39. 136
    116
      cura/Settings/ContainerManager.py
  40. 216
    44
      cura/Settings/CuraContainerRegistry.py
  41. 17
    11
      cura/Settings/CuraContainerStack.py
  42. 24
    22
      cura/Settings/CuraStackBuilder.py
  43. 99
    4
      cura/Settings/ExtruderManager.py
  44. 54
    0
      cura/Settings/ExtruderStack.py
  45. 8
    32
      cura/Settings/ExtrudersModel.py
  46. 15
    14
      cura/Settings/GlobalStack.py
  47. 143
    46
      cura/Settings/MachineManager.py
  48. 15
    1
      cura/Settings/MaterialsModel.py
  49. 15
    11
      cura/Settings/ProfilesModel.py
  50. 14
    4
      cura/Settings/QualityAndUserProfilesModel.py
  51. 68
    60
      cura/Settings/QualitySettingsModel.py
  52. 21
    2
      cura/Settings/SettingOverrideDecorator.py
  53. 45
    4
      cura/Settings/UserProfilesModel.py
  54. 22
    0
      cura/Stages/CuraStage.py
  55. 2
    0
      cura/Stages/__init__.py
  56. 44
    28
      cura_app.py
  57. 1
    5
      installer.nsi
  58. 15
    6
      plugins/3MFReader/ThreeMFReader.py
  59. 119
    80
      plugins/3MFReader/ThreeMFWorkspaceReader.py
  60. 3
    11
      plugins/3MFReader/WorkspaceDialog.py
  61. 7
    0
      plugins/3MFReader/WorkspaceDialog.qml
  62. 4
    2
      plugins/3MFWriter/ThreeMFWorkspaceWriter.py
  63. 12
    3
      plugins/3MFWriter/ThreeMFWriter.py
  64. 3
    8
      plugins/ChangeLogPlugin/ChangeLog.py
  65. 163
    1
      plugins/ChangeLogPlugin/ChangeLog.txt
  66. 154
    59
      plugins/CuraEngineBackend/CuraEngineBackend.py
  67. 3
    1
      plugins/CuraEngineBackend/ProcessGCodeJob.py
  68. 12
    13
      plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
  69. 128
    51
      plugins/CuraEngineBackend/StartSliceJob.py
  70. 0
    1
      plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py
  71. 2
    0
      plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py
  72. 471
    0
      plugins/GCodeReader/FlavorParser.py
  73. 35
    87
      plugins/GCodeReader/GCodeReader.py
  74. 10
    0
      plugins/GCodeReader/MarlinFlavorParser.py
  75. 32
    0
      plugins/GCodeReader/RepRapFlavorParser.py
  76. 14
    9
      plugins/GCodeWriter/GCodeWriter.py
  77. 2
    1
      plugins/ImageReader/ImageReader.py
  78. 3
    9
      plugins/ImageReader/ImageReaderUI.py
  79. 27
    8
      plugins/LegacyProfileReader/LegacyProfileReader.py
  80. 13
    146
      plugins/MachineSettingsAction/MachineSettingsAction.py
  81. 48
    41
      plugins/MachineSettingsAction/MachineSettingsAction.qml
  82. 45
    0
      plugins/MonitorStage/MonitorMainView.qml
  83. 73
    0
      plugins/MonitorStage/MonitorStage.py
  84. 20
    0
      plugins/MonitorStage/__init__.py
  85. 8
    0
      plugins/MonitorStage/plugin.json
  86. 11
    0
      plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py
  87. 150
    15
      plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml
  88. 35
    1
      plugins/PerObjectSettingsTool/PerObjectSettingsTool.py
  89. 2
    14
      plugins/PluginBrowser/PluginBrowser.py
  90. 206
    0
      plugins/PostProcessingPlugin/PostProcessingPlugin.py
  91. 501
    0
      plugins/PostProcessingPlugin/PostProcessingPlugin.qml
  92. 2
    0
      plugins/PostProcessingPlugin/README.md
  93. 111
    0
      plugins/PostProcessingPlugin/Script.py
  94. 11
    0
      plugins/PostProcessingPlugin/__init__.py
  95. 8
    0
      plugins/PostProcessingPlugin/plugin.json
  96. 47
    0
      plugins/PostProcessingPlugin/postprocessing.svg
  97. 48
    0
      plugins/PostProcessingPlugin/scripts/BQ_PauseAtHeight.py
  98. 76
    0
      plugins/PostProcessingPlugin/scripts/ColorChange.py
  99. 43
    0
      plugins/PostProcessingPlugin/scripts/ExampleScript.py
  100. 221
    0
      plugins/PostProcessingPlugin/scripts/PauseAtHeight.py
  101. 169
    0
      plugins/PostProcessingPlugin/scripts/PauseAtHeightforRepetier.py
  102. 56
    0
      plugins/PostProcessingPlugin/scripts/SearchAndReplace.py
  103. 469
    0
      plugins/PostProcessingPlugin/scripts/Stretch.py
  104. 495
    0
      plugins/PostProcessingPlugin/scripts/TweakAtZ.py
  105. 18
    0
      plugins/PrepareStage/PrepareStage.py
  106. 20
    0
      plugins/PrepareStage/__init__.py
  107. 8
    0
      plugins/PrepareStage/plugin.json
  108. 3
    2
      plugins/SimulationView/SimulationPass.py
  109. 35
    8
      plugins/SimulationView/SimulationView.py
  110. 10
    12
      plugins/SimulationView/SimulationView.qml
  111. 2
    2
      plugins/SimulationView/SimulationViewProxy.py
  112. 2
    2
      plugins/SimulationView/__init__.py
  113. 24
    13
      plugins/SimulationView/layers3d.shader
  114. 17
    10
      plugins/SimulationView/layers3d_shadow.shader
  115. 44
    40
      plugins/SolidView/SolidView.py
  116. 7
    1
      plugins/UM3NetworkPrinting/ClusterMonitorItem.qml
  117. 10
    15
      plugins/UM3NetworkPrinting/DiscoverUM3Action.py
  118. 3
    3
      plugins/UM3NetworkPrinting/DiscoverUM3Action.qml
  119. 42
    29
      plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py
  120. 10
    8
      plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py
  121. 4
    0
      plugins/UM3NetworkPrinting/NetworkPrinterOutputDevicePlugin.py
  122. 1
    1
      plugins/UM3NetworkPrinting/PrinterInfoBlock.qml
  123. 40
    2
      plugins/USBPrinting/USBPrinterOutputDevice.py
  124. 4
    9
      plugins/USBPrinting/USBPrinterOutputDeviceManager.py
  125. 6
    11
      plugins/UserAgreementPlugin/UserAgreement.py
  126. 152
    152
      plugins/X3DReader/X3DReader.py
  127. 22
    0
      plugins/XRayView/XRayView.py
  128. 384
    164
      plugins/XmlMaterialProfile/XmlMaterialProfile.py
  129. 12
    0
      plugins/XmlMaterialProfile/product_to_id.json
  130. 0
    1
      resources/definitions/101Hero.def.json
  131. 2
    3
      resources/definitions/3dator.def.json
  132. 0
    1
      resources/definitions/abax_pri3.def.json
  133. 0
    1
      resources/definitions/abax_pri5.def.json
  134. 0
    1
      resources/definitions/abax_titan.def.json
  135. 2
    3
      resources/definitions/alya3dp.def.json
  136. 69
    0
      resources/definitions/anycubic_i3_mega.def.json
  137. 0
    1
      resources/definitions/bfb.def.json
  138. 0
    1
      resources/definitions/bq_hephestos.def.json
  139. 0
    1
      resources/definitions/bq_hephestos_2.def.json
  140. 0
    1
      resources/definitions/bq_hephestos_xl.def.json
  141. 0
    1
      resources/definitions/bq_witbox.def.json
  142. 0
    1
      resources/definitions/bq_witbox_2.def.json
  143. 0
    1
      resources/definitions/builder_premium_large.def.json
  144. 0
    1
      resources/definitions/builder_premium_medium.def.json
  145. 0
    1
      resources/definitions/builder_premium_small.def.json
  146. 0
    1
      resources/definitions/cartesio.def.json
  147. 8
    1
      resources/definitions/creality_cr10.def.json
  148. 0
    1
      resources/definitions/creality_cr10s4.def.json
  149. 0
    1
      resources/definitions/creality_cr10s5.def.json
  150. 0
    1
      resources/definitions/custom.def.json
  151. 11
    13
      resources/definitions/dagoma_discoeasy200.def.json
  152. 0
    1
      resources/definitions/delta_go.def.json
  153. 0
    1
      resources/definitions/deltabot.def.json
  154. 0
    1
      resources/definitions/deltacomb.def.json
  155. 1
    2
      resources/definitions/easyarts_ares.def.json
  156. 0
    1
      resources/definitions/fabtotum.def.json
  157. 0
    1
      resources/definitions/fdmextruder.def.json
  158. 323
    148
      resources/definitions/fdmprinter.def.json
  159. 0
    1
      resources/definitions/grr_neo.def.json
  160. 0
    1
      resources/definitions/helloBEEprusa.def.json
  161. 0
    1
      resources/definitions/imade3d_jellybox.def.json
  162. 1
    2
      resources/definitions/innovo_inventor.def.json
  163. 0
    1
      resources/definitions/julia.def.json
  164. 0
    1
      resources/definitions/kemiq_q2_beta.def.json
  165. 0
    1
      resources/definitions/kemiq_q2_gama.def.json
  166. 0
    1
      resources/definitions/kossel_mini.def.json
  167. 0
    1
      resources/definitions/kossel_pro.def.json
  168. 0
    1
      resources/definitions/kupido.def.json
  169. 0
    1
      resources/definitions/makeR_pegasus.def.json
  170. 0
    1
      resources/definitions/makeR_prusa_tairona_i3.def.json
  171. 0
    1
      resources/definitions/makeit_pro_l.def.json
  172. 0
    1
      resources/definitions/makeit_pro_m.def.json
  173. 0
    1
      resources/definitions/maker_starter.def.json
  174. 0
    1
      resources/definitions/makerbotreplicator.def.json
  175. 1
    1
      resources/definitions/malyan_m180.def.json
  176. 85
    0
      resources/definitions/malyan_m200.def.json
  177. 0
    1
      resources/definitions/mankati_fullscale_xt_plus.def.json
  178. 0
    1
      resources/definitions/mendel90.def.json
  179. 18
    0
      resources/definitions/monoprice_select_mini_v1.def.json
  180. 25
    0
      resources/definitions/monoprice_select_mini_v2.def.json
  181. 0
    1
      resources/definitions/ord.def.json
  182. 0
    1
      resources/definitions/peopoly_moai.def.json
  183. 0
    1
      resources/definitions/printrbot_play.def.json
  184. 0
    1
      resources/definitions/printrbot_play_heated.def.json
  185. 0
    1
      resources/definitions/printrbot_simple.def.json
  186. 0
    1
      resources/definitions/printrbot_simple_extended.def.json
  187. 0
    1
      resources/definitions/prusa_i3.def.json
  188. 0
    1
      resources/definitions/prusa_i3_mk2.def.json
  189. 0
    1
      resources/definitions/prusa_i3_xl.def.json
  190. 0
    1
      resources/definitions/punchtec_connect_xl.def.json
  191. 1
    5
      resources/definitions/raise3D_N2_dual.def.json
  192. 0
    4
      resources/definitions/raise3D_N2_plus_dual.def.json
  193. 0
    4
      resources/definitions/raise3D_N2_plus_single.def.json
  194. 0
    4
      resources/definitions/raise3D_N2_single.def.json
  195. 0
    1
      resources/definitions/renkforce_rf100.def.json
  196. 0
    1
      resources/definitions/rigid3d.def.json
  197. 0
    1
      resources/definitions/rigid3d_3rdgen.def.json
  198. 0
    1
      resources/definitions/rigid3d_hobby.def.json
  199. 0
    1
      resources/definitions/rigid3d_zero.def.json
  200. 0
    1
      resources/definitions/rigid3d_zero2.def.json
  201. 0
    1
      resources/definitions/rigidbot.def.json
  202. 0
    1
      resources/definitions/rigidbot_big.def.json
  203. 0
    1
      resources/definitions/robo_3d_r1.def.json
  204. 0
    1
      resources/definitions/tam.def.json
  205. 67
    0
      resources/definitions/tevo_blackwidow.def.json
  206. 1
    2
      resources/definitions/tevo_tarantula.def.json
  207. 0
    1
      resources/definitions/ultimaker.def.json
  208. 0
    1
      resources/definitions/ultimaker2.def.json
  209. 0
    1
      resources/definitions/ultimaker2_extended.def.json
  210. 0
    1
      resources/definitions/ultimaker2_extended_plus.def.json
  211. 0
    1
      resources/definitions/ultimaker2_go.def.json
  212. 0
    1
      resources/definitions/ultimaker2_plus.def.json
  213. 0
    1
      resources/definitions/ultimaker3.def.json
  214. 0
    1
      resources/definitions/ultimaker3_extended.def.json
  215. 0
    1
      resources/definitions/ultimaker_original.def.json
  216. 0
    1
      resources/definitions/ultimaker_original_dual.def.json
  217. 0
    1
      resources/definitions/ultimaker_original_plus.def.json
  218. 0
    1
      resources/definitions/uniqbot_one.def.json
  219. 0
    1
      resources/definitions/vertex_delta_k8800.def.json
  220. 0
    1
      resources/definitions/vertex_k8400.def.json
  221. 0
    1
      resources/definitions/vertex_k8400_dual.def.json
  222. 0
    1
      resources/definitions/zone3d_printer.def.json
  223. 4595
    0
      resources/i18n/pt_PT/cura.po
  224. 210
    0
      resources/i18n/pt_PT/fdmextruder.def.json.po
  225. 5814
    0
      resources/i18n/pt_PT/fdmprinter.def.json.po
  226. BIN
      resources/meshes/anycubic_i3_mega_platform.stl
  227. BIN
      resources/meshes/malyan_m200_platform.stl
  228. BIN
      resources/meshes/tevo_blackwidow.stl
  229. 63
    4
      resources/qml/Actions.qml
  230. 10
    1
      resources/qml/AddMachineDialog.qml
  231. 45
    45
      resources/qml/AskOpenAsProjectOrModelsDialog.qml
  232. 114
    35
      resources/qml/Cura.qml
  233. 71
    0
      resources/qml/MachineSelection.qml
  234. 32
    1
      resources/qml/Menus/ContextMenu.qml
  235. 5
    4
      resources/qml/Menus/MaterialMenu.qml
  236. 53
    12
      resources/qml/Menus/ViewMenu.qml
  237. 12
    8
      resources/qml/MonitorButton.qml
  238. 265
    0
      resources/qml/ObjectsList.qml
  239. 45
    1
      resources/qml/Preferences/GeneralPage.qml
  240. 12
    7
      resources/qml/Preferences/MachinesPage.qml
  241. 52
    36
      resources/qml/Preferences/MaterialView.qml
  242. 49
    49
      resources/qml/Preferences/MaterialsPage.qml
  243. 83
    2
      resources/qml/PrintMonitor.qml
  244. 46
    31
      resources/qml/SaveButton.qml
  245. 151
    24
      resources/qml/Settings/SettingCategory.qml
  246. 2
    3
      resources/qml/Settings/SettingCheckBox.qml
  247. 73
    67
      resources/qml/Settings/SettingComboBox.qml
  248. 83
    66
      resources/qml/Settings/SettingExtruder.qml
  249. 14
    10
      resources/qml/Settings/SettingItem.qml
  250. 83
    66
      resources/qml/Settings/SettingOptionalExtruder.qml
  251. 3
    10
      resources/qml/Settings/SettingTextField.qml
  252. 2
    2
      resources/qml/Settings/SettingUnknown.qml
  253. 4
    5
      resources/qml/Settings/SettingView.qml
  254. 131
    126
      resources/qml/Sidebar.qml
  255. 2
    3
      resources/qml/SidebarAdvanced.qml
  256. 4
    1
      resources/qml/SidebarHeader.qml
  257. 42
    18
      resources/qml/SidebarSimple.qml
  258. 68
    250
      resources/qml/Topbar.qml
  259. 60
    0
      resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg
  260. 60
    0
      resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg
  261. 60
    0
      resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg
  262. 24
    0
      resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg
  263. 25
    0
      resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg
  264. 23
    0
      resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg
  265. 24
    0
      resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg
  266. 25
    0
      resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg
  267. 23
    0
      resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg
  268. 24
    0
      resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg
  269. 25
    0
      resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg
  270. 23
    0
      resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg
  271. 0
    1
      resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg
  272. 0
    1
      resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg
  273. 0
    1
      resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg
  274. 24
    0
      resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg
  275. 25
    0
      resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg
  276. 23
    0
      resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg
  277. 14
    0
      resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg
  278. 14
    0
      resources/quality/builder_premium/bp_global_High_Quality.inst.cfg
  279. 14
    0
      resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg
  280. 15
    0
      resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg
  281. 15
    0
      resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg
  282. 15
    0
      resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg
  283. 15
    0
      resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg
  284. 15
    0
      resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg
  285. 15
    0
      resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg
  286. 15
    0
      resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg
  287. 15
    0
      resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg
  288. 22
    0
      resources/quality/malyan_m200/malyan_m200_0.04375.inst.cfg
  289. 22
    0
      resources/quality/malyan_m200/malyan_m200_0.0875.inst.cfg
  290. 22
    0
      resources/quality/malyan_m200/malyan_m200_0.13125.inst.cfg
  291. 23
    0
      resources/quality/malyan_m200/malyan_m200_0.175.inst.cfg
  292. 22
    0
      resources/quality/malyan_m200/malyan_m200_0.21875.inst.cfg
  293. 22
    0
      resources/quality/malyan_m200/malyan_m200_0.2625.inst.cfg
  294. 22
    0
      resources/quality/malyan_m200/malyan_m200_0.30625.inst.cfg
  295. 23
    0
      resources/quality/malyan_m200/malyan_m200_0.35.inst.cfg
  296. 23
    0
      resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg
  297. 23
    0
      resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg
  298. 23
    0
      resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg
  299. 23
    0
      resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg
  300. 23
    0
      resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg
  301. 23
    0
      resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg
  302. 23
    0
      resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg
  303. 23
    0
      resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg
  304. 11
    0
      resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg
  305. 11
    0
      resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg
  306. 11
    0
      resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg
  307. 11
    0
      resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg
  308. 11
    0
      resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg
  309. 11
    0
      resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg
  310. 11
    0
      resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg
  311. 11
    0
      resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg
  312. 15
    0
      resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg
  313. 15
    0
      resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg
  314. 15
    0
      resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg
  315. 15
    0
      resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg
  316. 15
    0
      resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg
  317. 15
    0
      resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg
  318. 15
    0
      resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg
  319. 15
    0
      resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg
  320. 15
    0
      resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg
  321. 15
    0
      resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg
  322. 15
    0
      resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg
  323. 15
    0
      resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg
  324. 15
    0
      resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg
  325. 15
    0
      resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg
  326. 15
    0
      resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg
  327. 15
    0
      resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg
  328. 23
    0
      resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg
  329. 23
    0
      resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg
  330. 23
    0
      resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg
  331. 23
    0
      resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg
  332. 23
    0
      resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg
  333. 23
    0
      resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg
  334. 23
    0
      resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg
  335. 23
    0
      resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg
  336. 11
    0
      resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg
  337. 11
    0
      resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg
  338. 11
    0
      resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg
  339. 11
    0
      resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg
  340. 11
    0
      resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg
  341. 11
    0
      resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg
  342. 11
    0
      resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg
  343. 11
    0
      resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg
  344. 15
    0
      resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg
  345. 15
    0
      resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg
  346. 15
    0
      resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg
  347. 15
    0
      resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg
  348. 15
    0
      resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg
  349. 15
    0
      resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg
  350. 15
    0
      resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg
  351. 15
    0
      resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg
  352. 11
    0
      resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg
  353. 11
    0
      resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg
  354. 11
    0
      resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg
  355. 11
    0
      resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg
  356. 11
    0
      resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg
  357. 11
    0
      resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg
  358. 11
    0
      resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg
  359. 11
    0
      resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg
  360. 11
    0
      resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg
  361. 11
    0
      resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg
  362. 11
    0
      resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg
  363. 11
    0
      resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg
  364. 11
    0
      resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg
  365. 11
    0
      resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg
  366. 11
    0
      resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg
  367. 11
    0
      resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg
  368. 33
    0
      resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg
  369. 33
    0
      resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg
  370. 33
    0
      resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg
  371. 34
    0
      resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg
  372. 11
    3
      resources/shaders/striped.shader
  373. 1
    0
      resources/shaders/transparent_object.shader
  374. 9
    5
      resources/themes/cura-light/styles.qml
  375. 7
    1
      resources/themes/cura-light/theme.json
  376. 65
    72
      tests/Settings/TestCuraContainerRegistry.py
  377. 29
    0
      tests/Settings/TestExtruderStack.py
  378. 29
    0
      tests/Settings/TestGlobalStack.py
  379. 9
    4
      tests/TestArrange.py

+ 36
- 0
.github/ISSUE_TEMPLATE.md View File

@@ -0,0 +1,36 @@
<!--
The following template is useful for filing new issues. Processing an issue will go much faster when this is filled out.
Before filing, please check if the issue already exists (either open or closed).

It is also helpful to attach a project (.3MF) file and Cura log file so we can debug issues quicker.
Information about how to find the log file can be found at https://github.com/Ultimaker/Cura/wiki/Cura-Preferences-and-Settings-Locations.

Thank you for using Cura!
-->

**Application Version**
<!-- The version of the application this issue occurs with -->

**Platform**
<!-- Information about the platform the issue occurs on -->

**Qt**
<!-- The version of Qt used (not necessary if you're using the version from Ultimaker's website) -->

**PyQt**
<!-- The version of PyQt used (not necessary if you're using the version from Ultimaker's website) -->

**Display Driver**
<!-- Video driver name and version -->

**Steps to Reproduce**
<!-- Add the steps needed that lead up to the issue (replace this text) -->

**Actual Results**
<!-- What happens after the above steps have been followed (replace this text) -->

**Expected results**
<!-- What should happen after the above steps have been followed (replace this text) -->

**Additional Information**
<!-- Extra information relevant to the issue, like screenshots (replace this text) -->

+ 12
- 8
.gitignore View File

@@ -33,18 +33,22 @@ cura.desktop
.settings

#Externally located plug-ins.
plugins/cura-big-flame-graph
plugins/cura-god-mode-plugin
plugins/cura-siemensnx-plugin
plugins/CuraBlenderPlugin
plugins/CuraCloudPlugin
plugins/CuraLiveScriptingPlugin
plugins/CuraOpenSCADPlugin
plugins/CuraPrintProfileCreator
plugins/CuraSolidWorksPlugin
plugins/CuraVariSlicePlugin
plugins/Doodle3D-cura-plugin
plugins/GodMode
plugins/PostProcessingPlugin
plugins/X3GWriter
plugins/FlatProfileExporter
plugins/GodMode
plugins/OctoPrintPlugin
plugins/ProfileFlattener
plugins/cura-god-mode-plugin
plugins/cura-big-flame-graph
plugins/cura-siemensnx-plugin
plugins/CuraVariSlicePlugin
plugins/CuraLiveScriptingPlugin
plugins/X3GWriter

#Build stuff
CMakeCache.txt

+ 0
- 173
CHANGES View File

@@ -1,173 +0,0 @@
Cura 15.06 Beta
===============

This is the *Beta* version of Cura 15.06.

Cura 15.06 is a new release built from the ground up on a completely new
framework called Uranium. This framework has been designed to make it easier to
extend Cura with additional functionality as well as provide a cleaner UI.

Changes since 15.05.95
----------------------

* Fixed: Selection ghost remains visible after deleting an object
* Fixed: Window does not show up immediately after starting application on OSX
* Fixed: Added display of rotation angle during rotation
* Fixed: Object changes position while rotating/scaling
* Fixed: Loading improvements in the layer view
* Fixed: Added application icons
* Fixed: Improved feedback when loading models
* Fixed: Eject device on MacOSX now provides proper feedback
* Fixed: Make it possible to show retraction settings for UM2
* Fixed: Opening the machine preferences page will switch to the first available machine
* Fixed: Improved tool handle hit area size
* Fixed: Render lines with a thickness based on screen DPI

Changes since 15.05.94
----------------------

* Added Russian translations
* Fixed: Infill not displayed in layer view
* Fixed: Cannot select/scale/rotate when first activating the tool and then trying to select a model.
* Fixed: Improved font rendering on Windows
* Fixed: Help > Show Documentation crashes Cura on Windows
* Fixed: "There is no disk in the drive" repeating messages on Windows
* Fixed: Retraction settings not visible for Ultimaker2
* Fixed: Display rotation angle when rotating an object
* Fixed: Time/Quality slider values are properly rounded
* Fixed: Improved clarity of buttons and text
* Fixed: No indication that anything is happening when loading a model
* Fixed: Eject device now works on Windows

Changes since 15.05.93
----------------------

* Fixed: No shortcuts for moving up/down layers in layer view.
* Fixed: Last view layers could not be scrolled through in layer view.
* Fixed: Files provided on command line would not actually show up on the build
platform.
* Fixed: Render a ghost of the selection in Layer view to make the actual object
position clear.
* Fixed: Showing a menu would clear the selection.
* Fixed: Size and scaling factor display for scale tool.
* Fixed: Missing background for additional tool controls.
* Fixed: Loading message times out when loading large files.
* Fixed: Show recent files in the file menu.
* Fixed: Windows installer will now install MSVC 2010 redistributable, to
prevent issues with missing DLL's.
* Fixed: Collapsed/expanded state of setting categories not stored.

Changes since 15.05.91
----------------------

* There is now a working MacOSX version. Currently it supports OSX 10.7 and
higher.
* Fixed: Need to deselect before selecting a different object.
* Fixed: Object can be moved on Z axis.
* Fixed: Error values should be considered invalid values and will not trigger a
slice.
* Fixed: Text fields used a locale-aware validator while the underlying code did
not.
* Fixed: Text fields will trigger a slice on text change, not only after focus
change/enter press.
* Fixed: Rotate Tool snaps to incorrect value.
* Fixed: Object Collision would only moved objects to the right.
* Fixed: Object Collision would move the selected object when it should not.
* Fixed: Camera panning now works correctly instead of doing nothing.
* Fixed: Camera would flip around center point at maximum rotation.
* Fixed: Build platform grid blocked view from below objects.
* Fixed: Viewport on MacOSX with high-DPI screens was only taking 1/4th of the
window

Changes since 15.05.90
----------------------

* Fixed: Additional UI elements for tools and views not loading.
* Fixed: Double click needed to change setting dialog page.
* Fixed: Context menu entries (reload, center object, etc.) not working.
* Fixed: "Open With" or passing files from command line not working.
* Fixed: "Reload All" would not reload files.

In addition, a lot of work has gone into getting a usable Mac OSX version.

New Features
------------

* Plugin based system
The Uranium framework provides us with a plugin-based system
that provides additional flexibility when extending Cura. Think
of new views, tools, file formats, etc. This is probably the
biggest new feature.
* Improved UI
The UI has received a complete overhaul.
* Time-Quality Slider
The 4 static quick print profiles have been replaced with
a slider that should make it easier to find the right spot
between print time and print quality.
* More Settings
The Advanced mode is now configurable and can show many
additional settings that were previously not available, while at
the same time not overwhelming new users with too many settings.
Custom set of visible settings can be created by the user.
* Support for high-DPI screens
The refreshed UI has been designed with high-DPI screens in
mind which should improve the experience of Cura on such
devices.
* Improved language support
(Not yet available for the Beta release.)
* Improved support structure generation
The new version of the CuraEngine now features improved
support generation algorithms and additional options for support
structure generation.
* Experimental Feature: Wire Printing
Wire Printing has been added as an experimental new feature. It
will print objects as a structure of lines. It can be enabled by
from Advanced Mode -> Fixes -> Wire Printing.
* Undo/Redo
It is now possible to undo and redo most scene operations, like
moving or rotating objects.

Features from earlier versions not (yet) in this release
--------------------------------------------------------

* The All-at-once/One-at-a-time toggle is not available.
We are working on an improved implementation of this mechanism
but it will not be available for this release.
* No dual extrusion features are available yet.
We are working on a completely new workflow for this but this
needs additional time.
* “Lay Flat” has been removed.
The existing implementation was unfortunately not salvageable.
We will be looking into an improved implementation for this
feature.
* "Split Object Into Parts" has been removed.
Due to the same reason as Lay Flat.
* Support for AMF and DAE file formats has been removed.
Both of these will be implemented as plugins in the future.
* Support for directly loading a GCode file is not yet available.
This will be implemented as a plugin in the future.
* Support for PNG, JPG and other image formats has been removed.
These can be supported by a plugin with an improved UI.
* Support for loading Minecraft levels has been removed.
This can be implemented as a plugin.
* Windows XP support has been dropped.
Microsoft is no longer supporting xp, so they no longer back
port certain features that we require.
* X-Ray view is missing.
Will be implemented as a (you might have guessed it) plugin.
* Fixes: Follow Mesh Surface
Has been removed from the engine, the same result can be
achieved using no infill or top/bottom layers.

Known Issues
------------

For an up to date list of all known issues, please see
https://github.com/Ultimaker/Cura/issues and
https://github.com/Ultimaker/Uranium/issues .

* Some OBJ files are rendered as black objects due to missing
normals.
* Disabling plugins does not work correctly yet.
* Unicorn occasionally still requires feeding. Do not feed it
after midnight.

+ 35
- 33
Jenkinsfile View File

@@ -1,45 +1,47 @@
parallel_nodes(['linux && cura', 'windows && cura']) {
// Prepare building
stage('Prepare') {
// Ensure we start with a clean build directory.
step([$class: 'WsCleanup'])
timeout(time: 2, unit: "HOURS") {
// Prepare building
stage('Prepare') {
// Ensure we start with a clean build directory.
step([$class: 'WsCleanup'])

// Checkout whatever sources are linked to this pipeline.
checkout scm
}
// Checkout whatever sources are linked to this pipeline.
checkout scm
}

// If any error occurs during building, we want to catch it and continue with the "finale" stage.
catchError {
// Building and testing should happen in a subdirectory.
dir('build') {
// Perform the "build". Since Uranium is Python code, this basically only ensures CMake is setup.
stage('Build') {
def branch = env.BRANCH_NAME
if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) {
branch = "master"
}
// If any error occurs during building, we want to catch it and continue with the "finale" stage.
catchError {
// Building and testing should happen in a subdirectory.
dir('build') {
// Perform the "build". Since Uranium is Python code, this basically only ensures CMake is setup.
stage('Build') {
def branch = env.BRANCH_NAME
if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) {
branch = "master"
}

// Ensure CMake is setup. Note that since this is Python code we do not really "build" it.
def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}")
cmake("..", "-DCMAKE_PREFIX_PATH=\"${env.CURA_ENVIRONMENT_PATH}/${branch}\" -DCMAKE_BUILD_TYPE=Release -DURANIUM_DIR=\"${uranium_dir}\"")
}
// Ensure CMake is setup. Note that since this is Python code we do not really "build" it.
def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}")
cmake("..", "-DCMAKE_PREFIX_PATH=\"${env.CURA_ENVIRONMENT_PATH}/${branch}\" -DCMAKE_BUILD_TYPE=Release -DURANIUM_DIR=\"${uranium_dir}\"")
}

// Try and run the unit tests. If this stage fails, we consider the build to be "unstable".
stage('Unit Test') {
try {
make('test')
} catch(e) {
currentBuild.result = "UNSTABLE"
// Try and run the unit tests. If this stage fails, we consider the build to be "unstable".
stage('Unit Test') {
try {
make('test')
} catch(e) {
currentBuild.result = "UNSTABLE"
}
}
}
}
}

// Perform any post-build actions like notification and publishing of unit tests.
stage('Finalize') {
// Publish the test results to Jenkins.
junit allowEmptyResults: true, testResults: 'build/junit*.xml'
// Perform any post-build actions like notification and publishing of unit tests.
stage('Finalize') {
// Publish the test results to Jenkins.
junit allowEmptyResults: true, testResults: 'build/junit*.xml'

notify_build_result(env.CURA_EMAIL_RECIPIENTS, '#cura-dev', ['master', '2.'])
notify_build_result(env.CURA_EMAIL_RECIPIENTS, '#cura-dev', ['master', '2.'])
}
}
}

+ 18
- 33
README.md View File

@@ -1,22 +1,16 @@
Cura
====

This is the new, shiny frontend for Cura. [daid/Cura](https://github.com/daid/Cura.git) is the old legacy Cura that everyone knows and loves/hates.

We re-worked the whole GUI code at Ultimaker, because the old code started to become a unmaintainable.

This is the new, shiny frontend for Cura. Check [daid/LegacyCura](https://github.com/daid/LegacyCura) for the legacy Cura that everyone knows and loves/hates. We re-worked the whole GUI code at Ultimaker, because the old code started to become unmaintainable.

Logging Issues
------------
Use [this](https://github.com/Ultimaker/Uranium/wiki/Bug-Reporting-Template) template to report issues. New issues that do not adhere to this template will take us a lot longer to handle and will therefore have a lower pirority.

For crashes and similar issues, please attach the following information:

* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output)
* The Cura GUI log file, located at
* %APPDATA%\cura\\`<Cura version>`\cura.log (Windows), or usually C:\Users\\`<your username>`\AppData\Roaming\cura\\`<Cura version>`\cura.log
* $User/Library/Application Support/cura/`<Cura version>`/cura.log (OSX)
* $USER/.local/share/cura/`<Cura version>`/cura.log (Ubuntu/Linux)
* `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
* `$USER/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
* `$USER/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux)

If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder

@@ -41,35 +35,22 @@ Configuring Cura
location = /[path_to_the..]/CuraEngine/build/CuraEngine
```


Build scripts
-------------
Please checkout [cura-build](https://github.com/Ultimaker/cura-build) for detailed building instructions.

Please checkout [cura-build](https://github.com/Ultimaker/cura-build)

Third party plugins
Plugins
-------------
* [Post Processing Plugin](https://github.com/nallath/PostProcessingPlugin): Allows for post-processing scripts to run on g-code.
* [Barbarian Plugin](https://github.com/nallath/BarbarianPlugin): Simple scale tool for imperial to metric.
* [X3G Writer](https://github.com/Ghostkeeper/X3GWriter): Adds support for exporting X3G files.
* [Auto orientation](https://github.com/nallath/CuraOrientationPlugin): Calculate the optimal orientation for a model.
* [OctoPrint Plugin](https://github.com/fieldofview/OctoPrintPlugin): Send printjobs directly to OctoPrint and monitor their progress in Cura.
* [Electric Print Cost Calculator Plugin](https://github.com/zoff99/ElectricPrintCostCalculator): Calculate the electric costs of a print.

Making profiles for other printers
----------------------------------
If your make of printer is not in the list of supported printers, and using the "Custom FDM Printer" does not offer enough flexibility, you can use [this](https://github.com/Ultimaker/Cura/blob/master/resources/definitions/ultimaker_original.def.json) as a template.
Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Plugin-Directory) for details about creating and using plugins.

* Change the machine ID to something unique
* Change the machine_name to your printer's name
* If you have a 3D model of your platform you can put it in resources/meshes and put its name under platform
* Set your machine's dimensions with machine_width, machine_depth, and machine_height
* If your printer's origin is in the center of the bed, set machine_center_is_zero to true.
* Set your print head dimensions with the machine_head_shape parameters
* Set the start and end gcode in machine_start_gcode and machine_end_gcode

Once you are done, put the profile you have made into resources/definitions, or in definitions in your cura profile folder.
Supported printers
-------------
Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Adding-new-machine-profiles-to-Cura) for guidelines about adding support for new machines.

If you want to make a definition for a multi-extrusion printer, have a look at [this](https://github.com/Ultimaker/Cura/blob/master/resources/definitions/ultimaker_original_dual.def.json) as a template, along with the two extruder definitions it references [here](https://github.com/Ultimaker/Cura/blob/master/resources/extruders/ultimaker_original_dual_1st.def.json) and [here](https://github.com/Ultimaker/Cura/blob/master/resources/extruders/ultimaker_original_dual_2nd.def.json)
Configuring Cura
----------------
Please check out [Wiki page](https://github.com/Ultimaker/Cura/wiki/Cura-Settings) about configuration options for developers.

Translating Cura
----------------
@@ -92,3 +73,7 @@ To submit your translation, ideally you would make two pull requests where all `
After the translation is submitted, the Cura maintainers will check for its completeness and check whether it is consistent. We will take special care to look for common mistakes, such as translating mark-up `<message>` code and such. We are often not fluent in every language, so we expect the translator and the international users to make corrections where necessary. Of course, there will always be some mistakes in every translation.

When the next Cura release comes around, some of the texts will have changed and some new texts will have been added. Around the time when the beta is released we will invoke a string freeze, meaning that no developer is allowed to make changes to the texts. Then we will update the translation template `.pot` files and ask all our translators to update their translations. If you are unable to update the translation in time for the actual release, we will remove the language from the drop-down menu in the Preferences window. The translation stays in Cura however, so that someone might pick it up again later and update it with the newest texts. Also, users who had previously selected the language can still continue Cura in their language but English text will appear among the original text.

License
----------------
Cura is released under the terms of the LGPLv3 or higher. A copy of this license should be included with the software.

+ 13
- 6
cmake/CuraTests.cmake View File

@@ -24,16 +24,23 @@ function(cura_add_test)
if(WIN32)
string(REPLACE "|" "\\;" _PYTHONPATH ${_PYTHONPATH})
set(_PYTHONPATH "${_PYTHONPATH}\\;$ENV{PYTHONPATH}")
else()
string(REPLACE "|" ":" _PYTHONPATH ${_PYTHONPATH})
set(_PYTHONPATH "${_PYTHONPATH}:$ENV{PYTHONPATH}")
endif()

add_test(
NAME ${_NAME}
COMMAND ${PYTHON_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
)
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C)
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}")
get_test_property(${_NAME} ENVIRONMENT test_exists) #Find out if the test exists by getting a property from it that always exists (such as ENVIRONMENT because we set that ourselves).
if (NOT ${test_exists})
add_test(
NAME ${_NAME}
COMMAND ${PYTHON_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
)
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C)
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}")
else()
message(WARNING "Duplicate test ${_NAME}!")
endif()
endfunction()

cura_add_test(NAME pytest-main DIRECTORY ${CMAKE_SOURCE_DIR}/tests PYTHONPATH "${CMAKE_SOURCE_DIR}|${URANIUM_DIR}")

cura/Arrange.py → cura/Arranging/Arrange.py View File

@@ -1,8 +1,8 @@
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Logger import Logger
from UM.Math.Vector import Vector
from cura.ShapeArray import ShapeArray
from cura import ZOffsetDecorator
from cura.Arranging.ShapeArray import ShapeArray
from cura.Scene import ZOffsetDecorator

from collections import namedtuple

@@ -29,6 +29,7 @@ class Arrange:
self._offset_x = offset_x
self._offset_y = offset_y
self._last_priority = 0
self._is_empty = True

## Helper to create an Arranger instance
#
@@ -37,8 +38,8 @@ class Arrange:
# \param scene_root Root for finding all scene nodes
# \param fixed_nodes Scene nodes to be placed
@classmethod
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5):
arranger = Arrange(220, 220, 110, 110, scale = scale)
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 220, y = 220):
arranger = Arrange(x, y, x // 2, y // 2, scale = scale)
arranger.centerFirst()

if fixed_nodes is None:
@@ -50,6 +51,8 @@ class Arrange:
# place all objects fixed nodes
for fixed_node in fixed_nodes:
vertices = fixed_node.callDecoration("getConvexHull")
if not vertices:
continue
points = copy.deepcopy(vertices._points)
shape_arr = ShapeArray.fromPolygon(points, scale = scale)
arranger.place(0, 0, shape_arr)
@@ -59,7 +62,7 @@ class Arrange:
for area in disallowed_areas:
points = copy.deepcopy(area._points)
shape_arr = ShapeArray.fromPolygon(points, scale = scale)
arranger.place(0, 0, shape_arr)
arranger.place(0, 0, shape_arr, update_empty = False)
return arranger

## Find placement for a node (using offset shape) and place it (using hull shape)
@@ -163,7 +166,8 @@ class Arrange:
# \param x x-coordinate
# \param y y-coordinate
# \param shape_arr ShapeArray object
def place(self, x, y, shape_arr):
# \param update_empty updates the _is_empty, used when adding disallowed areas
def place(self, x, y, shape_arr, update_empty = True):
x = int(self._scale * x)
y = int(self._scale * y)
offset_x = x + self._offset_x + shape_arr.offset_x
@@ -176,10 +180,17 @@ class Arrange:
max_y = min(max(offset_y + shape_arr.arr.shape[0], 0), shape_y - 1)
occupied_slice = self._occupied[min_y:max_y, min_x:max_x]
# we use a slice of shape because it can be out of bounds
occupied_slice[numpy.where(shape_arr.arr[
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)] = 1
new_occupied = numpy.where(shape_arr.arr[
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)
if update_empty and new_occupied:
self._is_empty = False
occupied_slice[new_occupied] = 1

# Set priority to low (= high number), so it won't get picked at trying out.
prio_slice = self._priority[min_y:max_y, min_x:max_x]
prio_slice[numpy.where(shape_arr.arr[
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)] = 999

@property
def isEmpty(self):
return self._is_empty

+ 154
- 0
cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py View File

@@ -0,0 +1,154 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

from UM.Job import Job
from UM.Scene.SceneNode import SceneNode
from UM.Math.Vector import Vector
from UM.Operations.TranslateOperation import TranslateOperation
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Message import Message
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")

from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
from cura.Arranging.Arrange import Arrange
from cura.Arranging.ShapeArray import ShapeArray

from typing import List


class ArrangeArray:
def __init__(self, x: int, y: int, fixed_nodes: List[SceneNode]):
self._x = x
self._y = y
self._fixed_nodes = fixed_nodes
self._count = 0
self._first_empty = None
self._has_empty = False
self._arrange = []

def _update_first_empty(self):
for i, a in enumerate(self._arrange):
if a.isEmpty:
self._first_empty = i
self._has_empty = True
return
self._first_empty = None
self._has_empty = False

def add(self):
new_arrange = Arrange.create(x = self._x, y = self._y, fixed_nodes = self._fixed_nodes)
self._arrange.append(new_arrange)
self._count += 1
self._update_first_empty()

def count(self):
return self._count

def get(self, index):
return self._arrange[index]

def getFirstEmpty(self):
if not self._is_empty:
self.add()
return self._arrange[self._first_empty]


class ArrangeObjectsAllBuildPlatesJob(Job):
def __init__(self, nodes: List[SceneNode], min_offset = 8):
super().__init__()
self._nodes = nodes
self._min_offset = min_offset

def run(self):
status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"),
lifetime = 0,
dismissable=False,
progress = 0,
title = i18n_catalog.i18nc("@info:title", "Finding Location"))
status_message.show()


# Collect nodes to be placed
nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr)
for node in self._nodes:
offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset)
nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr))

# Sort the nodes with the biggest area first.
nodes_arr.sort(key=lambda item: item[0])
nodes_arr.reverse()

x, y = 200, 200

arrange_array = ArrangeArray(x = x, y = y, fixed_nodes = [])
arrange_array.add()

# Place nodes one at a time
start_priority = 0
grouped_operation = GroupedOperation()
found_solution_for_all = True
left_over_nodes = [] # nodes that do not fit on an empty build plate

for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr):
# For performance reasons, we assume that when a location does not fit,
# it will also not fit for the next object (while what can be untrue).
# We also skip possibilities by slicing through the possibilities (step = 10)

try_placement = True

current_build_plate_number = 0 # always start with the first one

# # Only for first build plate
# if last_size == size and last_build_plate_number == current_build_plate_number:
# # This optimization works if many of the objects have the same size
# # Continue with same build plate number
# start_priority = last_priority
# else:
# start_priority = 0

while try_placement:
# make sure that current_build_plate_number is not going crazy or you'll have a lot of arrange objects
while current_build_plate_number >= arrange_array.count():
arrange_array.add()
arranger = arrange_array.get(current_build_plate_number)

best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority, step=10)
x, y = best_spot.x, best_spot.y
node.removeDecorator(ZOffsetDecorator)
if node.getBoundingBox():
center_y = node.getWorldPosition().y - node.getBoundingBox().bottom
else:
center_y = 0
if x is not None: # We could find a place
arranger.place(x, y, hull_shape_arr) # place the object in the arranger

node.callDecoration("setBuildPlateNumber", current_build_plate_number)
grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True))
try_placement = False
else:
# very naive, because we skip to the next build plate if one model doesn't fit.
if arranger.isEmpty:
# apparently we can never place this object
left_over_nodes.append(node)
try_placement = False
else:
# try next build plate
current_build_plate_number += 1
try_placement = True

status_message.setProgress((idx + 1) / len(nodes_arr) * 100)
Job.yieldThread()

for node in left_over_nodes:
node.callDecoration("setBuildPlateNumber", -1) # these are not on any build plate
found_solution_for_all = False

grouped_operation.push()

status_message.hide()

if not found_solution_for_all:
no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"),
title = i18n_catalog.i18nc("@info:title", "Can't Find Location"))
no_full_solution_message.show()

cura/ArrangeObjectsJob.py → cura/Arranging/ArrangeObjectsJob.py View File

@@ -4,7 +4,6 @@
from UM.Job import Job
from UM.Scene.SceneNode import SceneNode
from UM.Math.Vector import Vector
from UM.Operations.SetTransformOperation import SetTransformOperation
from UM.Operations.TranslateOperation import TranslateOperation
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Logger import Logger
@@ -12,9 +11,9 @@ from UM.Message import Message
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")

from cura.ZOffsetDecorator import ZOffsetDecorator
from cura.Arrange import Arrange
from cura.ShapeArray import ShapeArray
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
from cura.Arranging.Arrange import Arrange
from cura.Arranging.ShapeArray import ShapeArray

from typing import List


cura/ShapeArray.py → cura/Arranging/ShapeArray.py View File

@@ -29,8 +29,12 @@ class ShapeArray:
offset_x = int(numpy.amin(flip_vertices[:, 1]))
flip_vertices[:, 0] = numpy.add(flip_vertices[:, 0], -offset_y)
flip_vertices[:, 1] = numpy.add(flip_vertices[:, 1], -offset_x)
shape = [int(numpy.amax(flip_vertices[:, 0])), int(numpy.amax(flip_vertices[:, 1]))]
shape = numpy.array([int(numpy.amax(flip_vertices[:, 0])), int(numpy.amax(flip_vertices[:, 1]))])
shape[numpy.where(shape == 0)] = 1
arr = cls.arrayFromPolygon(shape, flip_vertices)
if not numpy.ndarray.any(arr):
# set at least 1 pixel
arr[0][0] = 1
return cls(arr, offset_x, offset_y)

## Instantiate an offset and hull ShapeArray from a scene node.
@@ -43,13 +47,12 @@ class ShapeArray:
transform_x = transform._data[0][3]
transform_y = transform._data[2][3]
hull_verts = node.callDecoration("getConvexHull")
# If a model is too small then it will not contain any points
if hull_verts is None or not hull_verts.getPoints().any():
return None, None
# For one_at_a_time printing you need the convex hull head.
hull_head_verts = node.callDecoration("getConvexHullHead") or hull_verts

# If a model is to small then it will not contain any points
if not hull_verts.getPoints().any():
return None, None

offset_verts = hull_head_verts.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
offset_points = copy.deepcopy(offset_verts._points) # x, y
offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)

+ 0
- 0
cura/Arranging/__init__.py View File


+ 53
- 0
cura/BuildPlateModel.py View File

@@ -0,0 +1,53 @@
from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot

from UM.Qt.ListModel import ListModel
from UM.Scene.Selection import Selection
from UM.Logger import Logger
from UM.Application import Application


class BuildPlateModel(ListModel):
maxBuildPlateChanged = pyqtSignal()
activeBuildPlateChanged = pyqtSignal()
selectionChanged = pyqtSignal()

def __init__(self):
super().__init__()
Application.getInstance().getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbers)
Selection.selectionChanged.connect(self._updateSelectedObjectBuildPlateNumbers)

self._max_build_plate = 1 # default
self._active_build_plate = -1
self._selection_build_plates = []

def setMaxBuildPlate(self, max_build_plate):
self._max_build_plate = max_build_plate
self.maxBuildPlateChanged.emit()

## Return the highest build plate number
@pyqtProperty(int, notify = maxBuildPlateChanged)
def maxBuildPlate(self):
return self._max_build_plate

def setActiveBuildPlate(self, nr):
self._active_build_plate = nr
self.activeBuildPlateChanged.emit()

@pyqtProperty(int, notify = activeBuildPlateChanged)
def activeBuildPlate(self):
return self._active_build_plate

@staticmethod
def createBuildPlateModel():
return BuildPlateModel()

def _updateSelectedObjectBuildPlateNumbers(self, *args):
result = set()
for node in Selection.getAllSelectedObjects():
result.add(node.callDecoration("getBuildPlateNumber"))
self._selection_build_plates = list(result)
self.selectionChanged.emit()

@pyqtProperty("QVariantList", notify = selectionChanged)
def selectionBuildPlates(self):
return self._selection_build_plates

+ 17
- 6
cura/BuildVolume.py View File

@@ -572,14 +572,13 @@ class BuildVolume(SceneNode):
update_disallowed_areas = False
update_raft_thickness = False
update_extra_z_clearance = True

for setting_key in self._changed_settings_since_last_rebuild:

if setting_key == "print_sequence":
machine_height = self._global_container_stack.getProperty("machine_height", "value")
if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence",
"value") == "one_at_a_time" and len(
self._scene_objects) > 1:
self._height = min(self._global_container_stack.getProperty("gantry_height", "value"),
machine_height)
if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1:
self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
if self._height < machine_height:
self._build_volume_message.show()
else:
@@ -587,9 +586,20 @@ class BuildVolume(SceneNode):
else:
self._height = self._global_container_stack.getProperty("machine_height", "value")
self._build_volume_message.hide()
update_disallowed_areas = True
rebuild_me = True

# sometimes the machine size or shape settings are adjusted on the active machine, we should reflect this
if setting_key in self._machine_settings:
self._height = self._global_container_stack.getProperty("machine_height", "value")
self._width = self._global_container_stack.getProperty("machine_width", "value")
self._depth = self._global_container_stack.getProperty("machine_depth", "value")
self._shape = self._global_container_stack.getProperty("machine_shape", "value")
update_extra_z_clearance = True
update_disallowed_areas = True
rebuild_me = True

if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings or setting_key == "print_sequence" or setting_key in self._ooze_shield_settings or setting_key in self._distance_settings or setting_key in self._extruder_settings:
if setting_key in self._skirt_settings + self._prime_settings + self._tower_settings + self._ooze_shield_settings + self._distance_settings + self._extruder_settings:
update_disallowed_areas = True
rebuild_me = True

@@ -1028,6 +1038,7 @@ class BuildVolume(SceneNode):
def _clamp(self, value, min_value, max_value):
return max(min(value, max_value), min_value)

_machine_settings = ["machine_width", "machine_depth", "machine_height", "machine_shape", "machine_center_is_zero"]
_skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "initial_layer_line_width_factor"]
_raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap", "layer_0_z_overlap"]
_extra_z_settings = ["retraction_hop_enabled", "retraction_hop"]

+ 14
- 11
cura/CrashHandler.py View File

@@ -37,7 +37,7 @@ else:
# List of exceptions that should be considered "fatal" and abort the program.
# These are primarily some exception types that we simply cannot really recover from
# (MemoryError and SystemError) and exceptions that indicate grave errors in the
# code that cause the Python interpreter to fail (SyntaxError, ImportError).
# code that cause the Python interpreter to fail (SyntaxError, ImportError).
fatal_exception_types = [
MemoryError,
SyntaxError,
@@ -52,13 +52,13 @@ class CrashHandler:
self.exception_type = exception_type
self.value = value
self.traceback = tb
self.dialog = QDialog()
self.dialog = None # Don't create a QDialog before there is a QApplication

# While we create the GUI, the information will be stored for sending afterwards
self.data = dict()
self.data["time_stamp"] = time.time()

Logger.log("c", "An uncaught exception has occurred!")
Logger.log("c", "An uncaught error has occurred!")
for line in traceback.format_exception(exception_type, value, tb):
for part in line.rstrip("\n").split("\n"):
Logger.log("c", part)
@@ -70,6 +70,7 @@ class CrashHandler:
if not application:
sys.exit(1)

self.dialog = QDialog()
self._createDialog()

## Creates a modal dialog.
@@ -89,7 +90,7 @@ class CrashHandler:

def _messageWidget(self):
label = QLabel()
label.setText(catalog.i18nc("@label crash message", """<p><b>A fatal exception has occurred. Please send us this Crash Report to fix the problem</p></b>
label.setText(catalog.i18nc("@label crash message", """<p><b>A fatal error has occurred. Please send us this Crash Report to fix the problem</p></b>
<p>Please use the "Send report" button to post a bug report automatically to our servers</p>
"""))

@@ -107,11 +108,11 @@ class CrashHandler:
except:
self.cura_version = catalog.i18nc("@label unknown version of Cura", "Unknown")

crash_info = catalog.i18nc("@label Cura version", "<b>Cura version:</b> {version}<br/>").format(version = self.cura_version)
crash_info += catalog.i18nc("@label Platform", "<b>Platform:</b> {platform}<br/>").format(platform = platform.platform())
crash_info += catalog.i18nc("@label Qt version", "<b>Qt version:</b> {qt}<br/>").format(qt = QT_VERSION_STR)
crash_info += catalog.i18nc("@label PyQt version", "<b>PyQt version:</b> {pyqt}<br/>").format(pyqt = PYQT_VERSION_STR)
crash_info += catalog.i18nc("@label OpenGL", "<b>OpenGL:</b> {opengl}<br/>").format(opengl = self._getOpenGLInfo())
crash_info = "<b>" + catalog.i18nc("@label Cura version number", "Cura version") + ":</b> " + str(self.cura_version) + "<br/>"
crash_info += "<b>" + catalog.i18nc("@label Type of platform", "Platform") + ":</b> " + str(platform.platform()) + "<br/>"
crash_info += "<b>" + catalog.i18nc("@label", "Qt version") + ":</b> " + str(QT_VERSION_STR) + "<br/>"
crash_info += "<b>" + catalog.i18nc("@label", "PyQt version") + ":</b> " + str(PYQT_VERSION_STR) + "<br/>"
crash_info += "<b>" + catalog.i18nc("@label OpenGL version", "OpenGL") + ":</b> " + str(self._getOpenGLInfo()) + "<br/>"
label.setText(crash_info)

layout.addWidget(label)
@@ -142,7 +143,7 @@ class CrashHandler:

def _exceptionInfoWidget(self):
group = QGroupBox()
group.setTitle(catalog.i18nc("@title:groupbox", "Exception traceback"))
group.setTitle(catalog.i18nc("@title:groupbox", "Error traceback"))
layout = QVBoxLayout()

text_area = QTextEdit()
@@ -284,5 +285,7 @@ class CrashHandler:
Application.getInstance().callLater(self._show)

def _show(self):
self.dialog.exec_()
# When the exception is not in the fatal_exception_types list, the dialog is not created, so we don't need to show it
if self.dialog:
self.dialog.exec_()
os._exit(1)

+ 43
- 3
cura/CuraActions.py View File

@@ -13,12 +13,18 @@ from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.SetTransformOperation import SetTransformOperation
from UM.Operations.TranslateOperation import TranslateOperation

from cura.SetParentOperation import SetParentOperation
from cura.Operations.SetParentOperation import SetParentOperation
from cura.MultiplyObjectsJob import MultiplyObjectsJob
from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation
from cura.Settings.ExtruderManager import ExtruderManager

from cura.Operations.SetBuildPlateNumberOperation import SetBuildPlateNumberOperation

from UM.Logger import Logger


class CuraActions(QObject):
def __init__(self, parent = None):
super().__init__(parent)
@@ -54,7 +60,11 @@ class CuraActions(QObject):
while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
current_node = current_node.getParent()

center_operation = SetTransformOperation(current_node, Vector())
# This was formerly done with SetTransformOperation but because of
# unpredictable matrix deconstruction it was possible that mirrors
# could manifest as rotations. Centering is therefore done by
# moving the node to negative whatever its position is:
center_operation = TranslateOperation(current_node, -current_node._position)
operation.addOperation(center_operation)
operation.push()

@@ -63,7 +73,7 @@ class CuraActions(QObject):
# \param count The number of times to multiply the selection.
@pyqtSlot(int)
def multiplySelection(self, count: int) -> None:
job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, 8)
job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset = 8)
job.start()

## Delete all selected objects.
@@ -84,6 +94,10 @@ class CuraActions(QObject):
removed_group_nodes.append(group_node)
op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
op.addOperation(RemoveSceneNodeOperation(group_node))

# Reset the print information
Application.getInstance().getController().getScene().sceneChanged.emit(node)

op.push()

## Set the extruder that should be used to print the selection.
@@ -124,6 +138,32 @@ class CuraActions(QObject):
operation.addOperation(SetObjectExtruderOperation(node, extruder_id))
operation.push()

@pyqtSlot(int)
def setBuildPlateForSelection(self, build_plate_nr: int) -> None:
Logger.log("d", "Setting build plate number... %d" % build_plate_nr)
operation = GroupedOperation()

root = Application.getInstance().getController().getScene().getRoot()

nodes_to_change = []
for node in Selection.getAllSelectedObjects():
parent_node = node # Find the parent node to change instead
while parent_node.getParent() != root:
parent_node = parent_node.getParent()

for single_node in BreadthFirstIterator(parent_node):
nodes_to_change.append(single_node)

if not nodes_to_change:
Logger.log("d", "Nothing to change.")
return

for node in nodes_to_change:
operation.addOperation(SetBuildPlateNumberOperation(node, build_plate_nr))
operation.push()

Selection.clear()

def _openUrl(self, url):
QDesktopServices.openUrl(url)


+ 238
- 150
cura/CuraApplication.py View File

@@ -1,6 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

from PyQt5.QtNetwork import QLocalServer
from PyQt5.QtNetwork import QLocalSocket

@@ -17,7 +17,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Mesh.ReadMeshJob import ReadMeshJob
from UM.Logger import Logger
from UM.Preferences import Preferences
from UM.SaveFile import SaveFile
from UM.Scene.Selection import Selection
from UM.Scene.GroupDecorator import GroupDecorator
from UM.Settings.ContainerStack import ContainerStack
@@ -32,7 +31,7 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.SetTransformOperation import SetTransformOperation
from cura.Arrange import Arrange
from cura.ShapeArray import ShapeArray
from cura.ConvexHullDecorator import ConvexHullDecorator
from cura.SetParentOperation import SetParentOperation
@@ -40,8 +39,20 @@ from cura.SliceableObjectDecorator import SliceableObjectDecorator
from cura.BlockSlicingDecorator import BlockSlicingDecorator
from cura.Settings.MaterialsModel import MaterialsModel

from cura.ArrangeObjectsJob import ArrangeObjectsJob
from cura.Arranging.Arrange import Arrange
from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob
from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
from cura.Arranging.ShapeArray import ShapeArray

from cura.MultiplyObjectsJob import MultiplyObjectsJob
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
from cura.Operations.SetParentOperation import SetParentOperation
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
from cura.Scene.BlockSlicingDecorator import BlockSlicingDecorator
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
from cura.Scene.CuraSceneNode import CuraSceneNode

from cura.Scene.CuraSceneController import CuraSceneController

from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
from UM.Settings.ContainerRegistry import ContainerRegistry
@@ -61,7 +72,7 @@ from . import BuildVolume
from . import CameraAnimation
from . import PrintInformation
from . import CuraActions
from . import ZOffsetDecorator
from cura.Scene import ZOffsetDecorator
from . import CuraSplashScreen
from . import CameraImageProvider
from . import MachineActionManager
@@ -75,8 +86,9 @@ from cura.Settings.ContainerSettingsModel import ContainerSettingsModel
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
from cura.Settings.QualitySettingsModel import QualitySettingsModel
from cura.Settings.ContainerManager import ContainerManager
from cura.Settings.GlobalStack import GlobalStack
from cura.Settings.ExtruderStack import ExtruderStack

from cura.ObjectsModel import ObjectsModel
from cura.BuildPlateModel import BuildPlateModel

from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
from UM.FlameProfiler import pyqtSlot
@@ -88,7 +100,6 @@ import sys
import os.path
import numpy
import copy
import urllib.parse
import os
import argparse
import json
@@ -99,10 +110,11 @@ numpy.seterr(all="ignore")
MYPY = False
if not MYPY:
try:
from cura.CuraVersion import CuraVersion, CuraBuildType
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode
except ImportError:
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
CuraBuildType = ""
CuraDebugMode = False


class CuraApplication(QtApplication):
@@ -130,7 +142,8 @@ class CuraApplication(QtApplication):
# Cura will always show the Add Machine Dialog upon start.
stacksValidationFinished = pyqtSignal() # Emitted whenever a validation is finished

def __init__(self):
def __init__(self, **kwargs):

# this list of dir names will be used by UM to detect an old cura directory
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
Resources.addExpectedDirNameInData(dir_name)
@@ -159,7 +172,6 @@ class CuraApplication(QtApplication):

SettingDefinition.addSettingType("extruder", None, str, Validator)
SettingDefinition.addSettingType("optional_extruder", None, str, None)

SettingDefinition.addSettingType("[int]", None, str, None)

SettingFunction.registerOperator("extruderValues", ExtruderManager.getExtruderValues)
@@ -175,16 +187,18 @@ class CuraApplication(QtApplication):
Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")

ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality_changes")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack, "machine")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")

## Initialise the version upgrade manager with Cura's storage paths.
import UM.VersionUpgradeManager #Needs to be here to prevent circular dependencies.
# Needs to be here to prevent circular dependencies.
import UM.VersionUpgradeManager

UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
{
@@ -218,14 +232,17 @@ class CuraApplication(QtApplication):
self._machine_manager = None # This is initialized on demand.
self._extruder_manager = None
self._material_manager = None
self._object_manager = None
self._build_plate_model = None
self._setting_inheritance_manager = None
self._simple_mode_settings_manager = None
self._cura_scene_controller = None

self._additional_components = {} # Components to add to certain areas in the interface

Preferences.getInstance().addPreference("info/automatic_update_check", False)

super().__init__(name = "cura-lulzbot", version = self.getComponentVersion("cura_version"), buildtype = CuraBuildType, tray_icon_name = "cura-icon.png")
super().__init__(name = "cura-lulzbot", version = self.getComponentVersion("cura_version"), buildtype = CuraBuildType, tray_icon_name = "cura-icon.png", is_debug_mode = CuraDebugMode,**kwargs)

self.default_theme = "lulzbot"

@@ -246,7 +263,9 @@ class CuraApplication(QtApplication):
"TranslateTool",
"FileLogger",
"XmlMaterialProfile",
"PluginBrowser"
"PluginBrowser",
"PrepareStage",
"MonitorStage"
])
self._physics = None
self._volume = None
@@ -271,6 +290,7 @@ class CuraApplication(QtApplication):
self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
self.getController().contextMenuRequested.connect(self._onContextMenuRequested)
self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivity)

Resources.addType(self.ResourceTypes.QmlFiles, "qml")
Resources.addType(self.ResourceTypes.Firmware, "firmware")
@@ -282,18 +302,23 @@ class CuraApplication(QtApplication):
# We need them to simplify the switching between materials.
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()

empty_definition_changes_container = copy.deepcopy(empty_container)
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
empty_definition_changes_container.addMetaDataEntry("type", "definition_changes")
ContainerRegistry.getInstance().addContainer(empty_definition_changes_container)

empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container._id = "empty_variant"
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.addMetaDataEntry("type", "variant")
ContainerRegistry.getInstance().addContainer(empty_variant_container)

empty_material_container = copy.deepcopy(empty_container)
empty_material_container._id = "empty_material"
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.addMetaDataEntry("type", "material")
ContainerRegistry.getInstance().addContainer(empty_material_container)

empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container._id = "empty_quality"
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.addMetaDataEntry("quality_type", "not_supported")
empty_quality_container.addMetaDataEntry("type", "quality")
@@ -301,12 +326,13 @@ class CuraApplication(QtApplication):
ContainerRegistry.getInstance().addContainer(empty_quality_container)

empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container._id = "empty_quality_changes"
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)

with ContainerRegistry.getInstance().lockFile():
ContainerRegistry.getInstance().load()
ContainerRegistry.getInstance().loadAllMetadata()

# set the setting version for Preferences
preferences = Preferences.getInstance()
@@ -326,11 +352,15 @@ class CuraApplication(QtApplication):
preferences.addPreference("cura/asked_dialog_on_project_save", False)
preferences.addPreference("cura/choice_on_profile_override", "always_ask")
preferences.addPreference("cura/choice_on_open_project", "always_ask")
preferences.addPreference("cura/not_arrange_objects_on_load", False)
preferences.addPreference("cura/use_multi_build_plate", False)

preferences.addPreference("cura/currency", "€")
preferences.addPreference("cura/material_settings", "{}")

preferences.addPreference("view/invert_zoom", False)
preferences.addPreference("view/filter_current_build_plate", False)
preferences.addPreference("cura/sidebar_collapsed", False)

Preferences.getInstance().addPreference("cura/recent_files", "")
self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement")
@@ -426,6 +456,7 @@ class CuraApplication(QtApplication):
self.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._onGlobalContainerChanged()
self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin")
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize

@pyqtSlot(str, result=str)
def getComponentVersion(self, component):
@@ -438,7 +469,6 @@ class CuraApplication(QtApplication):
def needToShowUserAgreement(self):
return self._need_to_show_user_agreement


def setNeedToShowUserAgreement(self, set_value = True):
self._need_to_show_user_agreement = set_value

@@ -446,7 +476,19 @@ class CuraApplication(QtApplication):
@pyqtSlot()
def closeApplication(self):
Logger.log("i", "Close application")
self._main_window.close()
main_window = self.getMainWindow()
if main_window is not None:
main_window.close()
else:
self.exit(0)

## Signal to connect preferences action in QML
showPreferencesWindow = pyqtSignal()

## Show the preferences window
@pyqtSlot()
def showPreferences(self):
self.showPreferencesWindow.emit()

## A reusable dialogbox
#
@@ -527,69 +569,10 @@ class CuraApplication(QtApplication):
if not self._started: # Do not do saving during application start
return

# Lock file for "more" atomically loading and saving to/from config dir.
with ContainerRegistry.getInstance().lockFile():
for instance in ContainerRegistry.getInstance().findInstanceContainers():
if not instance.isDirty():
continue

try:
data = instance.serialize()
except NotImplementedError:
continue
except Exception:
Logger.logException("e", "An exception occurred when serializing container %s", instance.getId())
continue

mime_type = ContainerRegistry.getMimeTypeForContainer(type(instance))
file_name = urllib.parse.quote_plus(instance.getId()) + "." + mime_type.preferredSuffix
instance_type = instance.getMetaDataEntry("type")
path = None
if instance_type == "material":
path = Resources.getStoragePath(self.ResourceTypes.MaterialInstanceContainer, file_name)
elif instance_type == "quality" or instance_type == "quality_changes":
path = Resources.getStoragePath(self.ResourceTypes.QualityInstanceContainer, file_name)
elif instance_type == "user":
path = Resources.getStoragePath(self.ResourceTypes.UserInstanceContainer, file_name)
elif instance_type == "variant":
path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name)
elif instance_type == "definition_changes":
path = Resources.getStoragePath(self.ResourceTypes.DefinitionChangesContainer, file_name)

if path:
instance.setPath(path)
with SaveFile(path, "wt") as f:
f.write(data)

for stack in ContainerRegistry.getInstance().findContainerStacks():
self.saveStack(stack)
ContainerRegistry.getInstance().saveDirtyContainers()

def saveStack(self, stack):
if not stack.isDirty():
return
try:
data = stack.serialize()
except NotImplementedError:
return
except Exception:
Logger.logException("e", "An exception occurred when serializing container %s", stack.getId())
return

mime_type = ContainerRegistry.getMimeTypeForContainer(type(stack))
file_name = urllib.parse.quote_plus(stack.getId()) + "." + mime_type.preferredSuffix

path = None
if isinstance(stack, GlobalStack):
path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name)
elif isinstance(stack, ExtruderStack):
path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name)
else:
path = Resources.getStoragePath(Resources.ContainerStacks, file_name)

stack.setPath(path)
with SaveFile(path, "wt") as f:
f.write(data)

ContainerRegistry.getInstance().saveContainer(stack)

@pyqtSlot(str, result = QUrl)
def getDefaultPath(self, key):
@@ -624,11 +607,10 @@ class CuraApplication(QtApplication):
self._plugins_loaded = True

@classmethod
def addCommandLineOptions(self, parser):
super().addCommandLineOptions(parser)
def addCommandLineOptions(self, parser, parsed_command_line = {}):
super().addCommandLineOptions(parser, parsed_command_line = parsed_command_line)
parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
parser.add_argument("--single-instance", action="store_true", default=False)
parser.add_argument("--headless", action = "store_true", default=False)

# Set up a local socket server which listener which coordinates single instances Curas and accepts commands.
def _setUpSingleInstanceServer(self):
@@ -682,13 +664,16 @@ class CuraApplication(QtApplication):
# This should be called directly before creating an instance of CuraApplication.
# \returns \type{bool} True if the whole Cura app should continue running.
@classmethod
def preStartUp(cls):
def preStartUp(cls, parser = None, parsed_command_line = {}):
# Peek the arguments and look for the 'single-instance' flag.
parser = argparse.ArgumentParser(prog="cura") # pylint: disable=bad-whitespace
CuraApplication.addCommandLineOptions(parser)
parsed_command_line = vars(parser.parse_args())

if "single_instance" in parsed_command_line and parsed_command_line["single_instance"]:
if not parser:
parser = argparse.ArgumentParser(prog = "cura", add_help = False) # pylint: disable=bad-whitespace
CuraApplication.addCommandLineOptions(parser, parsed_command_line = parsed_command_line)
# Important: It is important to keep this line here!
# In Uranium we allow to pass unknown arguments to the final executable or script.
parsed_command_line.update(vars(parser.parse_known_args()[0]))

if parsed_command_line["single_instance"]:
Logger.log("i", "Checking for the presence of an ready running Cura instance.")
single_instance_socket = QLocalSocket()
Logger.log("d", "preStartUp(): full server name: " + single_instance_socket.fullServerName())
@@ -720,21 +705,36 @@ class CuraApplication(QtApplication):
return False
return True

def preRun(self):
# Last check for unknown commandline arguments
parser = self.getCommandlineParser()
parser.add_argument("--help", "-h",
action='store_true',
default = False,
help = "Show this help message and exit."
)
parsed_args = vars(parser.parse_args()) # This won't allow unknown arguments
if parsed_args["help"]:
parser.print_help()
sys.exit(0)

def run(self):
self.preRun()

self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))

self._setUpSingleInstanceServer()

controller = self.getController()

controller.setActiveStage("PrepareStage")
controller.setActiveView("SolidView")

controller.setCameraTool("CameraTool")
controller.setSelectionTool("SelectionTool")

t = controller.getTool("TranslateTool")
if t:
t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis,ToolHandle.ZAxis])
t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis])

Selection.selectionChanged.connect(self.onSelectionChanged)

@@ -770,18 +770,27 @@ class CuraApplication(QtApplication):
qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, "ExtruderManager", self.getExtruderManager)
qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager)
qmlRegisterSingletonType(MaterialManager, "Cura", 1, 0, "MaterialManager", self.getMaterialManager)
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager", self.getSettingInheritanceManager)
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)

qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager",
self.getSettingInheritanceManager)
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager",
self.getSimpleModeSettingsManager)

qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 2, "ObjectsModel", self.getObjectsModel)
qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 2, "BuildPlateModel", self.getBuildPlateModel)
qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 2, "SceneController", self.getCuraSceneController)

qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)

self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles))

run_headless = self.getCommandLineOption("headless", False)
if not run_headless:
run_without_gui = self.getCommandLineOption("headless", False)
if not run_without_gui:
self.initializeEngine()
controller.setActiveStage("PrepareStage")

if run_headless or self._engine.rootObjects:
if run_without_gui or self._engine.rootObjects:
self.closeSplash()

for file_name in self.getCommandLineOption("file", []):
@@ -821,7 +830,7 @@ class CuraApplication(QtApplication):
def exitAllowed(self):
return self._exit_allowed

def getMachineManager(self, *args):
def getMachineManager(self, *args) -> MachineManager:
if self._machine_manager is None:
self._machine_manager = MachineManager.createMachineManager()
return self._machine_manager
@@ -836,6 +845,22 @@ class CuraApplication(QtApplication):
self._material_manager = MaterialManager.createMaterialManager()
return self._material_manager

def getObjectsModel(self, *args):
if self._object_manager is None:
self._object_manager = ObjectsModel.createObjectsModel()
return self._object_manager

def getBuildPlateModel(self, *args):
if self._build_plate_model is None:
self._build_plate_model = BuildPlateModel.createBuildPlateModel()

return self._build_plate_model

def getCuraSceneController(self, *args):
if self._cura_scene_controller is None:
self._cura_scene_controller = CuraSceneController.createCuraSceneController()
return self._cura_scene_controller

def getSettingInheritanceManager(self, *args):
if self._setting_inheritance_manager is None:
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
@@ -880,6 +905,7 @@ class CuraApplication(QtApplication):

qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")

qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer")
qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel)
@@ -969,12 +995,18 @@ class CuraApplication(QtApplication):
def getSceneBoundingBoxString(self):
return self._i18n_catalog.i18nc("@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm.", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_bounding_box.width.item(), 'depth': self._scene_bounding_box.depth.item(), 'height' : self._scene_bounding_box.height.item()}

## Update scene bounding box for current build plate
def updatePlatformActivity(self, node = None):
count = 0
scene_bounding_box = None
is_block_slicing_node = False
active_build_plate = self.getBuildPlateModel().activeBuildPlate
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode or (not node.getMeshData() and not node.callDecoration("getLayerData")):
if (
not issubclass(type(node), CuraSceneNode) or
(not node.getMeshData() and not node.callDecoration("getLayerData")) or
(node.callDecoration("getBuildPlateNumber") != active_build_plate)):

continue
if node.callDecoration("isBlockSlicing"):
is_block_slicing_node = True
@@ -1000,7 +1032,7 @@ class CuraApplication(QtApplication):
if not scene_bounding_box:
scene_bounding_box = AxisAlignedBox.Null

if repr(self._scene_bounding_box) != repr(scene_bounding_box) and scene_bounding_box.isValid():
if repr(self._scene_bounding_box) != repr(scene_bounding_box):
self._scene_bounding_box = scene_bounding_box
self.sceneBoundingBoxChanged.emit()

@@ -1097,7 +1129,7 @@ class CuraApplication(QtApplication):

Selection.clear()
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@@ -1105,21 +1137,29 @@ class CuraApplication(QtApplication):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
continue # i.e. node with layer data
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
continue # i.e. node with layer data

Selection.add(node)

## Delete all nodes containing mesh data in the scene.
# \param only_selectable. Set this to False to delete objects from all build plates
@pyqtSlot()
def deleteAll(self):
def deleteAll(self, only_selectable = True):
Logger.log("i", "Clearing scene")
if not self.getController().getToolsEnabled():
return

nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not isinstance(node, SceneNode):
continue
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if only_selectable and not node.isSelectable():
continue
if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData") and not node.callDecoration("isGroup"):
continue # Only remove nodes that are selectable.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
nodes.append(node)
@@ -1129,16 +1169,19 @@ class CuraApplication(QtApplication):
for node in nodes:
op.addOperation(RemoveSceneNodeOperation(node))

# Reset the print information
self.getController().getScene().sceneChanged.emit(node)

op.push()
Selection.clear()

## Reset all translation on nodes with mesh data.
## Reset all translation on nodes with mesh data.
@pyqtSlot()
def resetAllTranslation(self):
Logger.log("i", "Resetting all scene translations")
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@@ -1166,13 +1209,13 @@ class CuraApplication(QtApplication):
Logger.log("i", "Resetting all scene transformations")
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
continue # i.e. node with layer data
nodes.append(node)

@@ -1190,20 +1233,44 @@ class CuraApplication(QtApplication):

## Arrange all objects.
@pyqtSlot()
def arrangeAll(self):
def arrangeObjectsToAllBuildPlates(self):
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
continue # i.e. node with layer data
# Skip nodes that are too big
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
nodes.append(node)
job = ArrangeObjectsAllBuildPlatesJob(nodes)
job.start()
self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate

# Single build plate
@pyqtSlot()
def arrangeAll(self):
nodes = []
active_build_plate = self.getBuildPlateModel().activeBuildPlate
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
continue # i.e. node with layer data