ProfilesPage.qml 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. // Copyright (c) 2018 Ultimaker B.V.
  2. // Uranium is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 1.4
  5. import QtQuick.Layouts 1.3
  6. import QtQuick.Dialogs 1.2
  7. import UM 1.2 as UM
  8. import Cura 1.0 as Cura
  9. Item
  10. {
  11. id: base
  12. property QtObject qualityManager: CuraApplication.getQualityManager()
  13. property var resetEnabled: false // Keep PreferencesDialog happy
  14. property var extrudersModel: CuraApplication.getExtrudersModel()
  15. UM.I18nCatalog { id: catalog; name: "cura"; }
  16. Cura.QualityManagementModel {
  17. id: qualitiesModel
  18. }
  19. Label {
  20. id: titleLabel
  21. anchors {
  22. top: parent.top
  23. left: parent.left
  24. right: parent.right
  25. margins: 5 * screenScaleFactor
  26. }
  27. font.pointSize: 18
  28. text: catalog.i18nc("@title:tab", "Profiles")
  29. }
  30. property var hasCurrentItem: base.currentItem != null
  31. property var currentItem: {
  32. var current_index = qualityListView.currentIndex;
  33. return (current_index == -1) ? null : qualitiesModel.getItem(current_index);
  34. }
  35. property var currentItemName: hasCurrentItem ? base.currentItem.name : ""
  36. property var isCurrentItemActivated: {
  37. if (!base.currentItem) {
  38. return false;
  39. }
  40. return base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName;
  41. }
  42. property var canCreateProfile: {
  43. return isCurrentItemActivated && Cura.MachineManager.hasUserSettings;
  44. }
  45. Row // Button Row
  46. {
  47. id: buttonRow
  48. anchors {
  49. left: parent.left
  50. right: parent.right
  51. top: titleLabel.bottom
  52. }
  53. height: childrenRect.height
  54. // Activate button
  55. Button
  56. {
  57. id: activateMenuButton
  58. text: catalog.i18nc("@action:button", "Activate")
  59. iconName: "list-activate"
  60. enabled: !isCurrentItemActivated
  61. onClicked: {
  62. if (base.currentItem.is_read_only) {
  63. Cura.MachineManager.setQualityGroup(base.currentItem.quality_group);
  64. } else {
  65. Cura.MachineManager.setQualityChangesGroup(base.currentItem.quality_changes_group);
  66. }
  67. }
  68. }
  69. // Create button
  70. Button
  71. {
  72. id: createMenuButton
  73. text: catalog.i18nc("@label", "Create")
  74. iconName: "list-add"
  75. enabled: base.canCreateProfile && !Cura.MachineManager.stacksHaveErrors
  76. visible: base.canCreateProfile
  77. onClicked: {
  78. createQualityDialog.object = Cura.ContainerManager.makeUniqueName(base.currentItem.name);
  79. createQualityDialog.open();
  80. createQualityDialog.selectText();
  81. }
  82. }
  83. // Duplicate button
  84. Button
  85. {
  86. id: duplicateMenuButton
  87. text: catalog.i18nc("@label", "Duplicate")
  88. iconName: "list-add"
  89. enabled: !base.canCreateProfile
  90. visible: !base.canCreateProfile
  91. onClicked: {
  92. duplicateQualityDialog.object = Cura.ContainerManager.makeUniqueName(base.currentItem.name);
  93. duplicateQualityDialog.open();
  94. duplicateQualityDialog.selectText();
  95. }
  96. }
  97. // Remove button
  98. Button
  99. {
  100. id: removeMenuButton
  101. text: catalog.i18nc("@action:button", "Remove")
  102. iconName: "list-remove"
  103. enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated
  104. onClicked: {
  105. forceActiveFocus();
  106. confirmRemoveQualityDialog.open();
  107. }
  108. }
  109. // Rename button
  110. Button
  111. {
  112. id: renameMenuButton
  113. text: catalog.i18nc("@action:button", "Rename")
  114. iconName: "edit-rename"
  115. enabled: base.hasCurrentItem && !base.currentItem.is_read_only
  116. onClicked: {
  117. renameQualityDialog.object = base.currentItem.name;
  118. renameQualityDialog.open();
  119. renameQualityDialog.selectText();
  120. }
  121. }
  122. // Import button
  123. Button
  124. {
  125. id: importMenuButton
  126. text: catalog.i18nc("@action:button", "Import")
  127. iconName: "document-import"
  128. onClicked: {
  129. importDialog.open();
  130. }
  131. }
  132. // Export button
  133. Button
  134. {
  135. id: exportMenuButton
  136. text: catalog.i18nc("@action:button", "Export")
  137. iconName: "document-export"
  138. enabled: base.hasCurrentItem && !base.currentItem.is_read_only
  139. onClicked: {
  140. exportDialog.open();
  141. }
  142. }
  143. }
  144. // Click create profile from ... in Profile context menu
  145. signal createProfile()
  146. onCreateProfile:
  147. {
  148. createQualityDialog.object = Cura.ContainerManager.makeUniqueName(Cura.MachineManager.activeQualityOrQualityChangesName);
  149. createQualityDialog.open();
  150. createQualityDialog.selectText();
  151. }
  152. // Dialog to request a name when creating a new profile
  153. UM.RenameDialog
  154. {
  155. id: createQualityDialog
  156. title: catalog.i18nc("@title:window", "Create Profile")
  157. object: "<new name>"
  158. explanation: catalog.i18nc("@info", "Please provide a name for this profile.")
  159. onAccepted:
  160. {
  161. base.newQualityNameToSelect = newName; // We want to switch to the new profile once it's created
  162. base.toActivateNewQuality = true;
  163. base.qualityManager.createQualityChanges(newName);
  164. }
  165. }
  166. property string newQualityNameToSelect: ""
  167. property bool toActivateNewQuality: false
  168. // This connection makes sure that we will switch to the correct quality after the model gets updated
  169. Connections
  170. {
  171. target: qualitiesModel
  172. onItemsChanged:
  173. {
  174. var toSelectItemName = base.currentItem == null ? "" : base.currentItem.name;
  175. if (newQualityNameToSelect != "")
  176. {
  177. toSelectItemName = newQualityNameToSelect;
  178. }
  179. var newIdx = -1; // Default to nothing if nothing can be found
  180. if (toSelectItemName != "")
  181. {
  182. // Select the required quality name if given
  183. for (var idx = 0; idx < qualitiesModel.count; ++idx)
  184. {
  185. var item = qualitiesModel.getItem(idx);
  186. if (item.name == toSelectItemName)
  187. {
  188. // Switch to the newly created profile if needed
  189. newIdx = idx;
  190. if (base.toActivateNewQuality)
  191. {
  192. // Activate this custom quality if required
  193. Cura.MachineManager.setQualityChangesGroup(item.quality_changes_group);
  194. }
  195. break;
  196. }
  197. }
  198. }
  199. qualityListView.currentIndex = newIdx;
  200. // Reset states
  201. base.newQualityNameToSelect = "";
  202. base.toActivateNewQuality = false;
  203. }
  204. }
  205. // Dialog to request a name when duplicating a new profile
  206. UM.RenameDialog
  207. {
  208. id: duplicateQualityDialog
  209. title: catalog.i18nc("@title:window", "Duplicate Profile")
  210. object: "<new name>"
  211. onAccepted:
  212. {
  213. base.qualityManager.duplicateQualityChanges(newName, base.currentItem);
  214. }
  215. }
  216. // Confirmation dialog for removing a profile
  217. MessageDialog
  218. {
  219. id: confirmRemoveQualityDialog
  220. icon: StandardIcon.Question;
  221. title: catalog.i18nc("@title:window", "Confirm Remove")
  222. text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItemName)
  223. standardButtons: StandardButton.Yes | StandardButton.No
  224. modality: Qt.ApplicationModal
  225. onYes:
  226. {
  227. base.qualityManager.removeQualityChangesGroup(base.currentItem.quality_changes_group);
  228. // reset current item to the first if available
  229. qualityListView.currentIndex = -1; // Reset selection.
  230. }
  231. }
  232. // Dialog to rename a quality profile
  233. UM.RenameDialog
  234. {
  235. id: renameQualityDialog
  236. title: catalog.i18nc("@title:window", "Rename Profile")
  237. object: "<new name>"
  238. onAccepted:
  239. {
  240. var actualNewName = base.qualityManager.renameQualityChangesGroup(base.currentItem.quality_changes_group, newName);
  241. base.newQualityNameToSelect = actualNewName; // Select the new name after the model gets updated
  242. }
  243. }
  244. // Dialog for importing a quality profile
  245. FileDialog
  246. {
  247. id: importDialog
  248. title: catalog.i18nc("@title:window", "Import Profile")
  249. selectExisting: true
  250. nameFilters: qualitiesModel.getFileNameFilters("profile_reader")
  251. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  252. onAccepted:
  253. {
  254. var result = Cura.ContainerManager.importProfile(fileUrl);
  255. messageDialog.text = result.message;
  256. if (result.status == "ok") {
  257. messageDialog.icon = StandardIcon.Information;
  258. }
  259. else if (result.status == "duplicate") {
  260. messageDialog.icon = StandardIcon.Warning;
  261. }
  262. else {
  263. messageDialog.icon = StandardIcon.Critical;
  264. }
  265. messageDialog.open();
  266. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  267. }
  268. }
  269. // Dialog for exporting a quality profile
  270. FileDialog
  271. {
  272. id: exportDialog
  273. title: catalog.i18nc("@title:window", "Export Profile")
  274. selectExisting: false
  275. nameFilters: qualitiesModel.getFileNameFilters("profile_writer")
  276. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  277. onAccepted:
  278. {
  279. var result = Cura.ContainerManager.exportQualityChangesGroup(base.currentItem.quality_changes_group,
  280. fileUrl, selectedNameFilter);
  281. if (result && result.status == "error") {
  282. messageDialog.icon = StandardIcon.Critical;
  283. messageDialog.text = result.message;
  284. messageDialog.open();
  285. }
  286. // else pop-up Message thing from python code
  287. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  288. }
  289. }
  290. Item {
  291. id: contentsItem
  292. anchors {
  293. top: titleLabel.bottom
  294. left: parent.left
  295. right: parent.right
  296. bottom: parent.bottom
  297. margins: 5 * screenScaleFactor
  298. bottomMargin: 0
  299. }
  300. clip: true
  301. }
  302. Item
  303. {
  304. anchors {
  305. top: buttonRow.bottom
  306. topMargin: UM.Theme.getSize("default_margin").height
  307. left: parent.left
  308. right: parent.right
  309. bottom: parent.bottom
  310. }
  311. SystemPalette { id: palette }
  312. Label
  313. {
  314. id: captionLabel
  315. anchors {
  316. top: parent.top
  317. left: parent.left
  318. }
  319. visible: text != ""
  320. text: catalog.i18nc("@label %1 is printer name", "Printer: %1").arg(Cura.MachineManager.activeMachineName)
  321. width: profileScrollView.width
  322. elide: Text.ElideRight
  323. }
  324. ScrollView
  325. {
  326. id: profileScrollView
  327. anchors {
  328. top: captionLabel.visible ? captionLabel.bottom : parent.top
  329. topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0
  330. bottom: parent.bottom
  331. left: parent.left
  332. }
  333. Rectangle {
  334. parent: viewport
  335. anchors.fill: parent
  336. color: palette.light
  337. }
  338. width: true ? (parent.width * 0.4) | 0 : parent.width
  339. frameVisible: true
  340. clip: true
  341. ListView
  342. {
  343. id: qualityListView
  344. model: qualitiesModel
  345. Component.onCompleted:
  346. {
  347. var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName;
  348. // Select the required quality name if given
  349. for (var idx = 0; idx < qualitiesModel.count; idx++)
  350. {
  351. var item = qualitiesModel.getItem(idx);
  352. if (item.name == selectedItemName)
  353. {
  354. currentIndex = idx;
  355. break;
  356. }
  357. }
  358. }
  359. section.property: "is_read_only"
  360. section.delegate: Rectangle
  361. {
  362. height: childrenRect.height
  363. Label
  364. {
  365. anchors.left: parent.left
  366. anchors.leftMargin: UM.Theme.getSize("default_lining").width
  367. text: section == "true" ? catalog.i18nc("@label", "Default profiles") : catalog.i18nc("@label", "Custom profiles")
  368. font.bold: true
  369. }
  370. }
  371. delegate: Rectangle
  372. {
  373. width: profileScrollView.width
  374. height: childrenRect.height
  375. // Added this property to identify custom profiles in automated system tests (Squish)
  376. property bool isReadOnly: model.is_read_only
  377. property bool isCurrentItem: ListView.isCurrentItem
  378. color: isCurrentItem ? palette.highlight : (model.index % 2) ? palette.base : palette.alternateBase
  379. Label
  380. {
  381. anchors.left: parent.left
  382. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  383. anchors.right: parent.right
  384. width: Math.floor((parent.width * 0.8))
  385. text: model.name
  386. elide: Text.ElideRight
  387. font.italic: model.name == Cura.MachineManager.activeQualityOrQualityChangesName
  388. color: parent.isCurrentItem ? palette.highlightedText : palette.text
  389. }
  390. MouseArea
  391. {
  392. anchors.fill: parent
  393. onClicked: {
  394. parent.ListView.view.currentIndex = model.index;
  395. }
  396. }
  397. }
  398. }
  399. }
  400. // details panel on the right
  401. Item
  402. {
  403. id: detailsPanel
  404. anchors {
  405. left: profileScrollView.right
  406. leftMargin: UM.Theme.getSize("default_margin").width
  407. top: parent.top
  408. bottom: parent.bottom
  409. right: parent.right
  410. }
  411. Item
  412. {
  413. anchors.fill: parent
  414. visible: base.currentItem != null
  415. Item // Profile title Label
  416. {
  417. id: profileName
  418. width: parent.width
  419. height: childrenRect.height
  420. Label {
  421. text: base.currentItemName
  422. font: UM.Theme.getFont("large_bold")
  423. }
  424. }
  425. Flow {
  426. id: currentSettingsActions
  427. visible: base.hasCurrentItem && base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName
  428. anchors.left: parent.left
  429. anchors.right: parent.right
  430. anchors.top: profileName.bottom
  431. anchors.topMargin: UM.Theme.getSize("default_margin").height
  432. Button
  433. {
  434. text: catalog.i18nc("@action:button", "Update profile with current settings/overrides")
  435. enabled: Cura.MachineManager.hasUserSettings && !base.currentItem.is_read_only
  436. onClicked: Cura.ContainerManager.updateQualityChanges()
  437. }
  438. Button
  439. {
  440. text: catalog.i18nc("@action:button", "Discard current changes");
  441. enabled: Cura.MachineManager.hasUserSettings
  442. onClicked: Cura.ContainerManager.clearUserContainers();
  443. }
  444. }
  445. Column {
  446. id: profileNotices
  447. anchors.top: currentSettingsActions.visible ? currentSettingsActions.bottom : currentSettingsActions.anchors.top
  448. anchors.topMargin: UM.Theme.getSize("default_margin").height
  449. anchors.left: parent.left
  450. anchors.right: parent.right
  451. spacing: UM.Theme.getSize("default_margin").height
  452. Label {
  453. id: defaultsMessage
  454. visible: false
  455. text: catalog.i18nc("@action:label", "This profile uses the defaults specified by the printer, so it has no settings/overrides in the list below.")
  456. wrapMode: Text.WordWrap
  457. width: parent.width
  458. }
  459. Label {
  460. id: noCurrentSettingsMessage
  461. visible: base.isCurrentItemActivated && !Cura.MachineManager.hasUserSettings
  462. text: catalog.i18nc("@action:label", "Your current settings match the selected profile.")
  463. wrapMode: Text.WordWrap
  464. width: parent.width
  465. }
  466. }
  467. TabView
  468. {
  469. anchors.left: parent.left
  470. anchors.top: profileNotices.visible ? profileNotices.bottom : profileNotices.anchors.top
  471. anchors.topMargin: UM.Theme.getSize("default_margin").height
  472. anchors.right: parent.right
  473. anchors.bottom: parent.bottom
  474. currentIndex: 0
  475. ProfileTab
  476. {
  477. title: catalog.i18nc("@title:tab", "Global Settings")
  478. qualityItem: base.currentItem
  479. }
  480. Repeater
  481. {
  482. model: base.extrudersModel
  483. ProfileTab
  484. {
  485. title: model.name
  486. extruderPosition: model.index
  487. qualityItem: base.currentItem
  488. }
  489. }
  490. }
  491. }
  492. }
  493. }
  494. }