DiscoverUM3Action.qml 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. // Copyright (c) 2019 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import UM 1.2 as UM
  4. import Cura 1.5 as Cura
  5. import QtQuick 2.2
  6. import QtQuick.Controls 1.1
  7. import QtQuick.Layouts 1.1
  8. import QtQuick.Window 2.1
  9. import QtQuick.Dialogs 1.2
  10. Cura.MachineAction
  11. {
  12. id: base
  13. anchors.fill: parent;
  14. property alias currentItemIndex: listview.currentIndex
  15. property var selectedDevice: null
  16. property bool completeProperties: true
  17. // For validating IP addresses
  18. property var networkingUtil: Cura.NetworkingUtil {}
  19. function connectToPrinter()
  20. {
  21. if(base.selectedDevice && base.completeProperties)
  22. {
  23. var printerKey = base.selectedDevice.key
  24. var printerName = base.selectedDevice.name // TODO To change when the groups have a name
  25. if (manager.getStoredKey() != printerKey)
  26. {
  27. // Check if there is another instance with the same key
  28. if (!manager.existsKey(printerKey))
  29. {
  30. manager.associateActiveMachineWithPrinterDevice(base.selectedDevice)
  31. manager.setGroupName(printerName) // TODO To change when the groups have a name
  32. completed()
  33. }
  34. else
  35. {
  36. existingConnectionDialog.open()
  37. }
  38. }
  39. }
  40. }
  41. MessageDialog
  42. {
  43. id: existingConnectionDialog
  44. title: catalog.i18nc("@window:title", "Existing Connection")
  45. icon: StandardIcon.Information
  46. text: catalog.i18nc("@message:text", "This printer/group is already added to Cura. Please select another printer/group.")
  47. standardButtons: StandardButton.Ok
  48. modality: Qt.ApplicationModal
  49. }
  50. Column
  51. {
  52. anchors.fill: parent;
  53. id: discoverUM3Action
  54. spacing: UM.Theme.getSize("default_margin").height
  55. SystemPalette { id: palette }
  56. UM.I18nCatalog { id: catalog; name:"cura" }
  57. Label
  58. {
  59. id: pageTitle
  60. width: parent.width
  61. text: catalog.i18nc("@title:window", "Connect to Networked Printer")
  62. wrapMode: Text.WordWrap
  63. renderType: Text.NativeRendering
  64. font.pointSize: 18
  65. }
  66. Label
  67. {
  68. id: pageDescription
  69. width: parent.width
  70. wrapMode: Text.WordWrap
  71. renderType: Text.NativeRendering
  72. text: catalog.i18nc("@label", "To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.") + "\n\n" + catalog.i18nc("@label", "Select your printer from the list below:")
  73. }
  74. Row
  75. {
  76. spacing: UM.Theme.getSize("default_lining").width
  77. Button
  78. {
  79. id: addButton
  80. text: catalog.i18nc("@action:button", "Add");
  81. onClicked:
  82. {
  83. manualPrinterDialog.showDialog("", "");
  84. }
  85. }
  86. Button
  87. {
  88. id: editButton
  89. text: catalog.i18nc("@action:button", "Edit")
  90. enabled: base.selectedDevice != null && base.selectedDevice.getProperty("manual") == "true"
  91. onClicked:
  92. {
  93. manualPrinterDialog.showDialog(base.selectedDevice.key, base.selectedDevice.ipAddress);
  94. }
  95. }
  96. Button
  97. {
  98. id: removeButton
  99. text: catalog.i18nc("@action:button", "Remove")
  100. enabled: base.selectedDevice != null && base.selectedDevice.getProperty("manual") == "true"
  101. onClicked: manager.removeManualDevice(base.selectedDevice.key, base.selectedDevice.ipAddress)
  102. }
  103. Button
  104. {
  105. id: rediscoverButton
  106. text: catalog.i18nc("@action:button", "Refresh")
  107. onClicked: manager.restartDiscovery()
  108. }
  109. }
  110. Row
  111. {
  112. id: contentRow
  113. width: parent.width
  114. spacing: UM.Theme.getSize("default_margin").width
  115. Column
  116. {
  117. width: Math.round(parent.width * 0.5)
  118. spacing: UM.Theme.getSize("default_margin").height
  119. ScrollView
  120. {
  121. id: objectListContainer
  122. frameVisible: true
  123. width: parent.width
  124. height: base.height - contentRow.y - discoveryTip.height
  125. Rectangle
  126. {
  127. parent: viewport
  128. anchors.fill: parent
  129. color: palette.light
  130. }
  131. ListView
  132. {
  133. id: listview
  134. model: manager.foundDevices
  135. onModelChanged:
  136. {
  137. var selectedKey = manager.getLastManualEntryKey()
  138. // If there is no last manual entry key, then we select the stored key (if any)
  139. if (selectedKey == "")
  140. selectedKey = manager.getStoredKey()
  141. for(var i = 0; i < model.length; i++) {
  142. if(model[i].key == selectedKey)
  143. {
  144. currentIndex = i;
  145. return
  146. }
  147. }
  148. currentIndex = -1;
  149. }
  150. width: parent.width
  151. currentIndex: -1
  152. onCurrentIndexChanged:
  153. {
  154. base.selectedDevice = listview.model[currentIndex];
  155. // Only allow connecting if the printer has responded to API query since the last refresh
  156. base.completeProperties = base.selectedDevice != null && base.selectedDevice.getProperty("incomplete") != "true";
  157. }
  158. Component.onCompleted: manager.startDiscovery()
  159. delegate: Rectangle
  160. {
  161. height: childrenRect.height
  162. color: ListView.isCurrentItem ? palette.highlight : index % 2 ? palette.base : palette.alternateBase
  163. width: parent.width
  164. Label
  165. {
  166. anchors.left: parent.left
  167. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  168. anchors.right: parent.right
  169. text: listview.model[index].name
  170. color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text
  171. elide: Text.ElideRight
  172. renderType: Text.NativeRendering
  173. }
  174. MouseArea
  175. {
  176. anchors.fill: parent;
  177. onClicked:
  178. {
  179. if(!parent.ListView.isCurrentItem)
  180. {
  181. parent.ListView.view.currentIndex = index;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. }
  188. Label
  189. {
  190. id: discoveryTip
  191. anchors.left: parent.left
  192. anchors.right: parent.right
  193. wrapMode: Text.WordWrap
  194. renderType: Text.NativeRendering
  195. text: catalog.i18nc("@label", "If your printer is not listed, read the <a href='%1'>network printing troubleshooting guide</a>").arg("https://ultimaker.com/en/troubleshooting");
  196. onLinkActivated: Qt.openUrlExternally(link)
  197. }
  198. }
  199. Column
  200. {
  201. width: Math.round(parent.width * 0.5)
  202. visible: base.selectedDevice ? true : false
  203. spacing: UM.Theme.getSize("default_margin").height
  204. Label
  205. {
  206. width: parent.width
  207. wrapMode: Text.WordWrap
  208. text: base.selectedDevice ? base.selectedDevice.name : ""
  209. font: UM.Theme.getFont("large_bold")
  210. elide: Text.ElideRight
  211. renderType: Text.NativeRendering
  212. }
  213. Grid
  214. {
  215. visible: base.completeProperties
  216. width: parent.width
  217. columns: 2
  218. Label
  219. {
  220. width: Math.round(parent.width * 0.5)
  221. wrapMode: Text.WordWrap
  222. renderType: Text.NativeRendering
  223. text: catalog.i18nc("@label", "Type")
  224. }
  225. Label
  226. {
  227. width: Math.round(parent.width * 0.5)
  228. wrapMode: Text.WordWrap
  229. renderType: Text.NativeRendering
  230. text:
  231. {
  232. if(base.selectedDevice)
  233. {
  234. if (base.selectedDevice.printerType == "ultimaker3")
  235. {
  236. return "Ultimaker 3";
  237. }
  238. else if (base.selectedDevice.printerType == "ultimaker3_extended")
  239. {
  240. return "Ultimaker 3 Extended";
  241. }
  242. else if (base.selectedDevice.printerType == "ultimaker_s5")
  243. {
  244. return "Ultimaker S5";
  245. }
  246. else
  247. {
  248. return catalog.i18nc("@label", "Unknown") // We have no idea what type it is. Should not happen 'in the field'
  249. }
  250. }
  251. else
  252. {
  253. return ""
  254. }
  255. }
  256. }
  257. Label
  258. {
  259. width: Math.round(parent.width * 0.5)
  260. wrapMode: Text.WordWrap
  261. renderType: Text.NativeRendering
  262. text: catalog.i18nc("@label", "Firmware version")
  263. }
  264. Label
  265. {
  266. width: Math.round(parent.width * 0.5)
  267. wrapMode: Text.WordWrap
  268. renderType: Text.NativeRendering
  269. text: base.selectedDevice ? base.selectedDevice.firmwareVersion : ""
  270. }
  271. Label
  272. {
  273. width: Math.round(parent.width * 0.5)
  274. wrapMode: Text.WordWrap
  275. renderType: Text.NativeRendering
  276. text: catalog.i18nc("@label", "Address")
  277. }
  278. Label
  279. {
  280. width: Math.round(parent.width * 0.5)
  281. wrapMode: Text.WordWrap
  282. renderType: Text.NativeRendering
  283. text: base.selectedDevice ? base.selectedDevice.ipAddress : ""
  284. }
  285. }
  286. Label
  287. {
  288. width: parent.width
  289. wrapMode: Text.WordWrap
  290. renderType: Text.NativeRendering
  291. text:{
  292. // The property cluster size does not exist for older UM3 devices.
  293. if(!base.selectedDevice || base.selectedDevice.clusterSize == null || base.selectedDevice.clusterSize == 1)
  294. {
  295. return "";
  296. }
  297. else if (base.selectedDevice.clusterSize === 0)
  298. {
  299. return catalog.i18nc("@label", "This printer is not set up to host a group of printers.");
  300. }
  301. else
  302. {
  303. return catalog.i18nc("@label", "This printer is the host for a group of %1 printers.".arg(base.selectedDevice.clusterSize));
  304. }
  305. }
  306. }
  307. Label
  308. {
  309. width: parent.width
  310. wrapMode: Text.WordWrap
  311. renderType: Text.NativeRendering
  312. visible: base.selectedDevice != null && !base.completeProperties
  313. text: catalog.i18nc("@label", "The printer at this address has not yet responded." )
  314. }
  315. Button
  316. {
  317. text: catalog.i18nc("@action:button", "Connect")
  318. enabled: (base.selectedDevice && base.completeProperties && base.selectedDevice.clusterSize > 0) ? true : false
  319. onClicked: connectToPrinter()
  320. }
  321. }
  322. }
  323. }
  324. MessageDialog
  325. {
  326. id: invalidIPAddressMessageDialog
  327. x: (parent.x + (parent.width) / 2) | 0
  328. y: (parent.y + (parent.height) / 2) | 0
  329. title: catalog.i18nc("@title:window", "Invalid IP address")
  330. text: catalog.i18nc("@text", "Please enter a valid IP address.")
  331. icon: StandardIcon.Warning
  332. standardButtons: StandardButton.Ok
  333. }
  334. UM.Dialog
  335. {
  336. id: manualPrinterDialog
  337. property string printerKey
  338. property alias addressText: addressField.text
  339. title: catalog.i18nc("@title:window", "Printer Address")
  340. minimumWidth: 400 * screenScaleFactor
  341. minimumHeight: 130 * screenScaleFactor
  342. width: minimumWidth
  343. height: minimumHeight
  344. signal showDialog(string key, string address)
  345. onShowDialog:
  346. {
  347. printerKey = key;
  348. addressText = address;
  349. manualPrinterDialog.show();
  350. addressField.selectAll();
  351. addressField.focus = true;
  352. }
  353. Column {
  354. anchors.fill: parent
  355. spacing: UM.Theme.getSize("default_margin").height
  356. Label
  357. {
  358. text: catalog.i18nc("@label", "Enter the IP address or hostname of your printer on the network.")
  359. width: parent.width
  360. wrapMode: Text.WordWrap
  361. renderType: Text.NativeRendering
  362. }
  363. TextField
  364. {
  365. id: addressField
  366. width: parent.width
  367. validator: RegExpValidator
  368. {
  369. regExp: /[a-zA-Z0-9\.\-\_]*/
  370. }
  371. onAccepted: btnOk.clicked()
  372. }
  373. }
  374. rightButtons: [
  375. Button {
  376. text: catalog.i18nc("@action:button","Cancel")
  377. onClicked:
  378. {
  379. manualPrinterDialog.reject()
  380. manualPrinterDialog.hide()
  381. }
  382. },
  383. Button {
  384. id: btnOk
  385. text: catalog.i18nc("@action:button", "OK")
  386. onClicked:
  387. {
  388. // Validate the input first
  389. if (!networkingUtil.isValidIP(manualPrinterDialog.addressText))
  390. {
  391. invalidIPAddressMessageDialog.open()
  392. return
  393. }
  394. // if the entered IP address has already been discovered, switch the current item to that item
  395. // and do nothing else.
  396. for (var i = 0; i < manager.foundDevices.length; i++)
  397. {
  398. var device = manager.foundDevices[i]
  399. if (device.address == manualPrinterDialog.addressText)
  400. {
  401. currentItemIndex = i
  402. manualPrinterDialog.hide()
  403. return
  404. }
  405. }
  406. manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText)
  407. manualPrinterDialog.hide()
  408. }
  409. enabled: manualPrinterDialog.addressText.trim() != ""
  410. isDefault: true
  411. }
  412. ]
  413. }
  414. }