8 """ |
8 """ |
9 |
9 |
10 from __future__ import unicode_literals |
10 from __future__ import unicode_literals |
11 |
11 |
12 import os |
12 import os |
|
13 import shutil |
|
14 import time |
13 |
15 |
14 from PyQt5.QtCore import pyqtSlot, Qt, QPoint |
16 from PyQt5.QtCore import pyqtSlot, Qt, QPoint |
15 from PyQt5.QtWidgets import ( |
17 from PyQt5.QtWidgets import ( |
16 QWidget, QTreeWidgetItem, QHeaderView, QMenu, QInputDialog, QLineEdit |
18 QWidget, QTreeWidgetItem, QHeaderView, QMenu, QInputDialog, QLineEdit, |
|
19 QDialog |
17 ) |
20 ) |
18 |
21 |
19 from E5Gui import E5MessageBox, E5PathPickerDialog |
22 from E5Gui import E5MessageBox, E5PathPickerDialog |
20 from E5Gui.E5PathPicker import E5PathPickerModes |
23 from E5Gui.E5PathPicker import E5PathPickerModes |
21 from E5Gui.E5FileSaveConfirmDialog import confirmOverwrite |
24 from E5Gui.E5FileSaveConfirmDialog import confirmOverwrite |
51 |
56 |
52 self.syncButton.setIcon(UI.PixmapCache.getIcon("2rightarrow")) |
57 self.syncButton.setIcon(UI.PixmapCache.getIcon("2rightarrow")) |
53 self.putButton.setIcon(UI.PixmapCache.getIcon("1rightarrow")) |
58 self.putButton.setIcon(UI.PixmapCache.getIcon("1rightarrow")) |
54 self.getButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) |
59 self.getButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) |
55 self.localUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
60 self.localUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
|
61 self.localReloadButton.setIcon(UI.PixmapCache.getIcon("reload")) |
56 self.deviceUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
62 self.deviceUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
|
63 self.deviceReloadButton.setIcon(UI.PixmapCache.getIcon("reload")) |
57 |
64 |
58 self.putButton.setEnabled(False) |
65 self.putButton.setEnabled(False) |
59 self.getButton.setEnabled(False) |
66 self.getButton.setEnabled(False) |
60 |
67 |
61 self.localFileTreeWidget.header().setSortIndicator( |
68 self.localFileTreeWidget.header().setSortIndicator( |
70 self.__fileManager.currentDir.connect(self.__handleCurrentDir) |
77 self.__fileManager.currentDir.connect(self.__handleCurrentDir) |
71 self.__fileManager.currentDirChanged.connect(self.__handleCurrentDir) |
78 self.__fileManager.currentDirChanged.connect(self.__handleCurrentDir) |
72 self.__fileManager.putFileDone.connect(self.__newDeviceList) |
79 self.__fileManager.putFileDone.connect(self.__newDeviceList) |
73 self.__fileManager.getFileDone.connect(self.__handleGetDone) |
80 self.__fileManager.getFileDone.connect(self.__handleGetDone) |
74 self.__fileManager.rsyncDone.connect(self.__handleRsyncDone) |
81 self.__fileManager.rsyncDone.connect(self.__handleRsyncDone) |
75 self.__fileManager.rsyncMessages.connect(self.__handleRsyncMessages) |
|
76 self.__fileManager.rsyncProgressMessage.connect( |
82 self.__fileManager.rsyncProgressMessage.connect( |
77 self.__handleRsyncProgressMessage) |
83 self.__handleRsyncProgressMessage) |
78 self.__fileManager.removeDirectoryDone.connect(self.__newDeviceList) |
84 self.__fileManager.removeDirectoryDone.connect(self.__newDeviceList) |
79 self.__fileManager.createDirectoryDone.connect(self.__newDeviceList) |
85 self.__fileManager.createDirectoryDone.connect(self.__newDeviceList) |
80 self.__fileManager.deleteFileDone.connect(self.__newDeviceList) |
86 self.__fileManager.deleteFileDone.connect(self.__newDeviceList) |
91 self.__showDeviceContextMenu) |
97 self.__showDeviceContextMenu) |
92 |
98 |
93 self.__localMenu = QMenu(self) |
99 self.__localMenu = QMenu(self) |
94 self.__localMenu.addAction(self.tr("Change Directory"), |
100 self.__localMenu.addAction(self.tr("Change Directory"), |
95 self.__changeLocalDirectory) |
101 self.__changeLocalDirectory) |
96 # TODO: add some more local entries |
102 self.__localMenu.addAction( |
97 # TODO: add entry to reload |
103 self.tr("Create Directory"), self.__createLocalDirectory) |
|
104 self.__localDelDirTreeAct = self.__localMenu.addAction( |
|
105 self.tr("Delete Directory Tree"), self.__deleteLocalDirectoryTree) |
|
106 self.__localMenu.addSeparator() |
|
107 self.__localDelFileAct = self.__localMenu.addAction( |
|
108 self.tr("Delete File"), self.__deleteLocalFile) |
|
109 self.__localMenu.addSeparator() |
|
110 self.__localMenu.addAction( |
|
111 self.tr("Show Time"), self.__showLocalTime) |
98 |
112 |
99 self.__deviceMenu = QMenu(self) |
113 self.__deviceMenu = QMenu(self) |
100 self.__deviceMenu.addAction( |
114 self.__deviceMenu.addAction( |
101 self.tr("Change Directory"), self.__changeDeviceDirectory) |
115 self.tr("Change Directory"), self.__changeDeviceDirectory) |
102 self.__deviceMenu.addAction( |
116 self.__deviceMenu.addAction( |
107 self.tr("Delete Directory Tree"), self.__deleteDeviceDirectoryTree) |
121 self.tr("Delete Directory Tree"), self.__deleteDeviceDirectoryTree) |
108 self.__deviceMenu.addSeparator() |
122 self.__deviceMenu.addSeparator() |
109 self.__devDelFileAct = self.__deviceMenu.addAction( |
123 self.__devDelFileAct = self.__deviceMenu.addAction( |
110 self.tr("Delete File"), self.__deleteDeviceFile) |
124 self.tr("Delete File"), self.__deleteDeviceFile) |
111 self.__deviceMenu.addSeparator() |
125 self.__deviceMenu.addSeparator() |
112 # TODO: add entry to reload |
|
113 self.__deviceMenu.addSeparator() |
|
114 self.__deviceMenu.addAction( |
126 self.__deviceMenu.addAction( |
115 self.tr("Synchronize Time"), self.__synchronizeTime) |
127 self.tr("Synchronize Time"), self.__synchronizeTime) |
116 self.__deviceMenu.addAction( |
128 self.__deviceMenu.addAction( |
117 self.tr("Show Time"), self.__showDeviceTime) |
129 self.tr("Show Time"), self.__showDeviceTime) |
118 self.__deviceMenu.addSeparator() |
130 self.__deviceMenu.addSeparator() |
179 @param filesList tuple containing tuples with name, mode, size and time |
191 @param filesList tuple containing tuples with name, mode, size and time |
180 for each directory entry |
192 for each directory entry |
181 @type tuple of (str, str, str, str) |
193 @type tuple of (str, str, str, str) |
182 """ |
194 """ |
183 self.deviceFileTreeWidget.clear() |
195 self.deviceFileTreeWidget.clear() |
184 for name, mode, size, time in filesList: |
196 for name, mode, size, dateTime in filesList: |
185 itm = QTreeWidgetItem(self.deviceFileTreeWidget, |
197 itm = QTreeWidgetItem(self.deviceFileTreeWidget, |
186 [name, mode, size, time]) |
198 [name, mode, size, dateTime]) |
187 itm.setTextAlignment(1, Qt.AlignHCenter) |
199 itm.setTextAlignment(1, Qt.AlignHCenter) |
188 itm.setTextAlignment(2, Qt.AlignRight) |
200 itm.setTextAlignment(2, Qt.AlignRight) |
189 self.deviceFileTreeWidget.header().resizeSections( |
201 self.deviceFileTreeWidget.header().resizeSections( |
190 QHeaderView.ResizeToContents) |
202 QHeaderView.ResizeToContents) |
191 |
203 |
256 """ |
268 """ |
257 cwd = self.localCwd.text() |
269 cwd = self.localCwd.text() |
258 dirname = os.path.dirname(cwd) |
270 dirname = os.path.dirname(cwd) |
259 self.__listLocalFiles(dirname) |
271 self.__listLocalFiles(dirname) |
260 |
272 |
|
273 @pyqtSlot() |
|
274 def on_localReloadButton_clicked(self): |
|
275 """ |
|
276 Private slot to reload the local list. |
|
277 """ |
|
278 dirname = self.localCwd.text() |
|
279 self.__listLocalFiles(dirname) |
|
280 |
261 @pyqtSlot(QTreeWidgetItem, int) |
281 @pyqtSlot(QTreeWidgetItem, int) |
262 def on_deviceFileTreeWidget_itemActivated(self, item, column): |
282 def on_deviceFileTreeWidget_itemActivated(self, item, column): |
263 """ |
283 """ |
264 Private slot to handle the activation of a device item. |
284 Private slot to handle the activation of a device item. |
265 |
285 |
294 Private slot to go up one directory level on the device. |
314 Private slot to go up one directory level on the device. |
295 """ |
315 """ |
296 cwd = self.deviceCwd.text() |
316 cwd = self.deviceCwd.text() |
297 dirname = os.path.dirname(cwd) |
317 dirname = os.path.dirname(cwd) |
298 self.__fileManager.cd(dirname) |
318 self.__fileManager.cd(dirname) |
|
319 |
|
320 @pyqtSlot() |
|
321 def on_deviceReloadButton_clicked(self): |
|
322 """ |
|
323 Private slot to reload the device list. |
|
324 """ |
|
325 dirname = self.deviceCwd.text() |
|
326 if dirname: |
|
327 self.__fileManager.lls(dirname) |
|
328 else: |
|
329 self.__fileManager.pwd() |
299 |
330 |
300 def __isFileInList(self, filename, treeWidget): |
331 def __isFileInList(self, filename, treeWidget): |
301 """ |
332 """ |
302 Private method to check, if a file name is contained in a tree widget. |
333 Private method to check, if a file name is contained in a tree widget. |
303 |
334 |
325 filename = selectedItems[0].text(0).strip() |
356 filename = selectedItems[0].text(0).strip() |
326 if not filename.endswith("/"): |
357 if not filename.endswith("/"): |
327 # it is really a file |
358 # it is really a file |
328 if self.__isFileInList(filename, self.deviceFileTreeWidget): |
359 if self.__isFileInList(filename, self.deviceFileTreeWidget): |
329 # ask for overwrite permission |
360 # ask for overwrite permission |
330 # TODO: test this |
|
331 action, resultFilename = confirmOverwrite( |
361 action, resultFilename = confirmOverwrite( |
332 filename, self.tr("Copy File to Device"), |
362 filename, self.tr("Copy File to Device"), |
333 self.tr("The given file exists already" |
363 self.tr("The given file exists already" |
334 " (Enter file name only)."), |
364 " (Enter file name only)."), |
335 False, self) |
365 False, self) |
357 filename = selectedItems[0].text(0).strip() |
387 filename = selectedItems[0].text(0).strip() |
358 if not filename.endswith("/"): |
388 if not filename.endswith("/"): |
359 # it is really a file |
389 # it is really a file |
360 if self.__isFileInList(filename, self.localFileTreeWidget): |
390 if self.__isFileInList(filename, self.localFileTreeWidget): |
361 # ask for overwrite permission |
391 # ask for overwrite permission |
362 # TODO: test this |
|
363 action, resultFilename = confirmOverwrite( |
392 action, resultFilename = confirmOverwrite( |
364 filename, self.tr("Copy File from Device"), |
393 filename, self.tr("Copy File from Device"), |
365 self.tr("The given file exists already."), |
394 self.tr("The given file exists already."), |
366 True, self) |
395 True, self) |
367 if action == "cancel": |
396 if action == "cancel": |
416 @type str |
444 @type str |
417 """ |
445 """ |
418 self.__listLocalFiles(self.localCwd.text()) |
446 self.__listLocalFiles(self.localCwd.text()) |
419 self.__fileManager.lls(self.deviceCwd.text()) |
447 self.__fileManager.lls(self.deviceCwd.text()) |
420 |
448 |
421 @pyqtSlot(list) |
|
422 def __handleRsyncMessages(self, messages): |
|
423 """ |
|
424 Private slot to handle messages from the rsync operation. |
|
425 |
|
426 @param messages list of message generated by the rsync operation |
|
427 @type list |
|
428 """ |
|
429 E5MessageBox.information( |
|
430 self, |
|
431 self.tr("rsync Messages"), |
|
432 self.tr("""<p>rsync gave the following messages</p>""" |
|
433 """<ul><li>{0}</li></ul>""").format( |
|
434 "</li><li>".join(messages) |
|
435 ) |
|
436 ) |
|
437 |
|
438 @pyqtSlot(str) |
449 @pyqtSlot(str) |
439 def __handleRsyncProgressMessage(self, message): |
450 def __handleRsyncProgressMessage(self, message): |
440 """ |
451 """ |
441 Private slot handling progress messages sent by the file manager. |
452 Private slot handling progress messages sent by the file manager. |
442 """ |
453 |
443 # TODO: not implemented yet |
454 @param message message to be shown |
444 # create and open the info dialog, if it is None |
455 @type str |
445 # connect to the dialog finished(int) signal to set the memeber variable to None |
456 """ |
446 # add messages to dialog |
457 if self.__progressInfoDialog is None: |
|
458 from .MicroPythonProgressInfoDialog import ( |
|
459 MicroPythonProgressInfoDialog |
|
460 ) |
|
461 self.__progressInfoDialog = MicroPythonProgressInfoDialog(self) |
|
462 self.__progressInfoDialog.finished.connect( |
|
463 self.__progressInfoDialogFinished) |
|
464 self.__progressInfoDialog.show() |
|
465 self.__progressInfoDialog.addMessage(message) |
|
466 |
|
467 @pyqtSlot() |
|
468 def __progressInfoDialogFinished(self): |
|
469 """ |
|
470 Private slot handling the closing of the progress info dialog. |
|
471 """ |
|
472 self.__progressInfoDialog.deleteLater() |
|
473 self.__progressInfoDialog = None |
447 |
474 |
448 @pyqtSlot() |
475 @pyqtSlot() |
449 def __newDeviceList(self): |
476 def __newDeviceList(self): |
450 """ |
477 """ |
451 Private slot to initiate a new long list of the device directory. |
478 Private slot to initiate a new long list of the device directory. |
482 if not os.path.isabs(dirPath): |
520 if not os.path.isabs(dirPath): |
483 dirPath = os.path.join(self.localCwd.text(), dirPath) |
521 dirPath = os.path.join(self.localCwd.text(), dirPath) |
484 self.localCwd.setText(dirPath) |
522 self.localCwd.setText(dirPath) |
485 self.__listLocalFiles(dirPath) |
523 self.__listLocalFiles(dirPath) |
486 |
524 |
|
525 # TODO: test this |
|
526 @pyqtSlot() |
|
527 def __createLocalDirectory(self): |
|
528 """ |
|
529 Private slot to create a local directory. |
|
530 """ |
|
531 dirPath, ok = QInputDialog.getText( |
|
532 self, |
|
533 self.tr("Create Directory"), |
|
534 self.tr("Enter directory name:"), |
|
535 QLineEdit.Normal) |
|
536 if ok and dirPath: |
|
537 dirPath = os.path.join(self.localCwd.text(), dirPath) |
|
538 try: |
|
539 os.mkdir(dirPath) |
|
540 except (OSError, IOError) as exc: |
|
541 E5MessageBox.critical( |
|
542 self, |
|
543 self.tr("Create Directory"), |
|
544 self.tr("""<p>The directory <b>{0}</b> could not be""" |
|
545 """ created.</p><p>Reason: {1}</p>""").format( |
|
546 dirPath, str(exc)) |
|
547 ) |
|
548 |
|
549 # TODO: test this |
|
550 @pyqtSlot() |
|
551 def __deleteLocalDirectoryTree(self): |
|
552 """ |
|
553 Private slot to delete a local directory tree. |
|
554 """ |
|
555 if bool(len(self.localFileTreeWidget.selectedItems())): |
|
556 name = self.localFileTreeWidget.selectedItems()[0].text(0) |
|
557 dirname = os.path.join(self.localCwd.text(), name[:-1]) |
|
558 dlg = DeleteFilesConfirmationDialog( |
|
559 self, |
|
560 self.tr("Delete Directory Tree"), |
|
561 self.tr( |
|
562 "Do you really want to delete this directory tree?"), |
|
563 [dirname]) |
|
564 if dlg.exec_() == QDialog.Accepted: |
|
565 try: |
|
566 shutil.rmtree(dirname) |
|
567 except Exception as exc: |
|
568 E5MessageBox.critical( |
|
569 self, |
|
570 self.tr("Delete Directory Tree"), |
|
571 self.tr("""<p>The directory <b>{0}</b> could not be""" |
|
572 """ deleted.</p><p>Reason: {1}</p>""").format( |
|
573 dirname, str(exc)) |
|
574 ) |
|
575 |
|
576 # TODO: test this |
|
577 @pyqtSlot() |
|
578 def __deleteLocalFile(self): |
|
579 """ |
|
580 Private slot to delete a local file. |
|
581 """ |
|
582 if bool(len(self.localFileTreeWidget.selectedItems())): |
|
583 name = self.localFileTreeWidget.selectedItems()[0].text(0) |
|
584 filename = os.path.join(self.localCwd.text(), name) |
|
585 dlg = DeleteFilesConfirmationDialog( |
|
586 self, |
|
587 self.tr("Delete File"), |
|
588 self.tr( |
|
589 "Do you really want to delete this file?"), |
|
590 [filename]) |
|
591 if dlg.exec_() == QDialog.Accepted: |
|
592 try: |
|
593 os.remove(filename) |
|
594 except (OSError, IOError) as exc: |
|
595 E5MessageBox.critical( |
|
596 self, |
|
597 self.tr("Delete File"), |
|
598 self.tr("""<p>The file <b>{0}</b> could not be""" |
|
599 """ deleted.</p><p>Reason: {1}</p>""").format( |
|
600 filename, str(exc)) |
|
601 ) |
|
602 |
|
603 # TODO: test this |
|
604 @pyqtSlot() |
|
605 def __showLocalTime(self): |
|
606 """ |
|
607 Private slot to show the local date and time. |
|
608 """ |
|
609 localdatetime = time.localtime() |
|
610 loacldate = time.strftime('%Y-%m-%d', localdatetime) |
|
611 localtime = time.strftime('%H:%M:%S', localdatetime) |
|
612 E5MessageBox.information( |
|
613 self, |
|
614 self.tr("Local Date and Time"), |
|
615 self.tr("<h3>Local Date and Time</h3>" |
|
616 "<table>" |
|
617 "<tr><td><b>Date</b></td><td>{0}</td></tr>" |
|
618 "<tr><td><b>Time</b></td><td>{1}</td></tr>" |
|
619 "</table>" |
|
620 ).format(loacldate, localtime) |
|
621 ) |
|
622 |
487 ################################################################## |
623 ################################################################## |
488 ## Context menu methods for the device files below |
624 ## Context menu methods for the device files below |
489 ################################################################## |
625 ################################################################## |
490 |
626 |
491 @pyqtSlot(QPoint) |
627 @pyqtSlot(QPoint) |
550 Private slot to delete an empty directory on the device. |
686 Private slot to delete an empty directory on the device. |
551 """ |
687 """ |
552 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
688 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
553 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
689 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
554 dirname = self.deviceCwd.text() + "/" + name[:-1] |
690 dirname = self.deviceCwd.text() + "/" + name[:-1] |
555 # TODO: add confirmation |
691 dlg = DeleteFilesConfirmationDialog( |
556 self.__fileManager.rmdir(dirname) |
692 self, |
|
693 self.tr("Delete Directory"), |
|
694 self.tr( |
|
695 "Do you really want to delete this directory?"), |
|
696 [dirname]) |
|
697 if dlg.exec_() == QDialog.Accepted: |
|
698 self.__fileManager.rmdir(dirname) |
557 |
699 |
558 @pyqtSlot() |
700 @pyqtSlot() |
559 def __deleteDeviceDirectoryTree(self): |
701 def __deleteDeviceDirectoryTree(self): |
560 """ |
702 """ |
561 Private slot to delete a directory and all its subdirectories |
703 Private slot to delete a directory and all its subdirectories |
562 recursively. |
704 recursively. |
563 """ |
705 """ |
564 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
706 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
565 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
707 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
566 dirname = self.deviceCwd.text() + "/" + name[:-1] |
708 dirname = self.deviceCwd.text() + "/" + name[:-1] |
567 # TODO: add confirmation |
709 dlg = DeleteFilesConfirmationDialog( |
568 self.__fileManager.rmdir(dirname, recursive=True) |
710 self, |
|
711 self.tr("Delete Directory Tree"), |
|
712 self.tr( |
|
713 "Do you really want to delete this directory tree?"), |
|
714 [dirname]) |
|
715 if dlg.exec_() == QDialog.Accepted: |
|
716 self.__fileManager.rmdir(dirname, recursive=True) |
569 |
717 |
570 @pyqtSlot() |
718 @pyqtSlot() |
571 def __deleteDeviceFile(self): |
719 def __deleteDeviceFile(self): |
572 """ |
720 """ |
573 Private slot to delete a file |
721 Private slot to delete a file. |
574 """ |
722 """ |
575 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
723 if bool(len(self.deviceFileTreeWidget.selectedItems())): |
576 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
724 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) |
577 filename = self.deviceCwd.text() + "/" + name |
725 filename = self.deviceCwd.text() + "/" + name |
578 # TODO: add confirmation |
726 dlg = DeleteFilesConfirmationDialog( |
579 self.__fileManager.delete(filename) |
727 self, |
|
728 self.tr("Delete File"), |
|
729 self.tr( |
|
730 "Do you really want to delete this file?"), |
|
731 [filename]) |
|
732 if dlg.exec_() == QDialog.Accepted: |
|
733 self.__fileManager.delete(filename) |
580 |
734 |
581 @pyqtSlot() |
735 @pyqtSlot() |
582 def __synchronizeTime(self): |
736 def __synchronizeTime(self): |
583 """ |
737 """ |
584 Private slot to synchronize the local time to the device. |
738 Private slot to synchronize the local time to the device. |