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] |
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 ) |