ProfilesPage.qml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  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. Item
  10. {
  11. id: base
  12. property var resetEnabled: false // Keep PreferencesDialog happy
  13. property var extrudersModel: CuraApplication.getExtrudersModel()
  14. property var qualityManagementModel: CuraApplication.getQualityManagementModel()
  15. UM.I18nCatalog { id: catalog; name: "cura"; }
  16. Label
  17. {
  18. id: titleLabel
  19. anchors
  20. {
  21. top: parent.top
  22. left: parent.left
  23. right: parent.right
  24. margins: 5 * screenScaleFactor
  25. }
  26. font.pointSize: 18
  27. text: catalog.i18nc("@title:tab", "Profiles")
  28. }
  29. property var hasCurrentItem: base.currentItem != null
  30. property var currentItem:
  31. {
  32. var current_index = qualityListView.currentIndex;
  33. return (current_index == -1) ? null : base.qualityManagementModel.getItem(current_index);
  34. }
  35. property var currentItemName: hasCurrentItem ? base.currentItem.name : ""
  36. property var currentItemDisplayName: hasCurrentItem ? base.qualityManagementModel.getQualityItemDisplayName(base.currentItem) : ""
  37. property var isCurrentItemActivated:
  38. {
  39. if (!base.currentItem)
  40. {
  41. return false;
  42. }
  43. if (base.currentItem.is_read_only)
  44. {
  45. return (base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName) && (base.currentItem.intent_category == Cura.MachineManager.activeIntentCategory);
  46. }
  47. else
  48. {
  49. return base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName;
  50. }
  51. }
  52. property var canCreateProfile:
  53. {
  54. return isCurrentItemActivated && Cura.MachineManager.hasUserSettings;
  55. }
  56. Row // Button Row
  57. {
  58. id: buttonRow
  59. anchors
  60. {
  61. left: parent.left
  62. right: parent.right
  63. top: titleLabel.bottom
  64. }
  65. height: childrenRect.height
  66. // Activate button
  67. Button
  68. {
  69. id: activateMenuButton
  70. text: catalog.i18nc("@action:button", "Activate")
  71. icon.name: "list-activate"
  72. enabled: !isCurrentItemActivated && base.currentItem
  73. onClicked:
  74. {
  75. if(base.currentItem.is_read_only)
  76. {
  77. Cura.IntentManager.selectIntent(base.currentItem.intent_category, base.currentItem.quality_type);
  78. }
  79. else
  80. {
  81. Cura.MachineManager.setQualityChangesGroup(base.currentItem.quality_changes_group);
  82. }
  83. }
  84. }
  85. // Create button
  86. Button
  87. {
  88. id: createMenuButton
  89. text: catalog.i18nc("@label", "Create")
  90. icon.name: "list-add"
  91. enabled: base.canCreateProfile && !Cura.MachineManager.stacksHaveErrors
  92. visible: base.canCreateProfile
  93. onClicked:
  94. {
  95. createQualityDialog.object = Cura.ContainerManager.makeUniqueName(base.currentItem.name);
  96. createQualityDialog.open();
  97. createQualityDialog.selectText();
  98. }
  99. }
  100. // Duplicate button
  101. Button
  102. {
  103. id: duplicateMenuButton
  104. text: catalog.i18nc("@label", "Duplicate")
  105. icon.name: "list-add"
  106. enabled: !base.canCreateProfile
  107. visible: !base.canCreateProfile
  108. onClicked:
  109. {
  110. duplicateQualityDialog.object = Cura.ContainerManager.makeUniqueName(base.currentItem.name);
  111. duplicateQualityDialog.open();
  112. duplicateQualityDialog.selectText();
  113. }
  114. }
  115. // Remove button
  116. Button
  117. {
  118. id: removeMenuButton
  119. text: catalog.i18nc("@action:button", "Remove")
  120. icon.name: "list-remove"
  121. enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated
  122. onClicked:
  123. {
  124. forceActiveFocus();
  125. confirmRemoveQualityDialog.open();
  126. }
  127. }
  128. // Rename button
  129. Button
  130. {
  131. id: renameMenuButton
  132. text: catalog.i18nc("@action:button", "Rename")
  133. icon.name: "edit-rename"
  134. enabled: base.hasCurrentItem && !base.currentItem.is_read_only
  135. onClicked:
  136. {
  137. renameQualityDialog.object = base.currentItem.name;
  138. renameQualityDialog.open();
  139. renameQualityDialog.selectText();
  140. }
  141. }
  142. // Import button
  143. Button
  144. {
  145. id: importMenuButton
  146. text: catalog.i18nc("@action:button", "Import")
  147. icon.name: "document-import"
  148. onClicked:
  149. {
  150. importDialog.open();
  151. }
  152. }
  153. // Export button
  154. Button
  155. {
  156. id: exportMenuButton
  157. text: catalog.i18nc("@action:button", "Export")
  158. icon.name: "document-export"
  159. enabled: base.hasCurrentItem && !base.currentItem.is_read_only
  160. onClicked:
  161. {
  162. exportDialog.open();
  163. }
  164. }
  165. }
  166. // Click create profile from ... in Profile context menu
  167. signal createProfile()
  168. onCreateProfile:
  169. {
  170. createQualityDialog.object = Cura.ContainerManager.makeUniqueName(Cura.MachineManager.activeQualityOrQualityChangesName);
  171. createQualityDialog.open();
  172. createQualityDialog.selectText();
  173. }
  174. // Dialog to request a name when creating a new profile
  175. UM.RenameDialog
  176. {
  177. id: createQualityDialog
  178. title: catalog.i18nc("@title:window", "Create Profile")
  179. object: "<new name>"
  180. explanation: catalog.i18nc("@info", "Please provide a name for this profile.")
  181. onAccepted:
  182. {
  183. base.newQualityNameToSelect = newName; // We want to switch to the new profile once it's created
  184. base.toActivateNewQuality = true;
  185. base.qualityManagementModel.createQualityChanges(newName);
  186. }
  187. }
  188. property string newQualityNameToSelect: ""
  189. property bool toActivateNewQuality: false
  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. qualityListView.currentIndex = newIdx;
  225. // Reset states
  226. base.newQualityNameToSelect = "";
  227. base.toActivateNewQuality = false;
  228. }
  229. }
  230. // Dialog to request a name when duplicating a new profile
  231. UM.RenameDialog
  232. {
  233. id: duplicateQualityDialog
  234. title: catalog.i18nc("@title:window", "Duplicate Profile")
  235. object: "<new name>"
  236. onAccepted:
  237. {
  238. base.qualityManagementModel.duplicateQualityChanges(newName, base.currentItem);
  239. }
  240. }
  241. // Confirmation dialog for removing a profile
  242. MessageDialog
  243. {
  244. id: confirmRemoveQualityDialog
  245. icon: StandardIcon.Question;
  246. title: catalog.i18nc("@title:window", "Confirm Remove")
  247. text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItemName)
  248. standardButtons: StandardButton.Yes | StandardButton.No
  249. modality: Qt.ApplicationModal
  250. onYes:
  251. {
  252. base.qualityManagementModel.removeQualityChangesGroup(base.currentItem.quality_changes_group);
  253. // reset current item to the first if available
  254. qualityListView.currentIndex = -1; // Reset selection.
  255. }
  256. }
  257. // Dialog to rename a quality profile
  258. UM.RenameDialog
  259. {
  260. id: renameQualityDialog
  261. title: catalog.i18nc("@title:window", "Rename Profile")
  262. object: "<new name>"
  263. onAccepted:
  264. {
  265. var actualNewName = base.qualityManagementModel.renameQualityChangesGroup(base.currentItem.quality_changes_group, newName);
  266. base.newQualityNameToSelect = actualNewName; // Select the new name after the model gets updated
  267. }
  268. }
  269. // Dialog for importing a quality profile
  270. FileDialog
  271. {
  272. id: importDialog
  273. title: catalog.i18nc("@title:window", "Import Profile")
  274. selectExisting: true
  275. nameFilters: base.qualityManagementModel.getFileNameFilters("profile_reader")
  276. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  277. onAccepted:
  278. {
  279. var result = Cura.ContainerManager.importProfile(fileUrl);
  280. messageDialog.title = catalog.i18nc("@title:window", "Import Profile")
  281. messageDialog.text = result.message;
  282. messageDialog.open();
  283. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  284. }
  285. }
  286. // Dialog for exporting a quality profile
  287. FileDialog
  288. {
  289. id: exportDialog
  290. title: catalog.i18nc("@title:window", "Export Profile")
  291. selectExisting: false
  292. nameFilters: base.qualityManagementModel.getFileNameFilters("profile_writer")
  293. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  294. onAccepted:
  295. {
  296. var result = Cura.ContainerManager.exportQualityChangesGroup(base.currentItem.quality_changes_group,
  297. fileUrl, selectedNameFilter);
  298. if (result && result.status == "error")
  299. {
  300. messageDialog.title = catalog.i18nc("@title:window", "Export Profile")
  301. messageDialog.text = result.message;
  302. messageDialog.open();
  303. }
  304. // else pop-up Message thing from python code
  305. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  306. }
  307. }
  308. //Dialogue box for showing the result of importing or exporting profiles.
  309. UM.MessageDialog
  310. {
  311. id: messageDialog
  312. standardButtons: Dialog.Ok
  313. }
  314. Item
  315. {
  316. id: contentsItem
  317. anchors
  318. {
  319. top: titleLabel.bottom
  320. left: parent.left
  321. right: parent.right
  322. bottom: parent.bottom
  323. margins: 5 * screenScaleFactor
  324. bottomMargin: 0
  325. }
  326. clip: true
  327. }
  328. Item
  329. {
  330. anchors
  331. {
  332. top: buttonRow.bottom
  333. topMargin: UM.Theme.getSize("default_margin").height
  334. left: parent.left
  335. right: parent.right
  336. bottom: parent.bottom
  337. }
  338. SystemPalette
  339. {
  340. id: palette
  341. }
  342. Label
  343. {
  344. id: captionLabel
  345. anchors
  346. {
  347. top: parent.top
  348. left: parent.left
  349. }
  350. visible: text != ""
  351. text: catalog.i18nc("@label %1 is printer name", "Printer: %1").arg(Cura.MachineManager.activeMachine.name)
  352. width: profileBackground.width
  353. elide: Text.ElideRight
  354. }
  355. Rectangle
  356. {
  357. id: profileBackground
  358. anchors
  359. {
  360. top: captionLabel.visible ? captionLabel.bottom : parent.top
  361. topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0
  362. bottom: parent.bottom
  363. left: parent.left
  364. }
  365. width: (parent.width * 0.4) | 0
  366. color: palette.light
  367. ListView
  368. {
  369. id: qualityListView
  370. anchors.fill: parent
  371. ScrollBar.vertical: UM.ScrollBar
  372. {
  373. id: profileScrollBar
  374. }
  375. clip: true
  376. model: base.qualityManagementModel
  377. Component.onCompleted:
  378. {
  379. var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName;
  380. // Select the required quality name if given
  381. for (var idx = 0; idx < base.qualityManagementModel.count; idx++)
  382. {
  383. var item = base.qualityManagementModel.getItem(idx);
  384. if (item.name == selectedItemName)
  385. {
  386. currentIndex = idx;
  387. break;
  388. }
  389. }
  390. }
  391. section.property: "section_name"
  392. section.delegate: Rectangle
  393. {
  394. height: childrenRect.height
  395. Label
  396. {
  397. anchors.left: parent.left
  398. anchors.leftMargin: UM.Theme.getSize("default_lining").width
  399. text: section
  400. font.bold: true
  401. }
  402. }
  403. delegate: Rectangle
  404. {
  405. width: profileBackground.width - profileScrollBar.width
  406. height: childrenRect.height
  407. // Added this property to identify custom profiles in automated system tests (Squish)
  408. property bool isReadOnly: model.is_read_only
  409. property bool isCurrentItem: ListView.isCurrentItem
  410. color: isCurrentItem ? palette.highlight : (model.index % 2) ? palette.base : palette.alternateBase
  411. Label
  412. {
  413. anchors.left: parent.left
  414. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  415. anchors.right: parent.right
  416. width: Math.floor((parent.width * 0.8))
  417. text: model.name
  418. elide: Text.ElideRight
  419. font.italic:
  420. {
  421. if (model.is_read_only)
  422. {
  423. // For built-in qualities, it needs to match both the intent category and the quality name
  424. return model.name == Cura.MachineManager.activeQualityOrQualityChangesName && model.intent_category == Cura.MachineManager.activeIntentCategory
  425. }
  426. else
  427. {
  428. // For custom qualities, it only needs to match the name
  429. return model.name == Cura.MachineManager.activeQualityOrQualityChangesName
  430. }
  431. }
  432. color: parent.isCurrentItem ? palette.highlightedText : palette.text
  433. }
  434. MouseArea
  435. {
  436. anchors.fill: parent
  437. onClicked:
  438. {
  439. parent.ListView.view.currentIndex = model.index;
  440. }
  441. }
  442. }
  443. }
  444. }
  445. // details panel on the right
  446. Item
  447. {
  448. id: detailsPanel
  449. anchors
  450. {
  451. left: profileBackground.right
  452. leftMargin: UM.Theme.getSize("default_margin").width
  453. top: parent.top
  454. bottom: parent.bottom
  455. right: parent.right
  456. }
  457. Column
  458. {
  459. id: detailsPanelHeaderColumn
  460. anchors
  461. {
  462. left: parent.left
  463. right: parent.right
  464. top: parent.top
  465. }
  466. spacing: UM.Theme.getSize("default_margin").height
  467. visible: base.currentItem != null
  468. Item // Profile title Label
  469. {
  470. id: profileName
  471. width: parent.width
  472. height: childrenRect.height
  473. Label
  474. {
  475. anchors.left: parent.left
  476. anchors.right: parent.right
  477. text: base.currentItemDisplayName
  478. font: UM.Theme.getFont("large_bold")
  479. elide: Text.ElideRight
  480. renderType: Text.NativeRendering
  481. }
  482. }
  483. Flow
  484. {
  485. id: currentSettingsActions
  486. width: parent.width
  487. visible: base.hasCurrentItem && base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName && base.currentItem.intent_category == Cura.MachineManager.activeIntentCategory
  488. Button
  489. {
  490. text: catalog.i18nc("@action:button", "Update profile with current settings/overrides")
  491. enabled: Cura.MachineManager.hasUserSettings && qualityListView.currentItem && !qualityListView.currentItem.is_read_only
  492. onClicked: Cura.ContainerManager.updateQualityChanges()
  493. }
  494. Button
  495. {
  496. text: catalog.i18nc("@action:button", "Discard current changes");
  497. enabled: Cura.MachineManager.hasUserSettings
  498. onClicked: Cura.ContainerManager.clearUserContainers();
  499. }
  500. }
  501. Label
  502. {
  503. id: defaultsMessage
  504. visible: false
  505. text: catalog.i18nc("@action:label", "This profile uses the defaults specified by the printer, so it has no settings/overrides in the list below.")
  506. wrapMode: Text.WordWrap
  507. width: parent.width
  508. }
  509. Label
  510. {
  511. id: noCurrentSettingsMessage
  512. visible: base.isCurrentItemActivated && !Cura.MachineManager.hasUserSettings
  513. text: catalog.i18nc("@action:label", "Your current settings match the selected profile.")
  514. wrapMode: Text.WordWrap
  515. width: parent.width
  516. }
  517. UM.TabRow
  518. {
  519. id: profileExtruderTabs
  520. UM.TabRowButton //One extra tab for the global settings.
  521. {
  522. text: catalog.i18nc("@title:tab", "Global Settings")
  523. }
  524. Repeater
  525. {
  526. model: base.extrudersModel
  527. UM.TabRowButton
  528. {
  529. text: model.name
  530. }
  531. }
  532. }
  533. }
  534. Cura.ProfileOverview
  535. {
  536. anchors
  537. {
  538. top: detailsPanelHeaderColumn.bottom
  539. left: parent.left
  540. right: parent.right
  541. bottom: parent.bottom
  542. }
  543. visible: detailsPanelHeaderColumn.visible
  544. qualityItem: base.currentItem
  545. extruderPosition: profileExtruderTabs.currentIndex - 1
  546. }
  547. }
  548. }
  549. }