MaterialView.qml 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. // Copyright (c) 2017 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 1.4
  5. import QtQuick.Dialogs 1.2
  6. import UM 1.2 as UM
  7. import Cura 1.0 as Cura
  8. TabView
  9. {
  10. id: base
  11. property QtObject materialManager: CuraApplication.getMaterialManager()
  12. property QtObject properties
  13. property var currentMaterialNode: null
  14. property bool editingEnabled: false;
  15. property string currency: UM.Preferences.getValue("cura/currency") ? UM.Preferences.getValue("cura/currency") : "€"
  16. property real firstColumnWidth: (width * 0.50) | 0
  17. property real secondColumnWidth: (width * 0.40) | 0
  18. property string containerId: ""
  19. property var materialPreferenceValues: UM.Preferences.getValue("cura/material_settings") ? JSON.parse(UM.Preferences.getValue("cura/material_settings")) : {}
  20. property double spoolLength: calculateSpoolLength()
  21. property real costPerMeter: calculateCostPerMeter()
  22. property bool reevaluateLinkedMaterials: false
  23. property string linkedMaterialNames:
  24. {
  25. if (reevaluateLinkedMaterials) {
  26. reevaluateLinkedMaterials = false;
  27. }
  28. if (!base.containerId || !base.editingEnabled) {
  29. return ""
  30. }
  31. var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentMaterialNode, true);
  32. if (linkedMaterials.length == 0) {
  33. return ""
  34. }
  35. return linkedMaterials.join(", ");
  36. }
  37. function getApproximateDiameter(diameter) {
  38. return Math.round(diameter);
  39. }
  40. // This trick makes sure to make all fields lose focus so their onEditingFinished will be triggered
  41. // and modified values will be saved. This can happen when a user changes a value and then closes the
  42. // dialog directly.
  43. //
  44. // Please note that somehow this callback is ONLY triggered when visible is false.
  45. onVisibleChanged:
  46. {
  47. if (!visible)
  48. {
  49. base.focus = false;
  50. }
  51. }
  52. Tab
  53. {
  54. title: catalog.i18nc("@title", "Information")
  55. anchors.margins: UM.Theme.getSize("default_margin").width
  56. ScrollView
  57. {
  58. id: scrollView
  59. anchors.fill: parent
  60. horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
  61. flickableItem.flickableDirection: Flickable.VerticalFlick
  62. frameVisible: true
  63. property real columnWidth: (viewport.width * 0.5 - UM.Theme.getSize("default_margin").width) | 0
  64. Flow
  65. {
  66. id: containerGrid
  67. x: UM.Theme.getSize("default_margin").width
  68. y: UM.Theme.getSize("default_lining").height
  69. width: base.width
  70. property real rowHeight: textField.height + UM.Theme.getSize("default_lining").height
  71. MessageDialog
  72. {
  73. id: confirmDiameterChangeDialog
  74. icon: StandardIcon.Question;
  75. title: catalog.i18nc("@title:window", "Confirm Diameter Change")
  76. text: catalog.i18nc("@label (%1 is a number)", "The new filament diameter is set to %1 mm, which is not compatible with the current extruder. Do you wish to continue?".arg(new_diameter_value))
  77. standardButtons: StandardButton.Yes | StandardButton.No
  78. modality: Qt.ApplicationModal
  79. property var new_diameter_value: null;
  80. property var old_diameter_value: null;
  81. property var old_approximate_diameter_value: null;
  82. property bool keyPressed: false
  83. onYes:
  84. {
  85. Cura.ContainerManager.setContainerProperty(base.containerId, "material_diameter", "value", new_diameter_value);
  86. base.setMetaDataEntry("approximate_diameter", old_approximate_diameter_value, getApproximateDiameter(new_diameter_value).toString());
  87. base.setMetaDataEntry("properties/diameter", properties.diameter, new_diameter_value);
  88. }
  89. onNo:
  90. {
  91. properties.diameter = old_diameter_value;
  92. diameterSpinBox.value = properties.diameter;
  93. }
  94. onVisibilityChanged:
  95. {
  96. if (!visible && !keyPressed)
  97. {
  98. // If the user closes this dialog without clicking on any button, it's the same as clicking "No".
  99. no();
  100. }
  101. keyPressed = false;
  102. }
  103. }
  104. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Display Name") }
  105. ReadOnlyTextField
  106. {
  107. id: displayNameTextField;
  108. width: scrollView.columnWidth;
  109. text: properties.name;
  110. readOnly: !base.editingEnabled;
  111. onEditingFinished: base.updateMaterialDisplayName(properties.name, text)
  112. }
  113. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") }
  114. ReadOnlyTextField
  115. {
  116. id: textField;
  117. width: scrollView.columnWidth;
  118. text: properties.brand;
  119. readOnly: !base.editingEnabled;
  120. onEditingFinished: base.updateMaterialBrand(properties.brand, text)
  121. }
  122. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") }
  123. ReadOnlyTextField
  124. {
  125. width: scrollView.columnWidth;
  126. text: properties.material;
  127. readOnly: !base.editingEnabled;
  128. onEditingFinished: base.updateMaterialType(properties.material, text)
  129. }
  130. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
  131. Row {
  132. width: scrollView.columnWidth
  133. height: parent.rowHeight
  134. spacing: Math.round(UM.Theme.getSize("default_margin").width / 2)
  135. // color indicator square
  136. Rectangle {
  137. id: colorSelector
  138. color: properties.color_code
  139. width: Math.round(colorLabel.height * 0.75)
  140. height: Math.round(colorLabel.height * 0.75)
  141. border.width: UM.Theme.getSize("default_lining").height
  142. anchors.verticalCenter: parent.verticalCenter
  143. // open the color selection dialog on click
  144. MouseArea {
  145. anchors.fill: parent
  146. onClicked: colorDialog.open()
  147. enabled: base.editingEnabled
  148. }
  149. }
  150. // pretty color name text field
  151. ReadOnlyTextField {
  152. id: colorLabel;
  153. text: properties.color_name;
  154. readOnly: !base.editingEnabled
  155. onEditingFinished: base.setMetaDataEntry("color_name", properties.color_name, text)
  156. }
  157. // popup dialog to select a new color
  158. // if successful it sets the properties.color_code value to the new color
  159. ColorDialog {
  160. id: colorDialog
  161. color: properties.color_code
  162. onAccepted: base.setMetaDataEntry("color_code", properties.color_code, color)
  163. }
  164. }
  165. Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
  166. Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") }
  167. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") }
  168. ReadOnlySpinBox
  169. {
  170. id: densitySpinBox
  171. width: scrollView.columnWidth
  172. value: properties.density
  173. decimals: 2
  174. suffix: " g/cm³"
  175. stepSize: 0.01
  176. readOnly: !base.editingEnabled
  177. onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value)
  178. onValueChanged: updateCostPerMeter()
  179. }
  180. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") }
  181. ReadOnlySpinBox
  182. {
  183. id: diameterSpinBox
  184. width: scrollView.columnWidth
  185. value: properties.diameter
  186. decimals: 2
  187. suffix: " mm"
  188. stepSize: 0.01
  189. readOnly: !base.editingEnabled
  190. onEditingFinished:
  191. {
  192. // This does not use a SettingPropertyProvider, because we need to make the change to all containers
  193. // which derive from the same base_file
  194. var old_diameter = Cura.ContainerManager.getContainerProperty(base.containerId, "material_diameter", "value").toString();
  195. var old_approximate_diameter = Cura.ContainerManager.getContainerMetaDataEntry(base.containerId, "approximate_diameter");
  196. var new_approximate_diameter = getApproximateDiameter(value);
  197. if (new_approximate_diameter != Cura.ExtruderManager.getActiveExtruderStack().approximateMaterialDiameter)
  198. {
  199. confirmDiameterChangeDialog.old_diameter_value = old_diameter;
  200. confirmDiameterChangeDialog.new_diameter_value = value;
  201. confirmDiameterChangeDialog.old_approximate_diameter_value = old_approximate_diameter;
  202. confirmDiameterChangeDialog.open()
  203. }
  204. else {
  205. Cura.ContainerManager.setContainerProperty(base.containerId, "material_diameter", "value", value);
  206. base.setMetaDataEntry("approximate_diameter", old_approximate_diameter, getApproximateDiameter(value).toString());
  207. base.setMetaDataEntry("properties/diameter", properties.diameter, value);
  208. }
  209. }
  210. onValueChanged: updateCostPerMeter()
  211. }
  212. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") }
  213. SpinBox
  214. {
  215. id: spoolCostSpinBox
  216. width: scrollView.columnWidth
  217. value: base.getMaterialPreferenceValue(properties.guid, "spool_cost")
  218. prefix: base.currency + " "
  219. decimals: 2
  220. maximumValue: 100000000
  221. onValueChanged: {
  222. base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value))
  223. updateCostPerMeter()
  224. }
  225. }
  226. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") }
  227. SpinBox
  228. {
  229. id: spoolWeightSpinBox
  230. width: scrollView.columnWidth
  231. value: base.getMaterialPreferenceValue(properties.guid, "spool_weight")
  232. suffix: " g"
  233. stepSize: 100
  234. decimals: 0
  235. maximumValue: 10000
  236. onValueChanged: {
  237. base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value))
  238. updateCostPerMeter()
  239. }
  240. }
  241. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") }
  242. Label
  243. {
  244. width: scrollView.columnWidth
  245. text: "~ %1 m".arg(Math.round(base.spoolLength))
  246. verticalAlignment: Qt.AlignVCenter
  247. height: parent.rowHeight
  248. }
  249. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter") }
  250. Label
  251. {
  252. width: scrollView.columnWidth
  253. text: "~ %1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency)
  254. verticalAlignment: Qt.AlignVCenter
  255. height: parent.rowHeight
  256. }
  257. Item { width: parent.width; height: UM.Theme.getSize("default_margin").height; visible: unlinkMaterialButton.visible }
  258. Label
  259. {
  260. width: 2 * scrollView.columnWidth
  261. verticalAlignment: Qt.AlignVCenter
  262. text: catalog.i18nc("@label", "This material is linked to %1 and shares some of its properties.").arg(base.linkedMaterialNames)
  263. wrapMode: Text.WordWrap
  264. visible: unlinkMaterialButton.visible
  265. }
  266. Button
  267. {
  268. id: unlinkMaterialButton
  269. text: catalog.i18nc("@label", "Unlink Material")
  270. visible: base.linkedMaterialNames != ""
  271. onClicked:
  272. {
  273. Cura.ContainerManager.unlinkMaterial(base.currentMaterialNode)
  274. base.reevaluateLinkedMaterials = true
  275. }
  276. }
  277. Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
  278. Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") }
  279. ReadOnlyTextArea
  280. {
  281. text: properties.description;
  282. width: 2 * scrollView.columnWidth
  283. wrapMode: Text.WordWrap
  284. readOnly: !base.editingEnabled;
  285. onEditingFinished: base.setMetaDataEntry("description", properties.description, text)
  286. }
  287. Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Adhesion Information") }
  288. ReadOnlyTextArea
  289. {
  290. text: properties.adhesion_info;
  291. width: 2 * scrollView.columnWidth
  292. wrapMode: Text.WordWrap
  293. readOnly: !base.editingEnabled;
  294. onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text)
  295. }
  296. Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
  297. }
  298. function updateCostPerMeter()
  299. {
  300. base.spoolLength = calculateSpoolLength(diameterSpinBox.value, densitySpinBox.value, spoolWeightSpinBox.value);
  301. base.costPerMeter = calculateCostPerMeter(spoolCostSpinBox.value);
  302. }
  303. }
  304. }
  305. Tab
  306. {
  307. title: catalog.i18nc("@label", "Print settings")
  308. anchors
  309. {
  310. leftMargin: UM.Theme.getSize("default_margin").width
  311. topMargin: UM.Theme.getSize("default_margin").height
  312. bottomMargin: UM.Theme.getSize("default_margin").height
  313. rightMargin: 0
  314. }
  315. ScrollView
  316. {
  317. anchors.fill: parent;
  318. ListView
  319. {
  320. model: UM.SettingDefinitionsModel
  321. {
  322. containerId: Cura.MachineManager.activeDefinitionId
  323. visibilityHandler: Cura.MaterialSettingsVisibilityHandler { }
  324. expanded: ["*"]
  325. }
  326. delegate: UM.TooltipArea
  327. {
  328. width: childrenRect.width
  329. height: childrenRect.height
  330. text: model.description
  331. Label
  332. {
  333. id: label
  334. width: base.firstColumnWidth;
  335. height: spinBox.height + UM.Theme.getSize("default_lining").height
  336. text: model.label
  337. elide: Text.ElideRight
  338. verticalAlignment: Qt.AlignVCenter
  339. }
  340. ReadOnlySpinBox
  341. {
  342. id: spinBox
  343. anchors.left: label.right
  344. value: {
  345. // In case the setting is not in the material...
  346. if (!isNaN(parseFloat(materialPropertyProvider.properties.value)))
  347. {
  348. return parseFloat(materialPropertyProvider.properties.value);
  349. }
  350. // ... we search in the variant, and if it is not there...
  351. if (!isNaN(parseFloat(variantPropertyProvider.properties.value)))
  352. {
  353. return parseFloat(variantPropertyProvider.properties.value);
  354. }
  355. // ... then look in the definition container.
  356. if (!isNaN(parseFloat(machinePropertyProvider.properties.value)))
  357. {
  358. return parseFloat(machinePropertyProvider.properties.value);
  359. }
  360. return 0;
  361. }
  362. width: base.secondColumnWidth
  363. readOnly: !base.editingEnabled
  364. suffix: " " + model.unit
  365. maximumValue: 99999
  366. decimals: model.unit == "mm" ? 2 : 0
  367. onEditingFinished: materialPropertyProvider.setPropertyValue("value", value)
  368. }
  369. UM.ContainerPropertyProvider
  370. {
  371. id: materialPropertyProvider
  372. containerId: base.containerId
  373. watchedProperties: [ "value" ]
  374. key: model.key
  375. }
  376. UM.ContainerPropertyProvider
  377. {
  378. id: variantPropertyProvider
  379. containerId: Cura.MachineManager.activeVariantId
  380. watchedProperties: [ "value" ]
  381. key: model.key
  382. }
  383. UM.ContainerPropertyProvider
  384. {
  385. id: machinePropertyProvider
  386. containerId: Cura.MachineManager.activeDefinitionId
  387. watchedProperties: [ "value" ]
  388. key: model.key
  389. }
  390. }
  391. }
  392. }
  393. }
  394. function calculateSpoolLength(diameter, density, spoolWeight)
  395. {
  396. if(!diameter)
  397. {
  398. diameter = properties.diameter;
  399. }
  400. if(!density)
  401. {
  402. density = properties.density;
  403. }
  404. if(!spoolWeight)
  405. {
  406. spoolWeight = base.getMaterialPreferenceValue(properties.guid, "spool_weight");
  407. }
  408. if (diameter == 0 || density == 0 || spoolWeight == 0)
  409. {
  410. return 0;
  411. }
  412. var area = Math.PI * Math.pow(diameter / 2, 2); // in mm2
  413. var volume = (spoolWeight / density); // in cm3
  414. return volume / area; // in m
  415. }
  416. function calculateCostPerMeter(spoolCost)
  417. {
  418. if(!spoolCost)
  419. {
  420. spoolCost = base.getMaterialPreferenceValue(properties.guid, "spool_cost");
  421. }
  422. if (spoolLength == 0)
  423. {
  424. return 0;
  425. }
  426. return spoolCost / spoolLength;
  427. }
  428. // Tiny convenience function to check if a value really changed before trying to set it.
  429. function setMetaDataEntry(entry_name, old_value, new_value) {
  430. if (old_value != new_value) {
  431. Cura.ContainerManager.setContainerMetaDataEntry(base.currentMaterialNode, entry_name, new_value)
  432. // make sure the UI properties are updated as well since we don't re-fetch the entire model here
  433. // When the entry_name is something like properties/diameter, we take the last part of the entry_name
  434. var list = entry_name.split("/")
  435. var key = list[list.length - 1]
  436. properties[key] = new_value
  437. }
  438. }
  439. function setMaterialPreferenceValue(material_guid, entry_name, new_value)
  440. {
  441. if(!(material_guid in materialPreferenceValues))
  442. {
  443. materialPreferenceValues[material_guid] = {};
  444. }
  445. if(entry_name in materialPreferenceValues[material_guid] && materialPreferenceValues[material_guid][entry_name] == new_value)
  446. {
  447. // value has not changed
  448. return
  449. }
  450. materialPreferenceValues[material_guid][entry_name] = new_value;
  451. // store preference
  452. UM.Preferences.setValue("cura/material_settings", JSON.stringify(materialPreferenceValues));
  453. }
  454. function getMaterialPreferenceValue(material_guid, entry_name)
  455. {
  456. if(material_guid in materialPreferenceValues && entry_name in materialPreferenceValues[material_guid])
  457. {
  458. return materialPreferenceValues[material_guid][entry_name];
  459. }
  460. return 0;
  461. }
  462. // update the display name of the material
  463. function updateMaterialDisplayName (old_name, new_name) {
  464. // don't change when new name is the same
  465. if (old_name == new_name) {
  466. return
  467. }
  468. // update the values
  469. base.materialManager.setMaterialName(base.currentMaterialNode, new_name)
  470. materialProperties.name = new_name
  471. }
  472. // update the type of the material
  473. function updateMaterialType (old_type, new_type) {
  474. base.setMetaDataEntry("material", old_type, new_type)
  475. materialProperties.material= new_type
  476. }
  477. // update the brand of the material
  478. function updateMaterialBrand (old_brand, new_brand) {
  479. base.setMetaDataEntry("brand", old_brand, new_brand)
  480. materialProperties.brand = new_brand
  481. }
  482. }