ProfilesPage.qml 19 KB

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