ProfilesPage.qml 15 KB

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