eric6/MicroPython/MicroPythonFileManagerWidget.py

branch
micropython
changeset 7082
ec199ef0cfc6
parent 7081
ed510767c096
child 7083
217862c28319
equal deleted inserted replaced
7081:ed510767c096 7082:ec199ef0cfc6
9 9
10 from __future__ import unicode_literals 10 from __future__ import unicode_literals
11 11
12 import os 12 import os
13 13
14 from PyQt5.QtCore import pyqtSlot, Qt 14 from PyQt5.QtCore import pyqtSlot, Qt, QPoint
15 from PyQt5.QtWidgets import QWidget, QTreeWidgetItem, QHeaderView 15 from PyQt5.QtWidgets import (
16 16 QWidget, QTreeWidgetItem, QHeaderView, QMenu, QInputDialog, QLineEdit
17 from E5Gui import E5MessageBox 17 )
18
19 from E5Gui import E5MessageBox, E5PathPickerDialog
20 from E5Gui.E5PathPicker import E5PathPickerModes
18 from E5Gui.E5Application import e5App 21 from E5Gui.E5Application import e5App
19 22
20 from .Ui_MicroPythonFileManagerWidget import Ui_MicroPythonFileManagerWidget 23 from .Ui_MicroPythonFileManagerWidget import Ui_MicroPythonFileManagerWidget
21 24
22 from .MicroPythonFileSystem import MicroPythonFileManager 25 from .MicroPythonFileSystem import MicroPythonFileManager
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)

eric ide

mercurial