ProfilesPage.qml 16 KB

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