62 self.__fileManager = MicroPythonFileManager(port, self) |
65 self.__fileManager = MicroPythonFileManager(port, self) |
63 |
66 |
64 self.__fileManager.longListFiles.connect(self.__handleLongListFiles) |
67 self.__fileManager.longListFiles.connect(self.__handleLongListFiles) |
65 self.__fileManager.currentDir.connect(self.__handleCurrentDir) |
68 self.__fileManager.currentDir.connect(self.__handleCurrentDir) |
66 self.__fileManager.currentDirChanged.connect(self.__handleCurrentDir) |
69 self.__fileManager.currentDirChanged.connect(self.__handleCurrentDir) |
67 self.__fileManager.putFileDone.connect(self.__handlePutDone) |
70 self.__fileManager.putFileDone.connect(self.__newDeviceList) |
68 self.__fileManager.getFileDone.connect(self.__handleGetDone) |
71 self.__fileManager.getFileDone.connect(self.__handleGetDone) |
69 self.__fileManager.rsyncDone.connect(self.__handleRsyncDone) |
72 self.__fileManager.rsyncDone.connect(self.__handleRsyncDone) |
70 self.__fileManager.rsyncMessages.connect(self.__handleRsyncMessages) |
73 self.__fileManager.rsyncMessages.connect(self.__handleRsyncMessages) |
71 |
74 self.__fileManager.removeDirectoryDone.connect(self.__newDeviceList) |
72 self.__fileManager.longListFilesFailed.connect(self.__handleError) |
75 self.__fileManager.createDirectoryDone.connect(self.__newDeviceList) |
73 self.__fileManager.currentDirFailed.connect(self.__handleError) |
76 self.__fileManager.deleteFileDone.connect(self.__newDeviceList) |
74 self.__fileManager.currentDirChangeFailed.connect(self.__handleError) |
77 self.__fileManager.synchTimeDone.connect(self.__timeSynchronized) |
75 self.__fileManager.putFileFailed.connect(self.__handleError) |
78 self.__fileManager.showTimeDone.connect(self.__deviceTimeReceived) |
76 self.__fileManager.getFileFailed.connect(self.__handleError) |
79 self.__fileManager.showVersionDone.connect( |
77 self.__fileManager.rsyncFailed.connect(self.__handleError) |
80 self.__deviceVersionReceived) |
78 |
81 |
79 # TODO: add context menus for panes (separate menus) |
82 self.__fileManager.error.connect(self.__handleError) |
80 # local pane: |
83 |
81 # Change Directory |
84 self.localFileTreeWidget.customContextMenuRequested.connect( |
82 # |
85 self.__showLocalContextMenu) |
83 # device pane: |
86 self.deviceFileTreeWidget.customContextMenuRequested.connect( |
84 # Change Directory |
87 self.__showDeviceContextMenu) |
85 # Create Directory |
88 |
86 # Delete Directory |
89 self.__localMenu = QMenu(self) |
87 # Delete Directory Tree (= recursive delete) |
90 self.__localMenu.addAction(self.tr("Change Directory"), |
88 # ---------------------------- |
91 self.__changeLocalDirectory) |
89 # Delete File |
92 |
90 # ---------------------------- |
93 self.__deviceMenu = QMenu(self) |
91 # Synchronize Time |
94 self.__deviceMenu.addAction( |
92 # Show Time |
95 self.tr("Change Directory"), self.__changeDeviceDirectory) |
93 # ---------------------------- |
96 self.__deviceMenu.addAction( |
94 # Show Version |
97 self.tr("Create Directory"), self.__createDeviceDirectory) |
|
98 self.__devDelDirAct = self.__deviceMenu.addAction( |
|
99 self.tr("Delete Directory"), self.__deleteDeviceDirectory) |
|
100 self.__devDelDirTreeAct = self.__deviceMenu.addAction( |
|
101 self.tr("Delete Directory Tree"), self.__deleteDeviceDirectoryTree) |
|
102 self.__deviceMenu.addSeparator() |
|
103 self.__devDelFileAct = self.__deviceMenu.addAction( |
|
104 self.tr("Delete File"), self.__deleteDeviceFile) |
|
105 self.__deviceMenu.addSeparator() |
|
106 self.__deviceMenu.addAction( |
|
107 self.tr("Synchronize Time"), self.__synchronizeTime) |
|
108 self.__deviceMenu.addAction( |
|
109 self.tr("Show Time"), self.__showDeviceTime) |
|
110 self.__deviceMenu.addSeparator() |
|
111 self.__deviceMenu.addAction( |
|
112 self.tr("Show Version"), self.__showDeviceVersion) |
95 |
113 |
96 def start(self): |
114 def start(self): |
97 """ |
115 """ |
98 Public method to start the widget. |
116 Public method to start the widget. |
99 """ |
117 """ |
115 """ |
133 """ |
116 Public method to stop the widget. |
134 Public method to stop the widget. |
117 """ |
135 """ |
118 self.__fileManager.disconnect() |
136 self.__fileManager.disconnect() |
119 |
137 |
120 @pyqtSlot(str) |
138 @pyqtSlot(str, str) |
121 def __handleError(self, error): |
139 def __handleError(self, method, error): |
122 """ |
140 """ |
123 Private slot to handle errors. |
141 Private slot to handle errors. |
124 |
142 |
|
143 @param method name of the method the error occured in |
|
144 @type str |
125 @param error error message |
145 @param error error message |
126 @type str |
146 @type str |
127 """ |
147 """ |
128 E5MessageBox.warning( |
148 E5MessageBox.warning( |
129 self, |
149 self, |
130 self.tr("Error handling device"), |
150 self.tr("Error handling device"), |
131 self.tr("<p>There was an error communicating with the connected" |
151 self.tr("<p>There was an error communicating with the connected" |
132 " device.</p><p>Message: {0}</p>").format(error)) |
152 " device.</p><p>Method: {0}</p><p>Message: {1}</p>") |
|
153 .format(method, error)) |
133 |
154 |
134 @pyqtSlot(str) |
155 @pyqtSlot(str) |
135 def __handleCurrentDir(self, dirname): |
156 def __handleCurrentDir(self, dirname): |
136 """ |
157 """ |
137 Private slot to handle a change of the current directory of the device. |
158 Private slot to handle a change of the current directory of the device. |
303 self.tr("Copy File to Device"), |
324 self.tr("Copy File to Device"), |
304 self.tr("<p>The file <b>{0}</b> exists on the" |
325 self.tr("<p>The file <b>{0}</b> exists on the" |
305 " connected device. Overwrite it?</p>") |
326 " connected device. Overwrite it?</p>") |
306 .format(filename) |
327 .format(filename) |
307 ) |
328 ) |
308 if not ok: |
329 if ok: |
309 return |
330 deviceFilename = filename |
310 # TODO: allow to rename the new file |
331 else: |
|
332 deviceFilename, ok = QInputDialog.getText( |
|
333 self, |
|
334 self.tr("Copy File to Device"), |
|
335 self.tr("Enter a new name:"), |
|
336 QLineEdit.Normal, |
|
337 filename) |
|
338 if not ok or not bool(deviceFilename): |
|
339 return |
|
340 else: |
|
341 deviceFilename = filename |
311 |
342 |
312 self.__fileManager.put( |
343 self.__fileManager.put( |
313 os.path.join(self.localCwd.text(), filename), |
344 os.path.join(self.localCwd.text(), filename), |
314 os.path.join(self.deviceCwd.text(), filename) |
345 os.path.join(self.deviceCwd.text(), deviceFilename) |
315 ) |
346 ) |
316 |
347 |
317 @pyqtSlot() |
348 @pyqtSlot() |
318 def on_getButton_clicked(self): |
349 def on_getButton_clicked(self): |
319 """ |
350 """ |
331 self.tr("Copy File from Device"), |
362 self.tr("Copy File from Device"), |
332 self.tr("<p>The file <b>{0}</b> exists locally." |
363 self.tr("<p>The file <b>{0}</b> exists locally." |
333 " Overwrite it?</p>") |
364 " Overwrite it?</p>") |
334 .format(filename) |
365 .format(filename) |
335 ) |
366 ) |
336 if not ok: |
367 if ok: |
337 return |
368 localFilename = filename |
338 # TODO: allow to rename the new file |
369 else: |
|
370 localFilename, ok = QInputDialog.getText( |
|
371 self, |
|
372 self.tr("Copy File from Device"), |
|
373 self.tr("Enter a new name:"), |
|
374 QLineEdit.Normal, |
|
375 filename) |
|
376 if not ok or not bool(localFilename): |
|
377 return |
|
378 else: |
|
379 localFilename = filename |
339 |
380 |
340 self.__fileManager.get( |
381 self.__fileManager.get( |
341 os.path.join(self.deviceCwd.text(), filename), |
382 os.path.join(self.deviceCwd.text(), filename), |
342 os.path.join(self.localCwd.text(), filename) |
383 os.path.join(self.localCwd.text(), localFilename) |
343 ) |
384 ) |
344 |
|
345 @pyqtSlot(str, str) |
|
346 def __handlePutDone(self, localFile, deviceFile): |
|
347 """ |
|
348 Private slot handling a successful copy of a file to the device. |
|
349 |
|
350 @param localFile name of the local file |
|
351 @type str |
|
352 @param deviceFile name of the file on the device |
|
353 @type str |
|
354 """ |
|
355 self.__fileManager.lls(self.deviceCwd.text()) |
|
356 |
385 |
357 @pyqtSlot(str, str) |
386 @pyqtSlot(str, str) |
358 def __handleGetDone(self, deviceFile, localFile): |
387 def __handleGetDone(self, deviceFile, localFile): |
359 """ |
388 """ |
360 Private slot handling a successful copy of a file from the device. |
389 Private slot handling a successful copy of a file from the device. |
404 self.tr("""<p>rsync gave the following messages</p>""" |
433 self.tr("""<p>rsync gave the following messages</p>""" |
405 """<ul><li>{0}</li></ul>""").format( |
434 """<ul><li>{0}</li></ul>""").format( |
406 "</li><li>".join(messages) |
435 "</li><li>".join(messages) |
407 ) |
436 ) |
408 ) |
437 ) |
|
438 |
|
439 @pyqtSlot() |
|
440 def __newDeviceList(self): |
|
441 """ |
|
442 Private slot to initiate a new long list of the device directory. |
|
443 """ |
|
444 self.__fileManager.lls(self.deviceCwd.text()) |
|
445 |
|
446 ################################################################## |
|
447 ## Context menu methods for the local files below |
|
448 ################################################################## |
|
449 |
|
450 @pyqtSlot(QPoint) |
|
451 def __showLocalContextMenu(self, pos): |
|
452 """ |
|
453 Private slot to show the REPL context menu. |
|
454 |
|
455 @param pos position to show the menu at |
|
456 @type QPoint |
|
457 """ |
|
458 self.__localMenu.exec_(self.localFileTreeWidget.mapToGlobal(pos)) |
|
459 |
|
460 @pyqtSlot() |
|
461 def __changeLocalDirectory(self): |
|
462 """ |
|
463 Private slot to change the local directory. |
|
464 """ |
|
465 path, ok = E5PathPickerDialog.getPath( |
|
466 self, |
|
467 self.tr("Change Directory"), |
|
468 self.tr("Select Directory"), |
|
469 E5PathPickerModes.DirectoryShowFilesMode, |
|
470 defaultDirectory=self.localCwd.text(), |
|
471 ) |
|
472 if ok and path: |
|
473 self.localCwd.setText(path) |
|
474 self.__listLocalFiles(path) |
|
475 |
|
476 ################################################################## |
|
477 ## Context menu methods for the device files below |
|
478 ################################################################## |
|
479 |
|
480 @pyqtSlot(QPoint) |
|
481 def __showDeviceContextMenu(self, pos): |
|
482 """ |
|
483 Private slot to show the REPL context menu. |
|
484 |
|
485 @param pos position to show the menu at |
|
486 @type QPoint |
|
487 """ |
|
488 hasSelection = bool(len(self.deviceFileTreeWidget.selectedItems())) |
|
489 if hasSelection: |
|
490 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
|
491 isDir = name.endswith("/") |
|
492 isFile = not isDir |
|
493 else: |
|
494 isDir = False |
|
495 isFile = False |
|
496 self.__devDelDirAct.setEnabled(isDir) |
|
497 self.__devDelDirTreeAct.setEnabled(isDir) |
|
498 self.__devDelFileAct.setEnabled(isFile) |
|
499 |
|
500 self.__deviceMenu.exec_(self.deviceFileTreeWidget.mapToGlobal(pos)) |
|
501 |
|
502 @pyqtSlot() |
|
503 def __changeDeviceDirectory(self): |
|
504 """ |
|
505 Private slot to change the current directory of the device. |
|
506 |
|
507 Note: This triggers a re-population of the device list for the new |
|
508 current directory. |
|
509 """ |
|
510 dirPath, ok = QInputDialog.getText( |
|
511 self, |
|
512 self.tr("Change Directory"), |
|
513 self.tr("Enter the full directory path on the device:"), |
|
514 QLineEdit.Normal, |
|
515 self.deviceCwd.text()) |
|
516 if ok and dirPath: |
|
517 self.__fileManager.cd(dirPath) |
|
518 |
|
519 @pyqtSlot() |
|
520 def __createDeviceDirectory(self): |
|
521 """ |
|
522 Private slot to create a directory on the device. |
|
523 """ |
|
524 dirPath, ok = QInputDialog.getText( |
|
525 self, |
|
526 self.tr("Create Directory"), |
|
527 self.tr("Enter directory name:"), |
|
528 QLineEdit.Normal) |
|
529 if ok and dirPath: |
|
530 self.__fileManager.mkdir(dirPath) |
|
531 |
|
532 @pyqtSlot() |
|
533 def __deleteDeviceDirectory(self): |
|
534 """ |
|
535 Private slot to delete an empty directory on the device. |
|
536 """ |
|
537 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
|
538 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
|
539 dirname = self.deviceCwd.text() + "/" + name[:-1] |
|
540 self.__fileManager.rmdir(dirname) |
|
541 |
|
542 @pyqtSlot() |
|
543 def __deleteDeviceDirectoryTree(self): |
|
544 """ |
|
545 Private slot to delete a directory and all its subdirectories |
|
546 recursively. |
|
547 """ |
|
548 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
|
549 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
|
550 dirname = self.deviceCwd.text() + "/" + name[:-1] |
|
551 self.__fileManager.rmdir(dirname, recursive=True) |
|
552 |
|
553 @pyqtSlot() |
|
554 def __deleteDeviceFile(self): |
|
555 """ |
|
556 Private slot to delete a file |
|
557 """ |
|
558 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
|
559 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
|
560 filename = self.deviceCwd.text() + "/" + name |
|
561 self.__fileManager.delete(filename) |
|
562 |
|
563 @pyqtSlot() |
|
564 def __synchronizeTime(self): |
|
565 """ |
|
566 Private slot to synchronize the local time to the device. |
|
567 """ |
|
568 self.__fileManager.synchronizeTime() |
|
569 |
|
570 @pyqtSlot() |
|
571 def __timeSynchronized(self): |
|
572 """ |
|
573 Private slot handling the successful syncronization of the time. |
|
574 """ |
|
575 E5MessageBox.information( |
|
576 self, |
|
577 self.tr("Synchronize Time"), |
|
578 self.tr("The time of the connected device was synchronized with" |
|
579 " the local time.")) |
|
580 |
|
581 @pyqtSlot() |
|
582 def __showDeviceTime(self): |
|
583 """ |
|
584 Private slot to show the date and time of the connected device. |
|
585 """ |
|
586 self.__fileManager.showTime() |
|
587 |
|
588 @pyqtSlot(str) |
|
589 def __deviceTimeReceived(self, dateTimeString): |
|
590 """ |
|
591 Private slot handling the receipt of the device date and time. |
|
592 |
|
593 @param dateTimeString string containg the date and time of the device |
|
594 @type str |
|
595 """ |
|
596 try: |
|
597 date, time = dateTimeString.strip().split(None, 1) |
|
598 msg = self.tr( |
|
599 "<h3>Device Date and Time</h3>" |
|
600 "<table>" |
|
601 "<tr><td><b>Date</b></td><td>{0}</td></tr>" |
|
602 "<tr><td><b>Time</b></td><td>{1}</td></tr>" |
|
603 "</table>" |
|
604 ).format(date, time) |
|
605 except ValueError: |
|
606 msg = self.tr( |
|
607 "<h3>Device Date and Time</h3>" |
|
608 "<p>{0}</p>" |
|
609 ).format(dateTimeString.strip()) |
|
610 E5MessageBox.information( |
|
611 self, |
|
612 self.tr("Device Date and Time"), |
|
613 msg) |
|
614 |
|
615 @pyqtSlot() |
|
616 def __showDeviceVersion(self): |
|
617 """ |
|
618 Private slot to show some version info about MicroPython of the device. |
|
619 """ |
|
620 self.__fileManager.showVersion() |
|
621 |
|
622 @pyqtSlot(dict) |
|
623 def __deviceVersionReceived(self, versionInfo): |
|
624 """ |
|
625 Private slot handling the receipt of the version info. |
|
626 |
|
627 @param versionInfo dictionary containing the version information |
|
628 @type dict |
|
629 """ |
|
630 if versionInfo: |
|
631 msg = self.tr( |
|
632 "<h3>Device Version Information</h3>" |
|
633 ) |
|
634 msg += "<table>" |
|
635 for key, value in versionInfo.items(): |
|
636 msg += "<tr><td><b>{0}</b></td><td>{1}</td></tr>".format( |
|
637 key, value) |
|
638 msg += "</table>" |
|
639 else: |
|
640 msg = self.tr("No version information available.") |
|
641 E5MessageBox.information( |
|
642 self, |
|
643 self.tr("Device Version Information"), |
|
644 msg) |