eric6/MicroPython/MicroPythonFileManagerWidget.py

branch
micropython
changeset 7081
ed510767c096
parent 7080
9a3adf033f90
child 7082
ec199ef0cfc6
equal deleted inserted replaced
7080:9a3adf033f90 7081:ed510767c096
17 from E5Gui import E5MessageBox 17 from E5Gui import E5MessageBox
18 from E5Gui.E5Application import e5App 18 from E5Gui.E5Application import e5App
19 19
20 from .Ui_MicroPythonFileManagerWidget import Ui_MicroPythonFileManagerWidget 20 from .Ui_MicroPythonFileManagerWidget import Ui_MicroPythonFileManagerWidget
21 21
22 from .MicroPythonFileSystem import ( 22 from .MicroPythonFileSystem import MicroPythonFileManager
23 MicroPythonFileManager, decoratedName, mode2string, mtime2string 23 from .MicroPythonFileSystemUtilities import (
24 mtime2string, mode2string, decoratedName, listdirStat
24 ) 25 )
25 26
26 import UI.PixmapCache 27 import UI.PixmapCache
27 import Preferences 28 import Preferences
28 import Utilities 29 import Utilities
42 @type QWidget 43 @type QWidget
43 """ 44 """
44 super(MicroPythonFileManagerWidget, self).__init__(parent) 45 super(MicroPythonFileManagerWidget, self).__init__(parent)
45 self.setupUi(self) 46 self.setupUi(self)
46 47
48 self.syncButton.setIcon(UI.PixmapCache.getIcon("2rightarrow"))
47 self.putButton.setIcon(UI.PixmapCache.getIcon("1rightarrow")) 49 self.putButton.setIcon(UI.PixmapCache.getIcon("1rightarrow"))
48 self.getButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) 50 self.getButton.setIcon(UI.PixmapCache.getIcon("1leftarrow"))
49 self.localUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) 51 self.localUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
50 self.deviceUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) 52 self.deviceUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
51 53
59 61
60 self.__fileManager = MicroPythonFileManager(port, self) 62 self.__fileManager = MicroPythonFileManager(port, self)
61 63
62 self.__fileManager.longListFiles.connect(self.__handleLongListFiles) 64 self.__fileManager.longListFiles.connect(self.__handleLongListFiles)
63 self.__fileManager.currentDir.connect(self.__handleCurrentDir) 65 self.__fileManager.currentDir.connect(self.__handleCurrentDir)
66 self.__fileManager.currentDirChanged.connect(self.__handleCurrentDir)
67 self.__fileManager.putFileDone.connect(self.__handlePutDone)
68 self.__fileManager.getFileDone.connect(self.__handleGetDone)
69 self.__fileManager.rsyncDone.connect(self.__handleRsyncDone)
70 self.__fileManager.rsyncMessages.connect(self.__handleRsyncMessages)
64 71
65 self.__fileManager.longListFilesFailed.connect(self.__handleError) 72 self.__fileManager.longListFilesFailed.connect(self.__handleError)
66 self.__fileManager.currentDirFailed.connect(self.__handleError) 73 self.__fileManager.currentDirFailed.connect(self.__handleError)
74 self.__fileManager.currentDirChangeFailed.connect(self.__handleError)
75 self.__fileManager.putFileFailed.connect(self.__handleError)
76 self.__fileManager.getFileFailed.connect(self.__handleError)
77 self.__fileManager.rsyncFailed.connect(self.__handleError)
78
79 # TODO: add context menus for panes (separate menus)
80 # local pane:
81 # Change Directory
82 #
83 # device pane:
84 # Change Directory
85 # Create Directory
86 # Delete Directory
87 # Delete Directory Tree (= recursive delete)
88 # ----------------------------
89 # Delete File
90 # ----------------------------
91 # Synchronize Time
92 # Show Time
93 # ----------------------------
94 # Show Version
67 95
68 def start(self): 96 def start(self):
69 """ 97 """
70 Public method to start the widget. 98 Public method to start the widget.
71 """ 99 """
137 Private method to populate the local files list. 165 Private method to populate the local files list.
138 166
139 @param dirname name of the local directory to be listed 167 @param dirname name of the local directory to be listed
140 @type str 168 @type str
141 """ # __IGNORE_WARNING_D234__ 169 """ # __IGNORE_WARNING_D234__
142 def isvisible(name):
143 return not name.startswith(".") and not name.endswith("~")
144
145 def stat(filename):
146 try:
147 rstat = os.lstat(filename)
148 except Exception:
149 rstat = os.stat(filename)
150 return tuple(rstat)
151
152 def listdir_stat(dirname):
153 try:
154 if dirname:
155 files = os.listdir(dirname)
156 else:
157 files = os.listdir()
158 except OSError:
159 return []
160 if dirname in ('', '/'):
161 return [(f, stat(f)) for f in files if isvisible(f)]
162 return [(f, stat(os.path.join(dirname, f))) for f in files
163 if isvisible(f)]
164 170
165 if not dirname: 171 if not dirname:
166 dirname = os.getcwd() 172 dirname = os.getcwd()
167 if dirname.endswith(os.sep): 173 if dirname.endswith(os.sep):
168 dirname = dirname[:-1] 174 dirname = dirname[:-1]
169 self.localCwd.setText(dirname) 175 self.localCwd.setText(dirname)
170 176
171 filesStatList = listdir_stat(dirname) 177 filesStatList = listdirStat(dirname)
172 filesList = [( 178 filesList = [(
173 decoratedName(f, s[0], os.path.isdir(os.path.join(dirname, f))), 179 decoratedName(f, s[0], os.path.isdir(os.path.join(dirname, f))),
174 mode2string(s[0]), 180 mode2string(s[0]),
175 str(s[6]), 181 str(s[6]),
176 mtime2string(s[8])) for f, s in filesStatList] 182 mtime2string(s[8])) for f, s in filesStatList]
196 @type int 202 @type int
197 """ 203 """
198 name = os.path.join(self.localCwd.text(), item.text(0)) 204 name = os.path.join(self.localCwd.text(), item.text(0))
199 if name.endswith("/"): 205 if name.endswith("/"):
200 # directory names end with a '/' 206 # directory names end with a '/'
201 self.__listLocalFiles(name) 207 self.__listLocalFiles(name[:-1])
202 elif Utilities.MimeTypes.isTextFile(name): 208 elif Utilities.MimeTypes.isTextFile(name):
203 e5App().getObject("ViewManager").getEditor(name) 209 e5App().getObject("ViewManager").getEditor(name)
204 210
205 @pyqtSlot() 211 @pyqtSlot()
206 def on_localFileTreeWidget_itemSelectionChanged(self): 212 def on_localFileTreeWidget_itemSelectionChanged(self):
224 self.__listLocalFiles(dirname) 230 self.__listLocalFiles(dirname)
225 231
226 @pyqtSlot(QTreeWidgetItem, int) 232 @pyqtSlot(QTreeWidgetItem, int)
227 def on_deviceFileTreeWidget_itemActivated(self, item, column): 233 def on_deviceFileTreeWidget_itemActivated(self, item, column):
228 """ 234 """
229 Slot documentation goes here. 235 Private slot to handle the activation of a device item.
230 236
231 @param item DESCRIPTION 237 If the item is a directory, the current working directory is changed
238 and the list will be re-populated for this directory.
239
240 @param item reference to the activated item
232 @type QTreeWidgetItem 241 @type QTreeWidgetItem
233 @param column DESCRIPTION 242 @param column column of the activation
234 @type int 243 @type int
235 """ 244 """
236 # TODO: not implemented yet 245 name = os.path.join(self.deviceCwd.text(), item.text(0))
237 # chdir to activated directory triggering a pwd triggering a lls 246 if name.endswith("/"):
247 # directory names end with a '/'
248 self.__fileManager.cd(name[:-1])
238 249
239 @pyqtSlot() 250 @pyqtSlot()
240 def on_deviceFileTreeWidget_itemSelectionChanged(self): 251 def on_deviceFileTreeWidget_itemSelectionChanged(self):
241 """ 252 """
242 Slot documentation goes here. 253 Private slot handling a change of selection in the local pane.
243 """ 254 """
244 # TODO: not implemented yet 255 enable = bool(len(self.deviceFileTreeWidget.selectedItems()))
256 if enable:
257 enable &= not (
258 self.deviceFileTreeWidget.selectedItems()[0].text(0)
259 .endswith("/"))
260 self.getButton.setEnabled(enable)
245 261
246 @pyqtSlot() 262 @pyqtSlot()
247 def on_deviceUpButton_clicked(self): 263 def on_deviceUpButton_clicked(self):
248 """ 264 """
249 Slot documentation goes here. 265 Private slot to go up one directory level on the device.
250 """ 266 """
251 # TODO: not implemented yet 267 cwd = self.deviceCwd.text()
252 raise NotImplementedError 268 dirname = os.path.dirname(cwd)
269 self.__fileManager.cd(dirname)
270
271 def __isFileInList(self, filename, treeWidget):
272 """
273 Private method to check, if a file name is contained in a tree widget.
274
275 @param filename name of the file to check
276 @type str
277 @param treeWidget reference to the tree widget to be checked against
278 @return flag indicating that the file name is present
279 @rtype bool
280 """
281 itemCount = treeWidget.topLevelItemCount()
282 if itemCount:
283 for row in range(itemCount):
284 if treeWidget.topLevelItem(row).text(0) == filename:
285 return True
286
287 return False
253 288
254 @pyqtSlot() 289 @pyqtSlot()
255 def on_putButton_clicked(self): 290 def on_putButton_clicked(self):
256 """ 291 """
257 Slot documentation goes here. 292 Private slot to copy the selected file to the connected device.
258 """ 293 """
259 # TODO: not implemented yet 294 selectedItems = self.localFileTreeWidget.selectedItems()
295 if selectedItems:
296 filename = selectedItems[0].text(0).strip()
297 if not filename.endswith("/"):
298 # it is really a file
299 if self.__isFileInList(filename, self.deviceFileTreeWidget):
300 # ask for overwrite permission
301 ok = E5MessageBox.yesNo(
302 self,
303 self.tr("Copy File to Device"),
304 self.tr("<p>The file <b>{0}</b> exists on the"
305 " connected device. Overwrite it?</p>")
306 .format(filename)
307 )
308 if not ok:
309 return
310 # TODO: allow to rename the new file
311
312 self.__fileManager.put(
313 os.path.join(self.localCwd.text(), filename),
314 os.path.join(self.deviceCwd.text(), filename)
315 )
260 316
261 @pyqtSlot() 317 @pyqtSlot()
262 def on_getButton_clicked(self): 318 def on_getButton_clicked(self):
263 """ 319 """
264 Slot documentation goes here. 320 Private slot to copy the selected file from the connected device.
265 """ 321 """
266 # TODO: not implemented yet 322 selectedItems = self.deviceFileTreeWidget.selectedItems()
323 if selectedItems:
324 filename = selectedItems[0].text(0).strip()
325 if not filename.endswith("/"):
326 # it is really a file
327 if self.__isFileInList(filename, self.localFileTreeWidget):
328 # ask for overwrite permission
329 ok = E5MessageBox.yesNo(
330 self,
331 self.tr("Copy File from Device"),
332 self.tr("<p>The file <b>{0}</b> exists locally."
333 " Overwrite it?</p>")
334 .format(filename)
335 )
336 if not ok:
337 return
338 # TODO: allow to rename the new file
339
340 self.__fileManager.get(
341 os.path.join(self.deviceCwd.text(), filename),
342 os.path.join(self.localCwd.text(), filename)
343 )
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
357 @pyqtSlot(str, str)
358 def __handleGetDone(self, deviceFile, localFile):
359 """
360 Private slot handling a successful copy of a file from the device.
361
362 @param deviceFile name of the file on the device
363 @type str
364 @param localFile name of the local file
365 @type str
366 """
367 self.__listLocalFiles(self.localCwd.text())
368
369 @pyqtSlot()
370 def on_syncButton_clicked(self):
371 """
372 Private slot to synchronize the local directory to the device.
373 """
374 self.__fileManager.rsync(
375 self.localCwd.text(),
376 self.deviceCwd.text(),
377 mirror=True
378 )
379
380 @pyqtSlot(str, str)
381 def __handleRsyncDone(self, localDir, deviceDir):
382 """
383 Private method to handle the completion of the rsync operation.
384
385 @param localDir name of the local directory
386 @type str
387 @param deviceDir name of the device directory
388 @type str
389 """
390 self.__listLocalFiles(self.localCwd.text())
391 self.__fileManager.lls(self.deviceCwd.text())
392
393 @pyqtSlot(list)
394 def __handleRsyncMessages(self, messages):
395 """
396 Private slot to handle messages from the rsync operation.
397
398 @param messages list of message generated by the rsync operation
399 @type list
400 """
401 E5MessageBox.information(
402 self,
403 self.tr("rsync Messages"),
404 self.tr("""<p>rsync gave the following messages</p>"""
405 """<ul><li>{0}</li></ul>""").format(
406 "</li><li>".join(messages)
407 )
408 )

eric ide

mercurial