ProfilesPage.qml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. //Copyright (c) 2022 Ultimaker B.V.
  2. //Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 2.15
  5. import QtQuick.Layouts 1.3
  6. import QtQuick.Dialogs 1.2
  7. import UM 1.5 as UM
  8. import Cura 1.6 as Cura
  9. UM.ManagementPage
  10. {
  11. id: base
  12. property var extrudersModel: CuraApplication.getExtrudersModel()
  13. property var qualityManagementModel: CuraApplication.getQualityManagementModel()
  14. property bool hasCurrentItem: base.currentItem != null
  15. property var currentItem: objectList.currentIndex == -1 ? null : base.qualityManagementModel.getItem(objectList.currentIndex)
  16. property string currentItemName: hasCurrentItem ? base.currentItem.name : ""
  17. property string currentItemDisplayName: hasCurrentItem ? base.qualityManagementModel.getQualityItemDisplayName(base.currentItem) : ""
  18. property bool isCurrentItemActivated:
  19. {
  20. if (!base.currentItem)
  21. {
  22. return false;
  23. }
  24. if (base.currentItem.is_read_only)
  25. {
  26. return (base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName) && (base.currentItem.intent_category == Cura.MachineManager.activeIntentCategory);
  27. }
  28. else
  29. {
  30. return base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName;
  31. }
  32. }
  33. property bool canCreateProfile: Cura.MachineManager.hasUserSettings
  34. signal createProfile() // Click create profile from ... in Profile context menu
  35. property string newQualityNameToSelect: ""
  36. property bool toActivateNewQuality: false
  37. onHamburgeButtonClicked: menu.popup(content_item, content_item.width - menu.width, hamburger_button.height)
  38. onCreateProfile:
  39. {
  40. createQualityDialog.object = Cura.ContainerManager.makeUniqueName(Cura.MachineManager.activeQualityOrQualityChangesName);
  41. createQualityDialog.open();
  42. createQualityDialog.selectText();
  43. }
  44. title: catalog.i18nc("@title:tab", "Profiles")
  45. scrollviewCaption: catalog.i18nc("@label", "Profiles compatible with active printer:") + "<br><b>" + Cura.MachineManager.activeMachine.name + "</b>"
  46. hamburgerButtonVisible: hasCurrentItem
  47. sectionRole: "section_name"
  48. model: qualityManagementModel
  49. buttons: [
  50. Cura.SecondaryButton
  51. {
  52. text: catalog.i18nc("@action:button", "Import")
  53. onClicked:importDialog.open()
  54. },
  55. Cura.SecondaryButton
  56. {
  57. id: createMenuButton
  58. text: catalog.i18nc("@action:button", "Create new")
  59. enabled: !Cura.MachineManager.stacksHaveErrors
  60. visible: base.canCreateProfile
  61. tooltip: catalog.i18nc("@action:tooltip", "Create new profile from current settings/overrides")
  62. onClicked:
  63. {
  64. createQualityDialog.object = Cura.ContainerManager.makeUniqueName("<new name>")
  65. createQualityDialog.open()
  66. createQualityDialog.selectText()
  67. }
  68. }
  69. ]
  70. Item
  71. {
  72. id: content_item
  73. anchors.fill: parent
  74. // This connection makes sure that we will switch to the correct quality after the model gets updated
  75. Connections
  76. {
  77. target: base.qualityManagementModel
  78. function onItemsChanged()
  79. {
  80. var toSelectItemName = base.currentItem == null ? "" : base.currentItem.name;
  81. if (newQualityNameToSelect != "")
  82. {
  83. toSelectItemName = newQualityNameToSelect;
  84. }
  85. var newIdx = -1; // Default to nothing if nothing can be found
  86. if (toSelectItemName != "")
  87. {
  88. // Select the required quality name if given
  89. for (var idx = 0; idx < base.qualityManagementModel.count; ++idx)
  90. {
  91. var item = base.qualityManagementModel.getItem(idx);
  92. if (item && item.name == toSelectItemName)
  93. {
  94. // Switch to the newly created profile if needed
  95. newIdx = idx;
  96. if (base.toActivateNewQuality)
  97. {
  98. // Activate this custom quality if required
  99. if(item.quality_changes_group)
  100. {
  101. Cura.MachineManager.setQualityChangesGroup(item.quality_changes_group);
  102. }
  103. }
  104. break;
  105. }
  106. }
  107. }
  108. objectList.currentIndex = newIdx;
  109. // Reset states
  110. base.newQualityNameToSelect = "";
  111. base.toActivateNewQuality = false;
  112. }
  113. }
  114. Cura.MessageDialog
  115. {
  116. id: messageDialog
  117. standardButtons: Dialog.Ok
  118. }
  119. // Dialog to request a name when creating a new profile
  120. Cura.RenameDialog
  121. {
  122. id: createQualityDialog
  123. title: catalog.i18nc("@title:window", "Create Profile")
  124. object: "<new name>"
  125. explanation: catalog.i18nc("@info", "Please provide a name for this profile.")
  126. onAccepted:
  127. {
  128. base.newQualityNameToSelect = newName; // We want to switch to the new profile once it's created
  129. base.toActivateNewQuality = true;
  130. base.qualityManagementModel.createQualityChanges(newName);
  131. }
  132. }
  133. Cura.Menu
  134. {
  135. id: menu
  136. Cura.MenuItem
  137. {
  138. text: catalog.i18nc("@action:button", "Activate")
  139. enabled: !isCurrentItemActivated && base.currentItem
  140. onTriggered:
  141. {
  142. if(base.currentItem.is_read_only)
  143. {
  144. Cura.IntentManager.selectIntent(base.currentItem.intent_category, base.currentItem.quality_type)
  145. }
  146. else
  147. {
  148. Cura.MachineManager.setQualityChangesGroup(base.currentItem.quality_changes_group)
  149. }
  150. }
  151. }
  152. Cura.MenuItem
  153. {
  154. text: catalog.i18nc("@action:button", "Duplicate")
  155. enabled: base.hasCurrentItem
  156. onTriggered:
  157. {
  158. forceActiveFocus()
  159. duplicateQualityDialog.open()
  160. }
  161. }
  162. Cura.MenuItem
  163. {
  164. text: catalog.i18nc("@action:button", "Remove")
  165. enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated
  166. onTriggered:
  167. {
  168. forceActiveFocus()
  169. confirmRemoveQualityDialog.open()
  170. }
  171. }
  172. Cura.MenuItem
  173. {
  174. text: catalog.i18nc("@action:button", "Rename")
  175. enabled: base.hasCurrentItem && !base.currentItem.is_read_only
  176. onTriggered:
  177. {
  178. renameQualityDialog.object = base.currentItem.name
  179. renameQualityDialog.open()
  180. renameQualityDialog.selectText()
  181. }
  182. }
  183. Cura.MenuItem
  184. {
  185. text: catalog.i18nc("@action:button", "Export")
  186. enabled: base.hasCurrentItem && !base.currentItem.is_read_only
  187. onTriggered: exportDialog.open()
  188. }
  189. }
  190. // Dialog for exporting a quality profile
  191. FileDialog
  192. {
  193. id: exportDialog
  194. title: catalog.i18nc("@title:window", "Export Profile")
  195. selectExisting: false
  196. nameFilters: base.qualityManagementModel.getFileNameFilters("profile_writer")
  197. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  198. onAccepted:
  199. {
  200. var result = Cura.ContainerManager.exportQualityChangesGroup(base.currentItem.quality_changes_group,
  201. fileUrl, selectedNameFilter);
  202. if (result && result.status == "error")
  203. {
  204. messageDialog.title = catalog.i18nc("@title:window", "Export Profile")
  205. messageDialog.text = result.message;
  206. messageDialog.open();
  207. }
  208. // else pop-up Message thing from python code
  209. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  210. }
  211. }
  212. // Dialog to request a name when duplicating a new profile
  213. Cura.RenameDialog
  214. {
  215. id: duplicateQualityDialog
  216. title: catalog.i18nc("@title:window", "Duplicate Profile")
  217. object: "<new name>"
  218. onAccepted: base.qualityManagementModel.duplicateQualityChanges(newName, base.currentItem)
  219. }
  220. // Confirmation dialog for removing a profile
  221. Cura.MessageDialog
  222. {
  223. id: confirmRemoveQualityDialog
  224. title: catalog.i18nc("@title:window", "Confirm Remove")
  225. text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItemName)
  226. standardButtons: StandardButton.Yes | StandardButton.No
  227. modal: true
  228. onAccepted:
  229. {
  230. base.qualityManagementModel.removeQualityChangesGroup(base.currentItem.quality_changes_group);
  231. // reset current item to the first if available
  232. qualityListView.currentIndex = -1; // Reset selection.
  233. }
  234. }
  235. // Dialog to rename a quality profile
  236. Cura.RenameDialog
  237. {
  238. id: renameQualityDialog
  239. title: catalog.i18nc("@title:window", "Rename Profile")
  240. object: "<new name>"
  241. onAccepted:
  242. {
  243. var actualNewName = base.qualityManagementModel.renameQualityChangesGroup(base.currentItem.quality_changes_group, newName);
  244. base.newQualityNameToSelect = actualNewName; // Select the new name after the model gets updated
  245. }
  246. }
  247. // Dialog for importing a quality profile
  248. FileDialog
  249. {
  250. id: importDialog
  251. title: catalog.i18nc("@title:window", "Import Profile")
  252. selectExisting: true
  253. nameFilters: base.qualityManagementModel.getFileNameFilters("profile_reader")
  254. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  255. onAccepted:
  256. {
  257. var result = Cura.ContainerManager.importProfile(fileUrl);
  258. messageDialog.title = catalog.i18nc("@title:window", "Import Profile")
  259. messageDialog.text = result.message;
  260. messageDialog.open();
  261. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  262. }
  263. }
  264. Column
  265. {
  266. id: detailsPanelHeaderColumn
  267. anchors
  268. {
  269. left: parent.left
  270. right: parent.right
  271. top: parent.top
  272. }
  273. spacing: UM.Theme.getSize("default_margin").height
  274. visible: base.currentItem != null
  275. UM.Label
  276. {
  277. anchors.left: parent.left
  278. anchors.right: parent.right
  279. text: base.currentItemDisplayName
  280. font: UM.Theme.getFont("large_bold")
  281. elide: Text.ElideRight
  282. }
  283. Flow
  284. {
  285. id: currentSettingsActions
  286. width: parent.width
  287. visible: base.hasCurrentItem && base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName && base.currentItem.intent_category == Cura.MachineManager.activeIntentCategory
  288. Cura.SecondaryButton
  289. {
  290. text: catalog.i18nc("@action:button", "Update profile.")
  291. enabled: Cura.MachineManager.hasUserSettings && objectList.currentIndex && !objectList.currentIndex.is_read_only
  292. onClicked: Cura.ContainerManager.updateQualityChanges()
  293. tooltip: catalog.i18nc("@action:tooltip", "Update profile with current settings/overrides")
  294. }
  295. Cura.SecondaryButton
  296. {
  297. text: catalog.i18nc("@action:button", "Discard current changes")
  298. enabled: Cura.MachineManager.hasUserSettings
  299. onClicked: Cura.ContainerManager.clearUserContainers()
  300. }
  301. }
  302. UM.Label
  303. {
  304. id: defaultsMessage
  305. visible: false
  306. text: catalog.i18nc("@action:label", "This profile uses the defaults specified by the printer, so it has no settings/overrides in the list below.")
  307. width: parent.width
  308. }
  309. UM.Label
  310. {
  311. id: noCurrentSettingsMessage
  312. visible: base.isCurrentItemActivated && !Cura.MachineManager.hasUserSettings
  313. text: catalog.i18nc("@action:label", "Your current settings match the selected profile.")
  314. width: parent.width
  315. }
  316. UM.TabRow
  317. {
  318. id: profileExtruderTabs
  319. UM.TabRowButton // One extra tab for the global settings.
  320. {
  321. text: catalog.i18nc("@title:tab", "Global Settings")
  322. }
  323. Repeater
  324. {
  325. model: base.extrudersModel
  326. UM.TabRowButton
  327. {
  328. text: model.name
  329. }
  330. }
  331. }
  332. }
  333. Rectangle
  334. {
  335. color: UM.Theme.getColor("main_background")
  336. anchors
  337. {
  338. top: detailsPanelHeaderColumn.bottom
  339. topMargin: -UM.Theme.getSize("default_lining").width
  340. left: parent.left
  341. right: parent.right
  342. bottom: parent.bottom
  343. }
  344. border.width: UM.Theme.getSize("default_lining").width
  345. border.color: UM.Theme.getColor("thick_lining")
  346. visible: base.hasCurrentItem
  347. }
  348. Cura.ProfileOverview
  349. {
  350. anchors
  351. {
  352. top: detailsPanelHeaderColumn.bottom
  353. margins: UM.Theme.getSize("default_margin").height
  354. left: parent.left
  355. right: parent.right
  356. bottom: parent.bottom
  357. }
  358. visible: detailsPanelHeaderColumn.visible
  359. qualityItem: base.currentItem
  360. extruderPosition: profileExtruderTabs.currentIndex - 1
  361. }
  362. }
  363. }