ProfilesPage.qml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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. UM.MessageDialog
  243. {
  244. id: confirmRemoveQualityDialog
  245. title: catalog.i18nc("@title:window", "Confirm Remove")
  246. text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItemName)
  247. standardButtons: StandardButton.Yes | StandardButton.No
  248. modal: true
  249. onAccepted:
  250. {
  251. base.qualityManagementModel.removeQualityChangesGroup(base.currentItem.quality_changes_group);
  252. // reset current item to the first if available
  253. qualityListView.currentIndex = -1; // Reset selection.
  254. }
  255. }
  256. // Dialog to rename a quality profile
  257. UM.RenameDialog
  258. {
  259. id: renameQualityDialog
  260. title: catalog.i18nc("@title:window", "Rename Profile")
  261. object: "<new name>"
  262. onAccepted:
  263. {
  264. var actualNewName = base.qualityManagementModel.renameQualityChangesGroup(base.currentItem.quality_changes_group, newName);
  265. base.newQualityNameToSelect = actualNewName; // Select the new name after the model gets updated
  266. }
  267. }
  268. // Dialog for importing a quality profile
  269. FileDialog
  270. {
  271. id: importDialog
  272. title: catalog.i18nc("@title:window", "Import Profile")
  273. selectExisting: true
  274. nameFilters: base.qualityManagementModel.getFileNameFilters("profile_reader")
  275. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  276. onAccepted:
  277. {
  278. var result = Cura.ContainerManager.importProfile(fileUrl);
  279. messageDialog.title = catalog.i18nc("@title:window", "Import Profile")
  280. messageDialog.text = result.message;
  281. messageDialog.open();
  282. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  283. }
  284. }
  285. // Dialog for exporting a quality profile
  286. FileDialog
  287. {
  288. id: exportDialog
  289. title: catalog.i18nc("@title:window", "Export Profile")
  290. selectExisting: false
  291. nameFilters: base.qualityManagementModel.getFileNameFilters("profile_writer")
  292. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  293. onAccepted:
  294. {
  295. var result = Cura.ContainerManager.exportQualityChangesGroup(base.currentItem.quality_changes_group,
  296. fileUrl, selectedNameFilter);
  297. if (result && result.status == "error")
  298. {
  299. messageDialog.title = catalog.i18nc("@title:window", "Export Profile")
  300. messageDialog.text = result.message;
  301. messageDialog.open();
  302. }
  303. // else pop-up Message thing from python code
  304. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  305. }
  306. }
  307. //Dialogue box for showing the result of importing or exporting profiles.
  308. UM.MessageDialog
  309. {
  310. id: messageDialog
  311. standardButtons: Dialog.Ok
  312. }
  313. Item
  314. {
  315. id: contentsItem
  316. anchors
  317. {
  318. top: titleLabel.bottom
  319. left: parent.left
  320. right: parent.right
  321. bottom: parent.bottom
  322. margins: 5 * screenScaleFactor
  323. bottomMargin: 0
  324. }
  325. clip: true
  326. }
  327. Item
  328. {
  329. anchors
  330. {
  331. top: buttonRow.bottom
  332. topMargin: UM.Theme.getSize("default_margin").height
  333. left: parent.left
  334. right: parent.right
  335. bottom: parent.bottom
  336. }
  337. SystemPalette
  338. {
  339. id: palette
  340. }
  341. Label
  342. {
  343. id: captionLabel
  344. anchors
  345. {
  346. top: parent.top
  347. left: parent.left
  348. }
  349. visible: text != ""
  350. text: catalog.i18nc("@label %1 is printer name", "Printer: %1").arg(Cura.MachineManager.activeMachine.name)
  351. width: profileBackground.width
  352. elide: Text.ElideRight
  353. }
  354. Rectangle
  355. {
  356. id: profileBackground
  357. anchors
  358. {
  359. top: captionLabel.visible ? captionLabel.bottom : parent.top
  360. topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0
  361. bottom: parent.bottom
  362. left: parent.left
  363. }
  364. width: (parent.width * 0.4) | 0
  365. color: palette.light
  366. ListView
  367. {
  368. id: qualityListView
  369. anchors.fill: parent
  370. ScrollBar.vertical: UM.ScrollBar
  371. {
  372. id: profileScrollBar
  373. }
  374. clip: true
  375. model: base.qualityManagementModel
  376. Component.onCompleted:
  377. {
  378. var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName;
  379. // Select the required quality name if given
  380. for (var idx = 0; idx < base.qualityManagementModel.count; idx++)
  381. {
  382. var item = base.qualityManagementModel.getItem(idx);
  383. if (item.name == selectedItemName)
  384. {
  385. currentIndex = idx;
  386. break;
  387. }
  388. }
  389. }
  390. section.property: "section_name"
  391. section.delegate: Rectangle
  392. {
  393. height: childrenRect.height
  394. Label
  395. {
  396. anchors.left: parent.left
  397. anchors.leftMargin: UM.Theme.getSize("default_lining").width
  398. text: section
  399. font.bold: true
  400. }
  401. }
  402. delegate: Rectangle
  403. {
  404. width: profileBackground.width - profileScrollBar.width
  405. height: childrenRect.height
  406. // Added this property to identify custom profiles in automated system tests (Squish)
  407. property bool isReadOnly: model.is_read_only
  408. property bool isCurrentItem: ListView.isCurrentItem
  409. color: isCurrentItem ? palette.highlight : (model.index % 2) ? palette.base : palette.alternateBase
  410. Label
  411. {
  412. anchors.left: parent.left
  413. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  414. anchors.right: parent.right
  415. width: Math.floor((parent.width * 0.8))
  416. text: model.name
  417. elide: Text.ElideRight
  418. font.italic:
  419. {
  420. if (model.is_read_only)
  421. {
  422. // For built-in qualities, it needs to match both the intent category and the quality name
  423. return model.name == Cura.MachineManager.activeQualityOrQualityChangesName && model.intent_category == Cura.MachineManager.activeIntentCategory
  424. }
  425. else
  426. {
  427. // For custom qualities, it only needs to match the name
  428. return model.name == Cura.MachineManager.activeQualityOrQualityChangesName
  429. }
  430. }
  431. color: parent.isCurrentItem ? palette.highlightedText : palette.text
  432. }
  433. MouseArea
  434. {
  435. anchors.fill: parent
  436. onClicked:
  437. {
  438. parent.ListView.view.currentIndex = model.index;
  439. }
  440. }
  441. }
  442. }
  443. }
  444. // details panel on the right
  445. Item
  446. {
  447. id: detailsPanel
  448. anchors
  449. {
  450. left: profileBackground.right
  451. leftMargin: UM.Theme.getSize("default_margin").width
  452. top: parent.top
  453. bottom: parent.bottom
  454. right: parent.right
  455. }
  456. Column
  457. {
  458. id: detailsPanelHeaderColumn
  459. anchors
  460. {
  461. left: parent.left
  462. right: parent.right
  463. top: parent.top
  464. }
  465. spacing: UM.Theme.getSize("default_margin").height
  466. visible: base.currentItem != null
  467. Item // Profile title Label
  468. {
  469. id: profileName
  470. width: parent.width
  471. height: childrenRect.height
  472. Label
  473. {
  474. anchors.left: parent.left
  475. anchors.right: parent.right
  476. text: base.currentItemDisplayName
  477. font: UM.Theme.getFont("large_bold")
  478. elide: Text.ElideRight
  479. renderType: Text.NativeRendering
  480. }
  481. }
  482. Flow
  483. {
  484. id: currentSettingsActions
  485. width: parent.width
  486. visible: base.hasCurrentItem && base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName && base.currentItem.intent_category == Cura.MachineManager.activeIntentCategory
  487. Button
  488. {
  489. text: catalog.i18nc("@action:button", "Update profile with current settings/overrides")
  490. enabled: Cura.MachineManager.hasUserSettings && qualityListView.currentItem && !qualityListView.currentItem.is_read_only
  491. onClicked: Cura.ContainerManager.updateQualityChanges()
  492. }
  493. Button
  494. {
  495. text: catalog.i18nc("@action:button", "Discard current changes");
  496. enabled: Cura.MachineManager.hasUserSettings
  497. onClicked: Cura.ContainerManager.clearUserContainers();
  498. }
  499. }
  500. Label
  501. {
  502. id: defaultsMessage
  503. visible: false
  504. text: catalog.i18nc("@action:label", "This profile uses the defaults specified by the printer, so it has no settings/overrides in the list below.")
  505. wrapMode: Text.WordWrap
  506. width: parent.width
  507. }
  508. Label
  509. {
  510. id: noCurrentSettingsMessage
  511. visible: base.isCurrentItemActivated && !Cura.MachineManager.hasUserSettings
  512. text: catalog.i18nc("@action:label", "Your current settings match the selected profile.")
  513. wrapMode: Text.WordWrap
  514. width: parent.width
  515. }
  516. UM.TabRow
  517. {
  518. id: profileExtruderTabs
  519. UM.TabRowButton //One extra tab for the global settings.
  520. {
  521. text: catalog.i18nc("@title:tab", "Global Settings")
  522. }
  523. Repeater
  524. {
  525. model: base.extrudersModel
  526. UM.TabRowButton
  527. {
  528. text: model.name
  529. }
  530. }
  531. }
  532. }
  533. Cura.ProfileOverview
  534. {
  535. anchors
  536. {
  537. top: detailsPanelHeaderColumn.bottom
  538. left: parent.left
  539. right: parent.right
  540. bottom: parent.bottom
  541. }
  542. visible: detailsPanelHeaderColumn.visible
  543. qualityItem: base.currentItem
  544. extruderPosition: profileExtruderTabs.currentIndex - 1
  545. }
  546. }
  547. }
  548. }