MaterialView.qml 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  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);
  32. if (linkedMaterials.length <= 1) {
  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 object name)", "The new material diameter is set to %1 mm, which is not compatible to the current machine. 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. onYes:
  83. {
  84. Cura.ContainerManager.setContainerProperty(base.containerId, "material_diameter", "value", new_diameter_value);
  85. base.setMetaDataEntry("approximate_diameter", old_approximate_diameter_value, getApproximateDiameter(new_diameter_value).toString());
  86. base.setMetaDataEntry("properties/diameter", properties.diameter, new_diameter_value);
  87. }
  88. onNo:
  89. {
  90. properties.diameter = old_diameter_value;
  91. diameterSpinBox.value = properties.diameter;
  92. }
  93. }
  94. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Display Name") }
  95. ReadOnlyTextField
  96. {
  97. id: displayNameTextField;
  98. width: scrollView.columnWidth;
  99. text: properties.name;
  100. readOnly: !base.editingEnabled;
  101. onEditingFinished: base.updateMaterialDisplayName(properties.name, text)
  102. }
  103. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") }
  104. ReadOnlyTextField
  105. {
  106. id: textField;
  107. width: scrollView.columnWidth;
  108. text: properties.brand;
  109. readOnly: !base.editingEnabled;
  110. onEditingFinished: base.updateMaterialBrand(properties.brand, text)
  111. }
  112. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") }
  113. ReadOnlyTextField
  114. {
  115. width: scrollView.columnWidth;
  116. text: properties.material;
  117. readOnly: !base.editingEnabled;
  118. onEditingFinished: base.updateMaterialType(properties.material, text)
  119. }
  120. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
  121. Row {
  122. width: scrollView.columnWidth
  123. height: parent.rowHeight
  124. spacing: Math.round(UM.Theme.getSize("default_margin").width / 2)
  125. // color indicator square
  126. Rectangle {
  127. id: colorSelector
  128. color: properties.color_code
  129. width: Math.round(colorLabel.height * 0.75)
  130. height: Math.round(colorLabel.height * 0.75)
  131. border.width: UM.Theme.getSize("default_lining").height
  132. anchors.verticalCenter: parent.verticalCenter
  133. // open the color selection dialog on click
  134. MouseArea {
  135. anchors.fill: parent
  136. onClicked: colorDialog.open()
  137. enabled: base.editingEnabled
  138. }
  139. }
  140. // pretty color name text field
  141. ReadOnlyTextField {
  142. id: colorLabel;
  143. text: properties.color_name;
  144. readOnly: !base.editingEnabled
  145. onEditingFinished: base.setMetaDataEntry("color_name", properties.color_name, text)
  146. }
  147. // popup dialog to select a new color
  148. // if successful it sets the properties.color_code value to the new color
  149. ColorDialog {
  150. id: colorDialog
  151. color: properties.color_code
  152. onAccepted: base.setMetaDataEntry("color_code", properties.color_code, color)
  153. }
  154. }
  155. Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
  156. Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") }
  157. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") }
  158. ReadOnlySpinBox
  159. {
  160. id: densitySpinBox
  161. width: scrollView.columnWidth
  162. value: properties.density
  163. decimals: 2
  164. suffix: " g/cm³"
  165. stepSize: 0.01
  166. readOnly: !base.editingEnabled
  167. onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value)
  168. onValueChanged: updateCostPerMeter()
  169. }
  170. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") }
  171. ReadOnlySpinBox
  172. {
  173. id: diameterSpinBox
  174. width: scrollView.columnWidth
  175. value: properties.diameter
  176. decimals: 2
  177. suffix: " mm"
  178. stepSize: 0.01
  179. readOnly: !base.editingEnabled
  180. onEditingFinished:
  181. {
  182. // This does not use a SettingPropertyProvider, because we need to make the change to all containers
  183. // which derive from the same base_file
  184. var old_diameter = Cura.ContainerManager.getContainerProperty(base.containerId, "material_diameter", "value").toString();
  185. var old_approximate_diameter = Cura.ContainerManager.getContainerMetaDataEntry(base.containerId, "approximate_diameter");
  186. var new_approximate_diameter = getApproximateDiameter(value);
  187. if (Cura.MachineManager.filterMaterialsByMachine && new_approximate_diameter != Cura.ExtruderManager.getActiveExtruderStack().approximateMaterialDiameter)
  188. {
  189. confirmDiameterChangeDialog.old_diameter_value = old_diameter;
  190. confirmDiameterChangeDialog.new_diameter_value = value;
  191. confirmDiameterChangeDialog.old_approximate_diameter_value = old_approximate_diameter;
  192. confirmDiameterChangeDialog.open()
  193. }
  194. else {
  195. Cura.ContainerManager.setContainerProperty(base.containerId, "material_diameter", "value", value);
  196. base.setMetaDataEntry("approximate_diameter", old_approximate_diameter, getApproximateDiameter(value).toString());
  197. base.setMetaDataEntry("properties/diameter", properties.diameter, value);
  198. }
  199. }
  200. onValueChanged: updateCostPerMeter()
  201. }
  202. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") }
  203. SpinBox
  204. {
  205. id: spoolCostSpinBox
  206. width: scrollView.columnWidth
  207. value: base.getMaterialPreferenceValue(properties.guid, "spool_cost")
  208. prefix: base.currency + " "
  209. decimals: 2
  210. maximumValue: 100000000
  211. onValueChanged: {
  212. base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value))
  213. updateCostPerMeter()
  214. }
  215. }
  216. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") }
  217. SpinBox
  218. {
  219. id: spoolWeightSpinBox
  220. width: scrollView.columnWidth
  221. value: base.getMaterialPreferenceValue(properties.guid, "spool_weight")
  222. suffix: " g"
  223. stepSize: 100
  224. decimals: 0
  225. maximumValue: 10000
  226. onValueChanged: {
  227. base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value))
  228. updateCostPerMeter()
  229. }
  230. }
  231. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") }
  232. Label
  233. {
  234. width: scrollView.columnWidth
  235. text: "~ %1 m".arg(Math.round(base.spoolLength))
  236. verticalAlignment: Qt.AlignVCenter
  237. height: parent.rowHeight
  238. }
  239. Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter") }
  240. Label
  241. {
  242. width: scrollView.columnWidth
  243. text: "~ %1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency)
  244. verticalAlignment: Qt.AlignVCenter
  245. height: parent.rowHeight
  246. }
  247. Item { width: parent.width; height: UM.Theme.getSize("default_margin").height; visible: unlinkMaterialButton.visible }
  248. Label
  249. {
  250. width: 2 * scrollView.columnWidth
  251. verticalAlignment: Qt.AlignVCenter
  252. text: catalog.i18nc("@label", "This material is linked to %1 and shares some of its properties.").arg(base.linkedMaterialNames)
  253. wrapMode: Text.WordWrap
  254. visible: unlinkMaterialButton.visible
  255. }
  256. Button
  257. {
  258. id: unlinkMaterialButton
  259. text: catalog.i18nc("@label", "Unlink Material")
  260. visible: base.linkedMaterialNames != ""
  261. onClicked:
  262. {
  263. Cura.ContainerManager.unlinkMaterial(base.currentMaterialNode)
  264. base.reevaluateLinkedMaterials = true
  265. }
  266. }
  267. Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
  268. Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") }
  269. ReadOnlyTextArea
  270. {
  271. text: properties.description;
  272. width: 2 * scrollView.columnWidth
  273. wrapMode: Text.WordWrap
  274. readOnly: !base.editingEnabled;
  275. onEditingFinished: base.setMetaDataEntry("description", properties.description, text)
  276. }
  277. Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Adhesion Information") }
  278. ReadOnlyTextArea
  279. {
  280. text: properties.adhesion_info;
  281. width: 2 * scrollView.columnWidth
  282. wrapMode: Text.WordWrap
  283. readOnly: !base.editingEnabled;
  284. onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text)
  285. }
  286. Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
  287. }
  288. function updateCostPerMeter()
  289. {
  290. base.spoolLength = calculateSpoolLength(diameterSpinBox.value, densitySpinBox.value, spoolWeightSpinBox.value);
  291. base.costPerMeter = calculateCostPerMeter(spoolCostSpinBox.value);
  292. }
  293. }
  294. }
  295. Tab
  296. {
  297. title: catalog.i18nc("@label", "Print settings")
  298. anchors
  299. {
  300. leftMargin: UM.Theme.getSize("default_margin").width
  301. topMargin: UM.Theme.getSize("default_margin").height
  302. bottomMargin: UM.Theme.getSize("default_margin").height
  303. rightMargin: 0
  304. }
  305. ScrollView
  306. {
  307. anchors.fill: parent;
  308. ListView
  309. {
  310. model: UM.SettingDefinitionsModel
  311. {
  312. containerId: Cura.MachineManager.activeDefinitionId
  313. visibilityHandler: Cura.MaterialSettingsVisibilityHandler { }
  314. expanded: ["*"]
  315. }
  316. delegate: UM.TooltipArea
  317. {
  318. width: childrenRect.width
  319. height: childrenRect.height
  320. text: model.description
  321. Label
  322. {
  323. id: label
  324. width: base.firstColumnWidth;
  325. height: spinBox.height + UM.Theme.getSize("default_lining").height
  326. text: model.label
  327. elide: Text.ElideRight
  328. verticalAlignment: Qt.AlignVCenter
  329. }
  330. ReadOnlySpinBox
  331. {
  332. id: spinBox
  333. anchors.left: label.right
  334. value: {
  335. if (!isNaN(parseFloat(materialPropertyProvider.properties.value)))
  336. {
  337. return parseFloat(materialPropertyProvider.properties.value);
  338. }
  339. if (!isNaN(parseFloat(machinePropertyProvider.properties.value)))
  340. {
  341. return parseFloat(machinePropertyProvider.properties.value);
  342. }
  343. return 0;
  344. }
  345. width: base.secondColumnWidth
  346. readOnly: !base.editingEnabled
  347. suffix: " " + model.unit
  348. maximumValue: 99999
  349. decimals: model.unit == "mm" ? 2 : 0
  350. onEditingFinished: materialPropertyProvider.setPropertyValue("value", value)
  351. }
  352. UM.ContainerPropertyProvider
  353. {
  354. id: materialPropertyProvider
  355. containerId: base.containerId
  356. watchedProperties: [ "value" ]
  357. key: model.key
  358. }
  359. UM.ContainerPropertyProvider
  360. {
  361. id: machinePropertyProvider
  362. containerId: Cura.MachineManager.activeDefinitionId
  363. watchedProperties: [ "value" ]
  364. key: model.key
  365. }
  366. }
  367. }
  368. }
  369. }
  370. function calculateSpoolLength(diameter, density, spoolWeight)
  371. {
  372. if(!diameter)
  373. {
  374. diameter = properties.diameter;
  375. }
  376. if(!density)
  377. {
  378. density = properties.density;
  379. }
  380. if(!spoolWeight)
  381. {
  382. spoolWeight = base.getMaterialPreferenceValue(properties.guid, "spool_weight");
  383. }
  384. if (diameter == 0 || density == 0 || spoolWeight == 0)
  385. {
  386. return 0;
  387. }
  388. var area = Math.PI * Math.pow(diameter / 2, 2); // in mm2
  389. var volume = (spoolWeight / density); // in cm3
  390. return volume / area; // in m
  391. }
  392. function calculateCostPerMeter(spoolCost)
  393. {
  394. if(!spoolCost)
  395. {
  396. spoolCost = base.getMaterialPreferenceValue(properties.guid, "spool_cost");
  397. }
  398. if (spoolLength == 0)
  399. {
  400. return 0;
  401. }
  402. return spoolCost / spoolLength;
  403. }
  404. // Tiny convenience function to check if a value really changed before trying to set it.
  405. function setMetaDataEntry(entry_name, old_value, new_value) {
  406. if (old_value != new_value) {
  407. Cura.ContainerManager.setContainerMetaDataEntry(base.currentMaterialNode, entry_name, new_value)
  408. // make sure the UI properties are updated as well since we don't re-fetch the entire model here
  409. // When the entry_name is something like properties/diameter, we take the last part of the entry_name
  410. var list = entry_name.split("/")
  411. var key = list[list.length - 1]
  412. properties[key] = new_value
  413. }
  414. }
  415. function setMaterialPreferenceValue(material_guid, entry_name, new_value)
  416. {
  417. if(!(material_guid in materialPreferenceValues))
  418. {
  419. materialPreferenceValues[material_guid] = {};
  420. }
  421. if(entry_name in materialPreferenceValues[material_guid] && materialPreferenceValues[material_guid][entry_name] == new_value)
  422. {
  423. // value has not changed
  424. return
  425. }
  426. materialPreferenceValues[material_guid][entry_name] = new_value;
  427. // store preference
  428. UM.Preferences.setValue("cura/material_settings", JSON.stringify(materialPreferenceValues));
  429. }
  430. function getMaterialPreferenceValue(material_guid, entry_name)
  431. {
  432. if(material_guid in materialPreferenceValues && entry_name in materialPreferenceValues[material_guid])
  433. {
  434. return materialPreferenceValues[material_guid][entry_name];
  435. }
  436. return 0;
  437. }
  438. // update the display name of the material
  439. function updateMaterialDisplayName (old_name, new_name) {
  440. // don't change when new name is the same
  441. if (old_name == new_name) {
  442. return
  443. }
  444. // update the values
  445. base.materialManager.setMaterialName(base.currentMaterialNode, new_name)
  446. materialProperties.name = new_name
  447. }
  448. // update the type of the material
  449. function updateMaterialType (old_type, new_type) {
  450. base.setMetaDataEntry("material", old_type, new_type)
  451. materialProperties.material= new_type
  452. }
  453. // update the brand of the material
  454. function updateMaterialBrand (old_brand, new_brand) {
  455. base.setMetaDataEntry("brand", old_brand, new_brand)
  456. materialProperties.brand = new_brand
  457. }
  458. }