ProfilesPage.qml 15 KB

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