src/eric7/MicroPython/MicroPythonFileManagerWidget.py

branch
eric7
changeset 10523
e4069ddd7dc7
parent 10518
1682f3203ae5
child 10690
fab36645aa7d
equal deleted inserted replaced
10522:c04e878aa308 10523:e4069ddd7dc7
5 5
6 """ 6 """
7 Module implementing a file manager for MicroPython devices. 7 Module implementing a file manager for MicroPython devices.
8 """ 8 """
9 9
10 import contextlib
10 import os 11 import os
11 import shutil 12 import shutil
12 13
13 from PyQt6.QtCore import QPoint, Qt, pyqtSlot 14 from PyQt6.QtCore import QPoint, Qt, pyqtSlot
14 from PyQt6.QtWidgets import ( 15 from PyQt6.QtWidgets import (
84 self.getButton.setEnabled(False) 85 self.getButton.setEnabled(False)
85 self.getAsButton.setEnabled(False) 86 self.getAsButton.setEnabled(False)
86 87
87 self.openButton.setEnabled(False) 88 self.openButton.setEnabled(False)
88 self.saveButton.setEnabled(False) 89 self.saveButton.setEnabled(False)
90 self.saveAsButton.setEnabled(False)
89 91
90 self.localFileTreeWidget.header().setSortIndicator( 92 self.localFileTreeWidget.header().setSortIndicator(
91 0, Qt.SortOrder.AscendingOrder 93 0, Qt.SortOrder.AscendingOrder
92 ) 94 )
93 self.deviceFileTreeWidget.header().setSortIndicator( 95 self.deviceFileTreeWidget.header().setSortIndicator(
142 self.__localMenu.addSeparator() 144 self.__localMenu.addSeparator()
143 act = self.__localMenu.addAction(self.tr("Show Hidden Files")) 145 act = self.__localMenu.addAction(self.tr("Show Hidden Files"))
144 act.setCheckable(True) 146 act.setCheckable(True)
145 act.setChecked(Preferences.getMicroPython("ShowHiddenLocal")) 147 act.setChecked(Preferences.getMicroPython("ShowHiddenLocal"))
146 act.triggered[bool].connect(self.__localHiddenChanged) 148 act.triggered[bool].connect(self.__localHiddenChanged)
149 self.__localMenu.addSeparator()
150 self.__localClearSelectionAct = self.__localMenu.addAction(
151 self.tr("Clear Selection"), self.__clearLocalSelection
152 )
147 153
148 self.__deviceMenu = QMenu(self) 154 self.__deviceMenu = QMenu(self)
149 if not isMicrobitDeviceWithMPy: 155 if not isMicrobitDeviceWithMPy:
150 self.__deviceMenu.addAction( 156 self.__deviceMenu.addAction(
151 self.tr("Change Directory"), self.__changeDeviceDirectory 157 self.tr("Change Directory"), self.__changeDeviceDirectory
175 if not isMicrobitDeviceWithMPy: 181 if not isMicrobitDeviceWithMPy:
176 self.__deviceMenu.addSeparator() 182 self.__deviceMenu.addSeparator()
177 self.__deviceMenu.addAction( 183 self.__deviceMenu.addAction(
178 self.tr("Show Filesystem Info"), self.__showFileSystemInfo 184 self.tr("Show Filesystem Info"), self.__showFileSystemInfo
179 ) 185 )
186 self.__deviceMenu.addSeparator()
187 self.__deviceClearSelectionAct = self.__deviceMenu.addAction(
188 self.tr("Clear Selection"), self.__clearDeviceSelection
189 )
180 190
181 def start(self): 191 def start(self):
182 """ 192 """
183 Public method to start the widget. 193 Public method to start the widget.
184 """ 194 """
195 self.__viewmanager = ericApp().getObject("ViewManager")
196 self.__viewmanager.editorCountChanged.connect(self.__updateSaveButtonStates)
197
185 dirname = "" 198 dirname = ""
186 vm = ericApp().getObject("ViewManager") 199 aw = self.__viewmanager.activeWindow()
187 aw = vm.activeWindow()
188 if aw and FileSystemUtilities.isPlainFileName(aw.getFileName()): 200 if aw and FileSystemUtilities.isPlainFileName(aw.getFileName()):
189 dirname = os.path.dirname(aw.getFileName()) 201 dirname = os.path.dirname(aw.getFileName())
190 if not dirname: 202 if not dirname:
191 dirname = ( 203 dirname = (
192 Preferences.getMicroPython("MpyWorkspace") 204 Preferences.getMicroPython("MpyWorkspace")
193 or Preferences.getMultiProject("Workspace") 205 or Preferences.getMultiProject("Workspace")
194 or os.path.expanduser("~") 206 or os.path.expanduser("~")
195 ) 207 )
196 self.__listLocalFiles(dirname) 208 self.__listLocalFiles(dirname=dirname)
197 209
198 if self.__repl.deviceSupportsLocalFileAccess(): 210 if self.__repl.deviceSupportsLocalFileAccess():
199 dirname = self.__repl.getDeviceWorkspace() 211 dirname = self.__repl.getDeviceWorkspace()
200 if dirname: 212 if dirname:
201 self.__listLocalFiles(dirname, True) 213 self.__listLocalFiles(dirname=dirname, localDevice=True)
202 return 214 return
203 215
204 # list files via device script 216 # list files via device script
217 self.__expandedDeviceEntries = []
205 self.__fileManager.pwd() 218 self.__fileManager.pwd()
206 219
207 def stop(self): 220 def stop(self):
208 """ 221 """
209 Public method to stop the widget. 222 Public method to stop the widget.
210 """ 223 """
211 pass 224 self.__viewmanager.editorCountChanged.disconnect(self.__updateSaveButtonStates)
225
226 @pyqtSlot()
227 def __updateSaveButtonStates(self):
228 """
229 Private slot to update the enabled state of the save buttons.
230 """
231 enable = bool(len(self.deviceFileTreeWidget.selectedItems()))
232 if enable:
233 enable &= not (
234 self.deviceFileTreeWidget.selectedItems()[0].text(0).endswith("/")
235 )
236 editorsCount = self.__viewmanager.getOpenEditorsCount()
237
238 self.saveButton.setEnabled(enable and bool(editorsCount))
239 self.saveAsButton.setEnabled(bool(editorsCount))
212 240
213 @pyqtSlot(str, str) 241 @pyqtSlot(str, str)
214 def __handleError(self, method, error): 242 def __handleError(self, method, error):
215 """ 243 """
216 Private slot to handle errors. 244 Private slot to handle errors.
238 @type str 266 @type str
239 """ 267 """
240 self.deviceCwd.setText(dirname) 268 self.deviceCwd.setText(dirname)
241 self.__newDeviceList() 269 self.__newDeviceList()
242 270
271 def __findDirectoryItem(self, dirPath, fileTreeWidget):
272 """
273 Private method to find a file tree item for the given path.
274
275 @param dirPath path to be searched for
276 @type str
277 @param fileTreeWidget reference to the file list to be searched
278 @type QTreeWidget
279 @return reference to the item for the path
280 @rtype QTreeWidgetItem
281 """
282 itm = fileTreeWidget.topLevelItem(0)
283 while itm is not None:
284 if itm.data(0, Qt.ItemDataRole.UserRole) == dirPath:
285 return itm
286 itm = fileTreeWidget.itemBelow(itm)
287
288 return None
289
243 @pyqtSlot(tuple) 290 @pyqtSlot(tuple)
244 def __handleLongListFiles(self, filesList): 291 def __handleLongListFiles(self, filesList):
245 """ 292 """
246 Private slot to receive a long directory listing. 293 Private slot to receive a long directory listing.
247 294
248 @param filesList tuple containing tuples with name, mode, size and time 295 @param filesList tuple containing tuples with name, mode, size and time
249 for each directory entry 296 for each directory entry
250 @type tuple of (str, str, str, str) 297 @type tuple of (str, str, str, str)
251 """ 298 """
252 self.deviceFileTreeWidget.clear() 299 if filesList:
253 for name, mode, size, dateTime in filesList: 300 dirPath = os.path.dirname(filesList[0][-1])
254 itm = QTreeWidgetItem( 301 dirItem = (
255 self.deviceFileTreeWidget, [name, mode, size, dateTime] 302 self.__findDirectoryItem(dirPath, self.deviceFileTreeWidget)
256 ) 303 if dirPath != self.deviceCwd.text()
257 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignHCenter) 304 else None
258 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight) 305 )
306
307 if dirItem:
308 dirItem.takeChildren()
309 else:
310 self.deviceFileTreeWidget.clear()
311
312 for name, mode, size, dateTime, filePath in filesList:
313 itm = QTreeWidgetItem(
314 self.deviceFileTreeWidget if dirItem is None else dirItem,
315 [name, mode, size, dateTime],
316 )
317 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignHCenter)
318 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight)
319 itm.setData(0, Qt.ItemDataRole.UserRole, filePath)
320 if name.endswith("/"):
321 itm.setChildIndicatorPolicy(
322 QTreeWidgetItem.ChildIndicatorPolicy.ShowIndicator
323 )
259 self.deviceFileTreeWidget.header().resizeSections( 324 self.deviceFileTreeWidget.header().resizeSections(
260 QHeaderView.ResizeMode.ResizeToContents 325 QHeaderView.ResizeMode.ResizeToContents
261 ) 326 )
262 327
263 def __listLocalFiles(self, dirname="", localDevice=False): 328 if self.__expandedDeviceEntries:
329 dirPath = self.__expandedDeviceEntries.pop(0)
330 dirItem = self.__findDirectoryItem(dirPath, self.deviceFileTreeWidget)
331 if dirItem:
332 dirItem.setExpanded(True)
333
334 def __listLocalFiles(self, dirname="", localDevice=False, parentItem=None):
264 """ 335 """
265 Private method to populate the local files list. 336 Private method to populate the local files list.
266 337
267 @param dirname name of the local directory to be listed 338 @param dirname name of the local directory to be listed (defaults to "")
268 @type str 339 @type str (optional)
269 @param localDevice flag indicating device access via local file system 340 @param localDevice flag indicating device access via local file system
270 @type bool 341 (defaults to False)
271 """ 342 @type bool (optional)
272 if not dirname: 343 @param parentItem reference to the parent item (defaults to None)
273 dirname = os.getcwd() 344 @type QTreeWidgetItem (optional)
274 if dirname != os.sep and dirname.endswith(os.sep): 345 """
275 dirname = dirname[:-1] 346 if parentItem:
276 if localDevice: 347 dirname = parentItem.data(0, Qt.ItemDataRole.UserRole)
277 self.deviceCwd.setText(dirname) 348 showHidden = (
278 showHidden = Preferences.getMicroPython("ShowHiddenDevice") 349 Preferences.getMicroPython("ShowHiddenDevice")
279 else: 350 if localDevice
280 self.localCwd.setText(dirname) 351 else Preferences.getMicroPython("ShowHiddenLocal")
281 showHidden = Preferences.getMicroPython("ShowHiddenLocal") 352 )
353 else:
354 if not dirname:
355 dirname = os.getcwd()
356 if dirname != os.sep and dirname.endswith(os.sep):
357 dirname = dirname[:-1]
358 if localDevice:
359 self.deviceCwd.setText(dirname)
360 showHidden = Preferences.getMicroPython("ShowHiddenDevice")
361 else:
362 self.localCwd.setText(dirname)
363 showHidden = Preferences.getMicroPython("ShowHiddenLocal")
282 364
283 filesStatList = listdirStat(dirname, showHidden=showHidden) 365 filesStatList = listdirStat(dirname, showHidden=showHidden)
284 filesList = [ 366 filesList = [
285 ( 367 (
286 decoratedName(f, s[0], os.path.isdir(os.path.join(dirname, f))), 368 decoratedName(f, s[0], os.path.isdir(os.path.join(dirname, f))),
287 mode2string(s[0]), 369 mode2string(s[0]),
288 str(s[6]), 370 str(s[6]),
289 mtime2string(s[8]), 371 mtime2string(s[8]),
372 os.path.join(dirname, f),
290 ) 373 )
291 for f, s in filesStatList 374 for f, s in filesStatList
292 ] 375 ]
293 fileTreeWidget = ( 376 fileTreeWidget = (
294 self.deviceFileTreeWidget if localDevice else self.localFileTreeWidget 377 self.deviceFileTreeWidget if localDevice else self.localFileTreeWidget
295 ) 378 )
296 fileTreeWidget.clear() 379 if parentItem:
380 parentItem.takeChildren()
381 else:
382 fileTreeWidget.clear()
383 parentItem = fileTreeWidget
297 for item in filesList: 384 for item in filesList:
298 itm = QTreeWidgetItem(fileTreeWidget, item) 385 itm = QTreeWidgetItem(parentItem, item[:4])
299 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignHCenter) 386 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignHCenter)
300 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight) 387 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight)
388 itm.setData(0, Qt.ItemDataRole.UserRole, item[4])
389 if os.path.isdir(item[4]):
390 itm.setChildIndicatorPolicy(
391 QTreeWidgetItem.ChildIndicatorPolicy.ShowIndicator
392 )
301 fileTreeWidget.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) 393 fileTreeWidget.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents)
394
395 def __repopulateLocalFilesList(self, dirname="", localDevice=False):
396 """
397 Private method to re-populate the local files tree.
398
399 @param dirname name of the local directory to be listed (defaults to "")
400 @type str (optional)
401 @param localDevice flag indicating device access via local file system
402 (defaults to False)
403 @type bool (optional)
404 """
405 fileTreeWidget = (
406 self.deviceFileTreeWidget if localDevice else self.localFileTreeWidget
407 )
408
409 # Step 1: record all expanded directories
410 expanded = []
411 itm = fileTreeWidget.topLevelItem(0)
412 while itm:
413 if itm.isExpanded():
414 expanded.append(itm.data(0, Qt.ItemDataRole.UserRole))
415 itm = fileTreeWidget.itemBelow(itm)
416
417 # Step 2: re-populate the top level directory
418 self.__listLocalFiles(dirname=dirname, localDevice=localDevice)
419
420 # Step 3: re-populate expanded directories
421 itm = fileTreeWidget.topLevelItem(0)
422 while itm:
423 if itm.data(0, Qt.ItemDataRole.UserRole) in expanded:
424 itm.setExpanded(True)
425 itm = fileTreeWidget.itemBelow(itm)
302 426
303 @pyqtSlot(QTreeWidgetItem, int) 427 @pyqtSlot(QTreeWidgetItem, int)
304 def on_localFileTreeWidget_itemActivated(self, item, column): 428 def on_localFileTreeWidget_itemActivated(self, item, column):
305 """ 429 """
306 Private slot to handle the activation of a local item. 430 Private slot to handle the activation of a local item.
311 @param item reference to the activated item 435 @param item reference to the activated item
312 @type QTreeWidgetItem 436 @type QTreeWidgetItem
313 @param column column of the activation 437 @param column column of the activation
314 @type int 438 @type int
315 """ 439 """
316 name = os.path.join(self.localCwd.text(), item.text(0)) 440 name = item.data(0, Qt.ItemDataRole.UserRole)
317 if name.endswith("/"): 441 if item.text(0).endswith("/"):
318 # directory names end with a '/' 442 # directory names end with a '/'
319 self.__listLocalFiles(name[:-1]) 443 self.__listLocalFiles(dirname=name)
320 elif MimeTypes.isTextFile(name): 444 elif MimeTypes.isTextFile(name):
321 ericApp().getObject("ViewManager").getEditor(name) 445 self.__viewmanager.getEditor(name)
322 446
323 @pyqtSlot() 447 @pyqtSlot()
324 def on_localFileTreeWidget_itemSelectionChanged(self): 448 def on_localFileTreeWidget_itemSelectionChanged(self):
325 """ 449 """
326 Private slot handling a change of selection in the local pane. 450 Private slot handling a change of selection in the local pane.
327 """ 451 """
328 enable = bool(len(self.localFileTreeWidget.selectedItems())) 452 enable = bool(self.localFileTreeWidget.selectedItems())
453 self.__localClearSelectionAct.setEnabled(enable)
454
329 if enable: 455 if enable:
330 enable &= not ( 456 enable &= not (
331 self.localFileTreeWidget.selectedItems()[0].text(0).endswith("/") 457 self.localFileTreeWidget.selectedItems()[0].text(0).endswith("/")
332 ) 458 )
333 self.putButton.setEnabled(enable) 459 self.putButton.setEnabled(enable)
334 self.putAsButton.setEnabled(enable) 460 self.putAsButton.setEnabled(enable)
335 461
462 @pyqtSlot(QTreeWidgetItem)
463 def on_localFileTreeWidget_itemExpanded(self, item):
464 """
465 Private slot handling the expansion of a local directory item.
466
467 @param item reference to the directory item
468 @type QTreeWidgetItem
469 """
470 if item.childCount() == 0:
471 # it was not populated yet
472 self.__listLocalFiles(parentItem=item)
473
336 @pyqtSlot(str) 474 @pyqtSlot(str)
337 def on_localCwd_textChanged(self, cwd): 475 def on_localCwd_textChanged(self, cwd):
338 """ 476 """
339 Private slot handling a change of the current local working directory. 477 Private slot handling a change of the current local working directory.
340 478
348 """ 486 """
349 Private slot to go up one directory level. 487 Private slot to go up one directory level.
350 """ 488 """
351 cwd = self.localCwd.text() 489 cwd = self.localCwd.text()
352 dirname = os.path.dirname(cwd) 490 dirname = os.path.dirname(cwd)
353 self.__listLocalFiles(dirname) 491 self.__listLocalFiles(dirname=dirname)
354 492
355 @pyqtSlot() 493 @pyqtSlot()
356 def on_localHomeButton_clicked(self): 494 def on_localHomeButton_clicked(self):
357 """ 495 """
358 Private slot to change directory to the configured workspace. 496 Private slot to change directory to the configured workspace.
360 dirname = ( 498 dirname = (
361 Preferences.getMicroPython("MpyWorkspace") 499 Preferences.getMicroPython("MpyWorkspace")
362 or Preferences.getMultiProject("Workspace") 500 or Preferences.getMultiProject("Workspace")
363 or os.path.expanduser("~") 501 or os.path.expanduser("~")
364 ) 502 )
365 self.__listLocalFiles(dirname) 503 self.__listLocalFiles(dirname=dirname)
366 504
367 @pyqtSlot() 505 @pyqtSlot()
368 def on_localReloadButton_clicked(self): 506 def on_localReloadButton_clicked(self):
369 """ 507 """
370 Private slot to reload the local list. 508 Private slot to reload the local list.
371 """ 509 """
372 dirname = self.localCwd.text() 510 dirname = self.localCwd.text()
373 self.__listLocalFiles(dirname) 511 self.__repopulateLocalFilesList(dirname=dirname)
374 512
375 @pyqtSlot(QTreeWidgetItem, int) 513 @pyqtSlot(QTreeWidgetItem, int)
376 def on_deviceFileTreeWidget_itemActivated(self, item, column): 514 def on_deviceFileTreeWidget_itemActivated(self, item, column):
377 """ 515 """
378 Private slot to handle the activation of a device item. 516 Private slot to handle the activation of a device item.
383 @param item reference to the activated item 521 @param item reference to the activated item
384 @type QTreeWidgetItem 522 @type QTreeWidgetItem
385 @param column column of the activation 523 @param column column of the activation
386 @type int 524 @type int
387 """ 525 """
526 name = item.data(0, Qt.ItemDataRole.UserRole)
388 if self.__repl.deviceSupportsLocalFileAccess(): 527 if self.__repl.deviceSupportsLocalFileAccess():
389 name = os.path.join(self.deviceCwd.text(), item.text(0)) 528 if item.text(0).endswith("/"):
390 if name.endswith("/"):
391 # directory names end with a '/' 529 # directory names end with a '/'
392 self.__listLocalFiles(name[:-1], True) 530 self.__listLocalFiles(dirname=name)
393 else: 531 else:
394 if not os.path.exists(name): 532 if not os.path.exists(name):
395 EricMessageBox.warning( 533 EricMessageBox.warning(
396 self, 534 self,
397 self.tr("Open Device File"), 535 self.tr("Open Device File"),
399 """<p>The file <b>{0}</b> does not exist.</p>""" 537 """<p>The file <b>{0}</b> does not exist.</p>"""
400 ).format(name), 538 ).format(name),
401 ) 539 )
402 return 540 return
403 if MimeTypes.isTextFile(name): 541 if MimeTypes.isTextFile(name):
404 ericApp().getObject("ViewManager").getEditor(name) 542 self.__viewmanager.getEditor(name)
405 else: 543 else:
406 cwd = self.deviceCwd.text() 544 if item.text(0).endswith("/"):
407 if cwd:
408 name = (
409 cwd + item.text(0)
410 if cwd.endswith("/")
411 else "{0}/{1}".format(cwd, item.text(0))
412 )
413 else:
414 name = item.text(0)
415 if name.endswith("/"):
416 # directory names end with a '/' 545 # directory names end with a '/'
417 self.__fileManager.cd(name[:-1]) 546 self.__fileManager.cd(name)
418 else: 547 else:
419 data = self.__fileManager.getData(name) 548 data = self.__fileManager.getData(name)
420 try: 549 try:
421 text = data.decode(encoding="utf-8") 550 text = data.decode(encoding="utf-8")
422 ericApp().getObject("ViewManager").newEditorWithText( 551 self.__viewmanager.newEditorWithText(
423 text, fileName=FileSystemUtilities.deviceFileName(name) 552 text, fileName=FileSystemUtilities.deviceFileName(name)
424 ) 553 )
425 except UnicodeDecodeError: 554 except UnicodeDecodeError:
426 EricMessageBox.warning( 555 EricMessageBox.warning(
427 self, 556 self,
435 @pyqtSlot() 564 @pyqtSlot()
436 def on_deviceFileTreeWidget_itemSelectionChanged(self): 565 def on_deviceFileTreeWidget_itemSelectionChanged(self):
437 """ 566 """
438 Private slot handling a change of selection in the local pane. 567 Private slot handling a change of selection in the local pane.
439 """ 568 """
440 enable = bool(len(self.deviceFileTreeWidget.selectedItems())) 569 enable = bool(self.deviceFileTreeWidget.selectedItems())
570 self.__deviceClearSelectionAct.setEnabled(enable)
571
441 if enable: 572 if enable:
442 enable &= not ( 573 enable &= not (
443 self.deviceFileTreeWidget.selectedItems()[0].text(0).endswith("/") 574 self.deviceFileTreeWidget.selectedItems()[0].text(0).endswith("/")
444 ) 575 )
576
445 self.getButton.setEnabled(enable) 577 self.getButton.setEnabled(enable)
446 self.getAsButton.setEnabled(enable) 578 self.getAsButton.setEnabled(enable)
447 579
448 self.openButton.setEnabled(enable) 580 self.openButton.setEnabled(enable)
449 self.saveButton.setEnabled(enable) 581
582 self.__updateSaveButtonStates()
583
584 @pyqtSlot(QTreeWidgetItem)
585 def on_deviceFileTreeWidget_itemExpanded(self, item):
586 """
587 Private slot handling the expansion of a local directory item.
588
589 @param item reference to the directory item
590 @type QTreeWidgetItem
591 """
592 if item.childCount() == 0:
593 # it was not populated yet
594 if self.__repl.deviceSupportsLocalFileAccess():
595 self.__listLocalFiles(localDevice=True, parentItem=item)
596 else:
597 self.__fileManager.lls(
598 item.data(0, Qt.ItemDataRole.UserRole),
599 showHidden=Preferences.getMicroPython("ShowHiddenDevice"),
600 )
450 601
451 @pyqtSlot(str) 602 @pyqtSlot(str)
452 def on_deviceCwd_textChanged(self, cwd): 603 def on_deviceCwd_textChanged(self, cwd):
453 """ 604 """
454 Private slot handling a change of the current device working directory. 605 Private slot handling a change of the current device working directory.
464 Private slot to go up one directory level on the device. 615 Private slot to go up one directory level on the device.
465 """ 616 """
466 cwd = self.deviceCwd.text() 617 cwd = self.deviceCwd.text()
467 dirname = os.path.dirname(cwd) 618 dirname = os.path.dirname(cwd)
468 if self.__repl.deviceSupportsLocalFileAccess(): 619 if self.__repl.deviceSupportsLocalFileAccess():
469 self.__listLocalFiles(dirname, True) 620 self.__listLocalFiles(dirname=dirname, localDevice=True)
470 else: 621 else:
471 self.__fileManager.cd(dirname) 622 self.__fileManager.cd(dirname)
472 623
473 @pyqtSlot() 624 @pyqtSlot()
474 def on_deviceHomeButton_clicked(self): 625 def on_deviceHomeButton_clicked(self):
476 Private slot to move to the device home directory. 627 Private slot to move to the device home directory.
477 """ 628 """
478 if self.__repl.deviceSupportsLocalFileAccess(): 629 if self.__repl.deviceSupportsLocalFileAccess():
479 dirname = self.__repl.getDeviceWorkspace() 630 dirname = self.__repl.getDeviceWorkspace()
480 if dirname: 631 if dirname:
481 self.__listLocalFiles(dirname, True) 632 self.__listLocalFiles(dirname=dirname, localDevice=True)
482 return 633 return
483 634
484 # list files via device script 635 # list files via device script
485 self.__fileManager.cd("/") 636 self.__fileManager.cd("/")
486 637
489 """ 640 """
490 Private slot to reload the device list. 641 Private slot to reload the device list.
491 """ 642 """
492 dirname = self.deviceCwd.text() 643 dirname = self.deviceCwd.text()
493 if self.__repl.deviceSupportsLocalFileAccess(): 644 if self.__repl.deviceSupportsLocalFileAccess():
494 self.__listLocalFiles(dirname, True) 645 self.__repopulateLocalFilesList(dirname=dirname, localDevice=True)
495 else: 646 else:
496 if dirname: 647 if dirname:
497 self.__newDeviceList() 648 self.__newDeviceList()
498 else: 649 else:
499 self.__fileManager.pwd() 650 self.__fileManager.pwd()
500 651
501 def __isFileInList(self, filename, treeWidget): 652 def __isFileInList(self, filename, parent):
502 """ 653 """
503 Private method to check, if a file name is contained in a tree widget. 654 Private method to check, if a file name is contained in a tree widget.
504 655
505 @param filename name of the file to check 656 @param filename name of the file to check
506 @type str 657 @type str
507 @param treeWidget reference to the tree widget to be checked against 658 @param parent reference to the parent to be checked against
508 @type QTreeWidget 659 @type QTreeWidget or QTreeWidgetItem
509 @return flag indicating that the file name is present 660 @return flag indicating that the file name is present
510 @rtype bool 661 @rtype bool
511 """ 662 """
512 itemCount = treeWidget.topLevelItemCount() 663 if isinstance(parent, QTreeWidgetItem):
513 return itemCount > 0 and any( 664 itemCount = parent.childCount()
514 treeWidget.topLevelItem(row).text(0) == filename for row in range(itemCount) 665 return itemCount > 0 and any(
515 ) 666 parent.child(row).text(0) == filename for row in range(itemCount)
667 )
668 else:
669 itemCount = parent.topLevelItemCount()
670 return itemCount > 0 and any(
671 parent.topLevelItem(row).text(0) == filename for row in range(itemCount)
672 )
516 673
517 @pyqtSlot() 674 @pyqtSlot()
518 def on_putButton_clicked(self, putAs=False): 675 def on_putButton_clicked(self, putAs=False):
519 """ 676 """
520 Private slot to copy the selected file to the connected device. 677 Private slot to copy the selected file to the connected device.
522 @param putAs flag indicating to give it a new name 679 @param putAs flag indicating to give it a new name
523 @type bool 680 @type bool
524 """ 681 """
525 selectedItems = self.localFileTreeWidget.selectedItems() 682 selectedItems = self.localFileTreeWidget.selectedItems()
526 if selectedItems: 683 if selectedItems:
527 filename = selectedItems[0].text(0).strip() 684 filepath = selectedItems[0].data(0, Qt.ItemDataRole.UserRole)
528 if not filename.endswith("/"): 685 filename = os.path.basename(filepath)
686 if not selectedItems[0].text(0).endswith("/"):
529 # it is really a file 687 # it is really a file
530 if putAs: 688 if putAs:
531 deviceFilename, ok = QInputDialog.getText( 689 deviceFilename, ok = QInputDialog.getText(
532 self, 690 self,
533 self.tr("Put File As"), 691 self.tr("Put File As"),
534 self.tr("Enter a new name for the file"), 692 self.tr("Enter a new name for the file"),
535 QLineEdit.EchoMode.Normal, 693 QLineEdit.EchoMode.Normal,
536 filename, 694 filename,
537 ) 695 )
538 if not ok or not filename: 696 if not ok or not deviceFilename:
539 return 697 return
540 else: 698 else:
541 deviceFilename = filename 699 deviceFilename = filename
542 700
543 if self.__isFileInList(deviceFilename, self.deviceFileTreeWidget): 701 selectedDeviceItems = self.deviceFileTreeWidget.selectedItems()
702 if selectedDeviceItems:
703 item = selectedDeviceItems[0]
704 if not item.text(0).endswith("/"):
705 # it is no directory, take its parent
706 item = item.parent()
707 devicePath = (
708 self.deviceCwd.text()
709 if item is None
710 else item.data(0, Qt.ItemDataRole.UserRole)
711 )
712 deviceParent = item
713 else:
714 devicePath = self.deviceCwd.text()
715 deviceParent = self.deviceFileTreeWidget
716
717 if self.__isFileInList(deviceFilename, deviceParent):
544 # ask for overwrite permission 718 # ask for overwrite permission
545 action, resultFilename = confirmOverwrite( 719 action, resultFilename = confirmOverwrite(
546 deviceFilename, 720 deviceFilename,
547 self.tr("Copy File to Device"), 721 self.tr("Copy File to Device"),
548 self.tr( 722 self.tr(
555 return 729 return
556 elif action == "rename": 730 elif action == "rename":
557 deviceFilename = os.path.basename(resultFilename) 731 deviceFilename = os.path.basename(resultFilename)
558 732
559 if self.__repl.deviceSupportsLocalFileAccess(): 733 if self.__repl.deviceSupportsLocalFileAccess():
560 shutil.copy2( 734 shutil.copy2(filepath, os.path.join(devicePath, deviceFilename))
561 os.path.join(self.localCwd.text(), filename), 735 self.__listLocalFiles(dirname=devicePath, localDevice=True)
562 os.path.join(self.deviceCwd.text(), deviceFilename),
563 )
564 self.__listLocalFiles(self.deviceCwd.text(), localDevice=True)
565 else: 736 else:
566 deviceCwd = self.deviceCwd.text() 737 if devicePath:
567 if deviceCwd: 738 deviceFilename = (
568 if deviceCwd != "/": 739 f"{devicePath}/{deviceFilename}"
569 deviceFilename = deviceCwd + "/" + deviceFilename 740 if devicePath != "/"
570 else: 741 else f"/{devicePath}"
571 deviceFilename = "/" + deviceFilename 742 )
572 self.__fileManager.put( 743 self.__fileManager.put(filepath, deviceFilename)
573 os.path.join(self.localCwd.text(), filename), deviceFilename
574 )
575 744
576 @pyqtSlot() 745 @pyqtSlot()
577 def on_putAsButton_clicked(self): 746 def on_putAsButton_clicked(self):
578 """ 747 """
579 Private slot to copy the selected file to the connected device 748 Private slot to copy the selected file to the connected device
590 @type bool 759 @type bool
591 """ 760 """
592 selectedItems = self.deviceFileTreeWidget.selectedItems() 761 selectedItems = self.deviceFileTreeWidget.selectedItems()
593 if selectedItems: 762 if selectedItems:
594 filename = selectedItems[0].text(0).strip() 763 filename = selectedItems[0].text(0).strip()
764 deviceFilename = selectedItems[0].data(0, Qt.ItemDataRole.UserRole)
595 if not filename.endswith("/"): 765 if not filename.endswith("/"):
596 # it is really a file 766 # it is really a file
597 if getAs: 767 if getAs:
598 localFilename, ok = QInputDialog.getText( 768 localFilename, ok = QInputDialog.getText(
599 self, 769 self,
605 if not ok or not filename: 775 if not ok or not filename:
606 return 776 return
607 else: 777 else:
608 localFilename = filename 778 localFilename = filename
609 779
610 if self.__isFileInList(localFilename, self.localFileTreeWidget): 780 selectedLocalItems = self.localFileTreeWidget.selectedItems()
781 if selectedLocalItems:
782 item = selectedLocalItems[0]
783 if not item.text(0).endswith("/"):
784 # it is no directory, take its parent
785 item = item.parent()
786 localPath = (
787 self.localCwd.text()
788 if item is None
789 else item.data(0, Qt.ItemDataRole.UserRole)
790 )
791 localParent = item
792 else:
793 localPath = self.localCwd.text()
794 localParent = self.localFileTreeWidget
795
796 if self.__isFileInList(localFilename, localParent):
611 # ask for overwrite permission 797 # ask for overwrite permission
612 action, resultFilename = confirmOverwrite( 798 action, resultFilename = confirmOverwrite(
613 localFilename, 799 localFilename,
614 self.tr("Copy File from Device"), 800 self.tr("Copy File from Device"),
615 self.tr("The given file exists already."), 801 self.tr("The given file exists already."),
621 elif action == "rename": 807 elif action == "rename":
622 localFilename = resultFilename 808 localFilename = resultFilename
623 809
624 if self.__repl.deviceSupportsLocalFileAccess(): 810 if self.__repl.deviceSupportsLocalFileAccess():
625 shutil.copy2( 811 shutil.copy2(
626 os.path.join(self.deviceCwd.text(), filename), 812 deviceFilename,
627 os.path.join(self.localCwd.text(), localFilename), 813 os.path.join(localPath, localFilename),
628 ) 814 )
629 self.__listLocalFiles(self.localCwd.text()) 815 if isinstance(localParent, QTreeWidgetItem):
816 self.__listLocalFiles(parentItem=localParent)
817 else:
818 self.__listLocalFiles(dirname=localPath)
630 else: 819 else:
631 deviceCwd = self.deviceCwd.text()
632 if deviceCwd:
633 filename = deviceCwd + "/" + filename
634 self.__fileManager.get( 820 self.__fileManager.get(
635 filename, os.path.join(self.localCwd.text(), localFilename) 821 deviceFilename, os.path.join(localPath, localFilename)
636 ) 822 )
637 823
638 @pyqtSlot() 824 @pyqtSlot()
639 def on_getAsButton_clicked(self): 825 def on_getAsButton_clicked(self):
640 """ 826 """
651 @param deviceFile name of the file on the device 837 @param deviceFile name of the file on the device
652 @type str 838 @type str
653 @param localFile name of the local file 839 @param localFile name of the local file
654 @type str 840 @type str
655 """ 841 """
656 self.__listLocalFiles(self.localCwd.text()) 842 localPath = os.path.dirname(localFile)
843
844 # find the directory entry associated with the new file
845 localParent = self.__findDirectoryItem(localPath, self.localFileTreeWidget)
846
847 if localParent:
848 self.__listLocalFiles(parentItem=localParent)
849 else:
850 self.__listLocalFiles(dirname=self.localCwd.text())
657 851
658 @pyqtSlot() 852 @pyqtSlot()
659 def on_syncButton_clicked(self): 853 def on_syncButton_clicked(self):
660 """ 854 """
661 Private slot to synchronize the local directory to the device. 855 Private slot to synchronize the local directory to the device.
662 """ 856 """
857 # 1. local directory
858 selectedItems = self.localFileTreeWidget.selectedItems()
859 if selectedItems:
860 localName = selectedItems[0].text(0)
861 if localName.endswith("/"):
862 localDirPath = selectedItems[0].data(0, Qt.ItemDataRole.UserRole)
863 else:
864 # it is not a directory
865 localDirPath = os.path.dirname(
866 selectedItems[0].data(0, Qt.ItemDataRole.UserRole)
867 )
868 else:
869 localName = ""
870 localDirPath = self.localCwd.text()
871
872 # 2. device directory
873 selectedItems = self.deviceFileTreeWidget.selectedItems()
874 if selectedItems:
875 if not selectedItems[0].text(0).endswith("/"):
876 # it is not a directory
877 deviceDirPath = os.path.dirname(
878 selectedItems[0].data(0, Qt.ItemDataRole.UserRole)
879 )
880 else:
881 deviceDirPath = selectedItems[0].data(0, Qt.ItemDataRole.UserRole)
882 else:
883 if localDirPath == self.localCwd.text():
884 # syncronize complete local directory
885 deviceDirPath = self.deviceCwd.text()
886 else:
887 deviceCwd = self.deviceCwd.text()
888 deviceDirPath = (
889 f"{deviceCwd}{localName[:-1]}"
890 if deviceCwd.endswith("/")
891 else f"{deviceCwd}/{localName[:-1]}"
892 )
893
663 self.__fileManager.rsync( 894 self.__fileManager.rsync(
664 self.localCwd.text(), 895 localDirPath,
665 self.deviceCwd.text(), 896 deviceDirPath,
666 mirror=True, 897 mirror=True,
667 localDevice=self.__repl.deviceSupportsLocalFileAccess(), 898 localDevice=self.__repl.deviceSupportsLocalFileAccess(),
668 ) 899 )
669 900
670 @pyqtSlot(str, str) 901 @pyqtSlot(str, str)
710 @pyqtSlot() 941 @pyqtSlot()
711 def __newDeviceList(self): 942 def __newDeviceList(self):
712 """ 943 """
713 Private slot to initiate a new long list of the device directory. 944 Private slot to initiate a new long list of the device directory.
714 """ 945 """
946 self.__expandedDeviceEntries.clear()
947 itm = self.deviceFileTreeWidget.topLevelItem(0)
948 while itm:
949 if itm.isExpanded():
950 self.__expandedDeviceEntries.append(
951 itm.data(0, Qt.ItemDataRole.UserRole)
952 )
953 itm = self.deviceFileTreeWidget.itemBelow(itm)
954
715 self.__fileManager.lls( 955 self.__fileManager.lls(
716 self.deviceCwd.text(), 956 self.deviceCwd.text(),
717 showHidden=Preferences.getMicroPython("ShowHiddenDevice"), 957 showHidden=Preferences.getMicroPython("ShowHiddenDevice"),
718 ) 958 )
719 959
722 """ 962 """
723 Private slot to open the selected file in a new editor. 963 Private slot to open the selected file in a new editor.
724 """ 964 """
725 selectedItems = self.deviceFileTreeWidget.selectedItems() 965 selectedItems = self.deviceFileTreeWidget.selectedItems()
726 if selectedItems: 966 if selectedItems:
727 filename = selectedItems[0].text(0).strip() 967 name = selectedItems[0].data(0, Qt.ItemDataRole.UserRole)
728 if self.__repl.deviceSupportsLocalFileAccess(): 968 if self.__repl.deviceSupportsLocalFileAccess():
729 name = os.path.join(self.deviceCwd.text(), filename) 969 if not selectedItems[0].text(0).endswith("/") and MimeTypes.isTextFile(
730 if not name.endswith("/") and MimeTypes.isTextFile(name): 970 name
731 ericApp().getObject("ViewManager").getEditor(name) 971 ):
972 self.__viewmanager.getEditor(name)
732 else: 973 else:
733 cwd = self.deviceCwd.text() 974 if not selectedItems[0].text(0).endswith("/"):
734 if cwd:
735 name = (
736 cwd + filename
737 if cwd.endswith("/")
738 else "{0}/{1}".format(cwd, filename)
739 )
740 else:
741 name = filename
742 if not name.endswith("/"):
743 data = self.__fileManager.getData(name) 975 data = self.__fileManager.getData(name)
744 text = data.decode(encoding="utf-8") 976 text = data.decode(encoding="utf-8")
745 ericApp().getObject("ViewManager").newEditorWithText( 977 self.__viewmanager.newEditorWithText(
746 text, "Python3", FileSystemUtilities.deviceFileName(name) 978 text, "Python3", FileSystemUtilities.deviceFileName(name)
747 ) 979 )
748 980
749 @pyqtSlot() 981 @pyqtSlot()
750 def on_saveButton_clicked(self, saveAs=False): 982 def on_saveButton_clicked(self, saveAs=False):
752 Private slot to save the text of the current editor to a file on the device. 984 Private slot to save the text of the current editor to a file on the device.
753 985
754 @param saveAs flag indicating to save the file with a new name 986 @param saveAs flag indicating to save the file with a new name
755 @type bool 987 @type bool
756 """ 988 """
757 aw = ericApp().getObject("ViewManager").activeWindow() 989 aw = self.__viewmanager.activeWindow()
758 if aw: 990 if aw:
759 selectedItems = self.deviceFileTreeWidget.selectedItems() 991 selectedItems = self.deviceFileTreeWidget.selectedItems()
992
760 if selectedItems: 993 if selectedItems:
761 filename = selectedItems[0].text(0).strip() 994 filepath = selectedItems[0].data(0, Qt.ItemDataRole.UserRole)
762 if filename.endswith("/"): 995 filename = os.path.basename(filepath)
996 if selectedItems[0].text(0).endswith("/"):
763 saveAs = True 997 saveAs = True
764 else: 998 else:
765 saveAs = True 999 saveAs = True
766 filename = "" 1000 filename = ""
767 1001
782 FileSystemUtilities.plainFileName(aw.getFileName()) 1016 FileSystemUtilities.plainFileName(aw.getFileName())
783 ) 1017 )
784 if editorFileName != filename: 1018 if editorFileName != filename:
785 saveAs = True 1019 saveAs = True
786 1020
787 if saveAs and self.__isFileInList(filename, self.deviceFileTreeWidget): 1021 if selectedItems:
1022 item = selectedItems[0]
1023 if not item.text(0).endswith("/"):
1024 # it is no directory, take its parent
1025 item = item.parent()
1026 devicePath = (
1027 self.deviceCwd.text()
1028 if item is None
1029 else item.data(0, Qt.ItemDataRole.UserRole)
1030 )
1031 deviceParent = item
1032 else:
1033 devicePath = self.deviceCwd.text()
1034 deviceParent = self.deviceFileTreeWidget
1035
1036 if saveAs and self.__isFileInList(filename, deviceParent):
788 # ask for overwrite permission 1037 # ask for overwrite permission
789 action, resultFilename = confirmOverwrite( 1038 action, resultFilename = confirmOverwrite(
790 filename, 1039 filename,
791 self.tr("Save File As"), 1040 self.tr("Save File As"),
792 self.tr("The given file exists already (Enter file name only)."), 1041 self.tr("The given file exists already (Enter file name only)."),
798 elif action == "rename": 1047 elif action == "rename":
799 filename = os.path.basename(resultFilename) 1048 filename = os.path.basename(resultFilename)
800 1049
801 text = aw.text() 1050 text = aw.text()
802 if self.__repl.deviceSupportsLocalFileAccess(): 1051 if self.__repl.deviceSupportsLocalFileAccess():
803 filename = os.path.join(self.deviceCwd.text(), filename) 1052 filename = os.path.join(devicePath, filename)
804 os.makedirs(os.path.dirname(filename), exist_ok=True) 1053 os.makedirs(os.path.dirname(filename), exist_ok=True)
805 with open(filename, "w") as f: 1054 with open(filename, "w") as f:
806 f.write(text) 1055 f.write(text)
807 self.__newDeviceList() 1056 self.__newDeviceList()
808 aw.setFileName(filename) 1057 aw.setFileName(filename)
809 else: 1058 else:
810 if not filename.startswith("/"): 1059 filename = (
811 deviceCwd = self.deviceCwd.text() 1060 f"{devicePath}/{filename}"
812 if deviceCwd: 1061 if devicePath != "/"
813 filename = ( 1062 else f"/{devicePath}"
814 deviceCwd + "/" + filename 1063 )
815 if deviceCwd != "/"
816 else "/" + filename
817 )
818 dirname = filename.rsplit("/", 1)[0] 1064 dirname = filename.rsplit("/", 1)[0]
819 self.__fileManager.makedirs(dirname) 1065 self.__fileManager.makedirs(dirname)
820 self.__fileManager.putData(filename, text.encode("utf-8")) 1066 self.__fileManager.putData(filename, text.encode("utf-8"))
821 aw.setFileName(FileSystemUtilities.deviceFileName(filename)) 1067 aw.setFileName(FileSystemUtilities.deviceFileName(filename))
822 1068
823 aw.setModified(False) 1069 aw.setModified(False)
824 aw.resetOnlineChangeTraceInfo() 1070 with contextlib.suppress(AttributeError):
1071 aw.resetOnlineChangeTraceInfo()
825 1072
826 @pyqtSlot() 1073 @pyqtSlot()
827 def on_saveAsButton_clicked(self): 1074 def on_saveAsButton_clicked(self):
828 """ 1075 """
829 Private slot to save the current editor in a new file on the connected device. 1076 Private slot to save the current editor in a new file on the connected device.
844 """ 1091 """
845 hasSelection = bool(len(self.localFileTreeWidget.selectedItems())) 1092 hasSelection = bool(len(self.localFileTreeWidget.selectedItems()))
846 if hasSelection: 1093 if hasSelection:
847 name = self.localFileTreeWidget.selectedItems()[0].text(0) 1094 name = self.localFileTreeWidget.selectedItems()[0].text(0)
848 isDir = name.endswith("/") 1095 isDir = name.endswith("/")
849 isFile = not isDir 1096 isLink = name.endswith("@")
1097 isFile = not (isDir or isLink)
850 else: 1098 else:
851 isDir = False 1099 isDir = False
852 isFile = False 1100 isFile = False
853 self.__localDelDirTreeAct.setEnabled(isDir) 1101 self.__localDelDirTreeAct.setEnabled(isDir)
854 self.__localRenameFileAct.setEnabled(isFile) 1102 self.__localRenameFileAct.setEnabled(isFile)
863 1111
864 @param localDevice flag indicating device access via local file system 1112 @param localDevice flag indicating device access via local file system
865 @type bool 1113 @type bool
866 """ 1114 """
867 cwdWidget = self.deviceCwd if localDevice else self.localCwd 1115 cwdWidget = self.deviceCwd if localDevice else self.localCwd
868 1116 fileTreeWidget = (
1117 self.deviceFileTreeWidget if localDevice else self.localFileTreeWidget
1118 )
1119
1120 if fileTreeWidget.selectedItems():
1121 defaultPath = fileTreeWidget.selectedItems()[0].data(
1122 0, Qt.ItemDataRole.UserRole
1123 )
1124 if not os.path.isdir(defaultPath):
1125 defaultPath = os.path.dirname(defaultPath)
1126 else:
1127 defaultPath = cwdWidget.text()
869 dirPath, ok = EricPathPickerDialog.getStrPath( 1128 dirPath, ok = EricPathPickerDialog.getStrPath(
870 self, 1129 self,
871 self.tr("Change Directory"), 1130 self.tr("Change Directory"),
872 self.tr("Select Directory"), 1131 self.tr("Select Directory"),
873 EricPathPickerModes.DIRECTORY_SHOW_FILES_MODE, 1132 EricPathPickerModes.DIRECTORY_SHOW_FILES_MODE,
874 strPath=cwdWidget.text(), 1133 strPath=defaultPath,
875 defaultDirectory=cwdWidget.text(), 1134 defaultDirectory=defaultPath,
876 ) 1135 )
877 if ok and dirPath: 1136 if ok and dirPath:
878 if not os.path.isabs(dirPath): 1137 if not os.path.isabs(dirPath):
879 dirPath = os.path.join(cwdWidget.text(), dirPath) 1138 dirPath = os.path.join(cwdWidget.text(), dirPath)
880 cwdWidget.setText(dirPath) 1139 cwdWidget.setText(dirPath)
881 self.__listLocalFiles(dirPath, localDevice=localDevice) 1140 self.__listLocalFiles(dirname=dirPath, localDevice=localDevice)
882 1141
883 @pyqtSlot() 1142 @pyqtSlot()
884 def __createLocalDirectory(self, localDevice=False): 1143 def __createLocalDirectory(self, localDevice=False):
885 """ 1144 """
886 Private slot to create a local directory. 1145 Private slot to create a local directory.
887 1146
888 @param localDevice flag indicating device access via local file system 1147 @param localDevice flag indicating device access via local file system
889 @type bool 1148 @type bool
890 """ 1149 """
891 cwdWidget = self.deviceCwd if localDevice else self.localCwd 1150 cwdWidget = self.deviceCwd if localDevice else self.localCwd
1151 fileTreeWidget = (
1152 self.deviceFileTreeWidget if localDevice else self.localFileTreeWidget
1153 )
1154
1155 if fileTreeWidget.selectedItems():
1156 localItem = fileTreeWidget.selectedItems()[0]
1157 defaultPath = localItem.data(0, Qt.ItemDataRole.UserRole)
1158 if not os.path.isdir(defaultPath):
1159 defaultPath = os.path.dirname(defaultPath)
1160 localItem = localItem.parent()
1161 else:
1162 defaultPath = cwdWidget.text()
1163 localItem = None
892 1164
893 dirPath, ok = QInputDialog.getText( 1165 dirPath, ok = QInputDialog.getText(
894 self, 1166 self,
895 self.tr("Create Directory"), 1167 self.tr("Create Directory"),
896 self.tr("Enter directory name:"), 1168 self.tr("Enter directory name:"),
897 QLineEdit.EchoMode.Normal, 1169 QLineEdit.EchoMode.Normal,
898 ) 1170 )
899 if ok and dirPath: 1171 if ok and dirPath:
900 dirPath = os.path.join(cwdWidget.text(), dirPath) 1172 dirPath = os.path.join(defaultPath, dirPath)
901 try: 1173 try:
902 os.mkdir(dirPath) 1174 os.mkdir(dirPath)
903 self.__listLocalFiles(cwdWidget.text(), localDevice=localDevice) 1175 if localItem:
1176 self.__listLocalFiles(localDevice=localDevice, parentItem=localItem)
1177 else:
1178 self.__listLocalFiles(
1179 dirname=cwdWidget.text(), localDevice=localDevice
1180 )
904 except OSError as exc: 1181 except OSError as exc:
905 EricMessageBox.critical( 1182 EricMessageBox.critical(
906 self, 1183 self,
907 self.tr("Create Directory"), 1184 self.tr("Create Directory"),
908 self.tr( 1185 self.tr(
924 fileTreeWidget = self.deviceFileTreeWidget 1201 fileTreeWidget = self.deviceFileTreeWidget
925 else: 1202 else:
926 cwdWidget = self.localCwd 1203 cwdWidget = self.localCwd
927 fileTreeWidget = self.localFileTreeWidget 1204 fileTreeWidget = self.localFileTreeWidget
928 1205
929 if bool(len(fileTreeWidget.selectedItems())): 1206 if bool(fileTreeWidget.selectedItems()):
930 name = fileTreeWidget.selectedItems()[0].text(0) 1207 localItem = fileTreeWidget.selectedItems()[0]
931 dirname = os.path.join(cwdWidget.text(), name[:-1]) 1208 parentItem = localItem.parent()
1209 dirname = localItem.data(0, Qt.ItemDataRole.UserRole)
932 dlg = DeleteFilesConfirmationDialog( 1210 dlg = DeleteFilesConfirmationDialog(
933 self, 1211 self,
934 self.tr("Delete Directory Tree"), 1212 self.tr("Delete Directory Tree"),
935 self.tr("Do you really want to delete this directory tree?"), 1213 self.tr("Do you really want to delete this directory tree?"),
936 [dirname], 1214 [dirname],
937 ) 1215 )
938 if dlg.exec() == QDialog.DialogCode.Accepted: 1216 if dlg.exec() == QDialog.DialogCode.Accepted:
939 try: 1217 try:
940 shutil.rmtree(dirname) 1218 shutil.rmtree(dirname)
941 self.__listLocalFiles(cwdWidget.text(), localDevice=localDevice) 1219 if parentItem:
1220 self.__listLocalFiles(
1221 localDevice=localDevice, parentItem=parentItem
1222 )
1223 else:
1224 self.__listLocalFiles(
1225 dirname=cwdWidget.text(), localDevice=localDevice
1226 )
942 except Exception as exc: 1227 except Exception as exc:
943 EricMessageBox.critical( 1228 EricMessageBox.critical(
944 self, 1229 self,
945 self.tr("Delete Directory Tree"), 1230 self.tr("Delete Directory Tree"),
946 self.tr( 1231 self.tr(
963 else: 1248 else:
964 cwdWidget = self.localCwd 1249 cwdWidget = self.localCwd
965 fileTreeWidget = self.localFileTreeWidget 1250 fileTreeWidget = self.localFileTreeWidget
966 1251
967 if bool(len(fileTreeWidget.selectedItems())): 1252 if bool(len(fileTreeWidget.selectedItems())):
968 name = fileTreeWidget.selectedItems()[0].text(0) 1253 localItem = fileTreeWidget.selectedItems()[0]
969 filename = os.path.join(cwdWidget.text(), name) 1254 parentItem = localItem.parent()
1255 filename = localItem.data(0, Qt.ItemDataRole.UserRole)
970 dlg = DeleteFilesConfirmationDialog( 1256 dlg = DeleteFilesConfirmationDialog(
971 self, 1257 self,
972 self.tr("Delete File"), 1258 self.tr("Delete File"),
973 self.tr("Do you really want to delete this file?"), 1259 self.tr("Do you really want to delete this file?"),
974 [filename], 1260 [filename],
975 ) 1261 )
976 if dlg.exec() == QDialog.DialogCode.Accepted: 1262 if dlg.exec() == QDialog.DialogCode.Accepted:
977 try: 1263 try:
978 os.remove(filename) 1264 os.remove(filename)
979 self.__listLocalFiles(cwdWidget.text(), localDevice=localDevice) 1265 if parentItem:
1266 self.__listLocalFiles(
1267 localDevice=localDevice, parentItem=parentItem
1268 )
1269 else:
1270 self.__listLocalFiles(
1271 dirname=cwdWidget.text(), localDevice=localDevice
1272 )
980 except OSError as exc: 1273 except OSError as exc:
981 EricMessageBox.critical( 1274 EricMessageBox.critical(
982 self, 1275 self,
983 self.tr("Delete File"), 1276 self.tr("Delete File"),
984 self.tr( 1277 self.tr(
994 1287
995 @param localDevice flag indicating device access via local file system 1288 @param localDevice flag indicating device access via local file system
996 (defaults to False) 1289 (defaults to False)
997 @type bool (optional) 1290 @type bool (optional)
998 """ 1291 """
999 if localDevice: 1292 fileTreeWidget = (
1000 cwdWidget = self.deviceCwd 1293 self.deviceFileTreeWidget if localDevice else self.localFileTreeWidget
1001 fileTreeWidget = self.deviceFileTreeWidget 1294 )
1002 else:
1003 cwdWidget = self.localCwd
1004 fileTreeWidget = self.localFileTreeWidget
1005 1295
1006 if bool(len(fileTreeWidget.selectedItems())): 1296 if bool(len(fileTreeWidget.selectedItems())):
1007 name = fileTreeWidget.selectedItems()[0].text(0) 1297 localItem = fileTreeWidget.selectedItems()[0]
1008 filename = os.path.join(cwdWidget.text(), name) 1298 filename = localItem.data(0, Qt.ItemDataRole.UserRole)
1009 newname, ok = QInputDialog.getText( 1299 newname, ok = QInputDialog.getText(
1010 self, 1300 self,
1011 self.tr("Rename File"), 1301 self.tr("Rename File"),
1012 self.tr("Enter the new path for the file"), 1302 self.tr("Enter the new path for the file"),
1013 QLineEdit.EchoMode.Normal, 1303 QLineEdit.EchoMode.Normal,
1041 @type bool 1331 @type bool
1042 """ 1332 """
1043 Preferences.setMicroPython("ShowHiddenLocal", checked) 1333 Preferences.setMicroPython("ShowHiddenLocal", checked)
1044 self.on_localReloadButton_clicked() 1334 self.on_localReloadButton_clicked()
1045 1335
1336 @pyqtSlot()
1337 def __clearLocalSelection(self):
1338 """
1339 Private slot to clear the local selection.
1340 """
1341 for item in self.localFileTreeWidget.selectedItems()[:]:
1342 item.setSelected(False)
1343
1046 ################################################################## 1344 ##################################################################
1047 ## Context menu methods for the device files below 1345 ## Context menu methods for the device files below
1048 ################################################################## 1346 ##################################################################
1049 1347
1050 @pyqtSlot(QPoint) 1348 @pyqtSlot(QPoint)
1080 current directory. 1378 current directory.
1081 """ 1379 """
1082 if self.__repl.deviceSupportsLocalFileAccess(): 1380 if self.__repl.deviceSupportsLocalFileAccess():
1083 self.__changeLocalDirectory(True) 1381 self.__changeLocalDirectory(True)
1084 else: 1382 else:
1383 selectedItems = self.deviceFileTreeWidget.selectedItems()
1384 if selectedItems:
1385 item = selectedItems[0]
1386 dirName = (
1387 item.data(0, Qt.ItemDataRole.UserRole)
1388 if item.text(0).endswith("/")
1389 else os.path.dirname(item.data(0, Qt.ItemDataRole.UserRole))
1390 )
1391 else:
1392 dirName = self.deviceCwd.text()
1085 dirPath, ok = QInputDialog.getText( 1393 dirPath, ok = QInputDialog.getText(
1086 self, 1394 self,
1087 self.tr("Change Directory"), 1395 self.tr("Change Directory"),
1088 self.tr("Enter the directory path on the device:"), 1396 self.tr("Enter the directory path on the device:"),
1089 QLineEdit.EchoMode.Normal, 1397 QLineEdit.EchoMode.Normal,
1090 self.deviceCwd.text(), 1398 dirName,
1091 ) 1399 )
1092 if ok and dirPath: 1400 if ok and dirPath:
1093 if not dirPath.startswith("/"): 1401 if not dirPath.startswith("/"):
1094 dirPath = self.deviceCwd.text() + "/" + dirPath 1402 dirPath = self.deviceCwd.text() + "/" + dirPath
1095 self.__fileManager.cd(dirPath) 1403 self.__fileManager.cd(dirPath)
1100 Private slot to create a directory on the device. 1408 Private slot to create a directory on the device.
1101 """ 1409 """
1102 if self.__repl.deviceSupportsLocalFileAccess(): 1410 if self.__repl.deviceSupportsLocalFileAccess():
1103 self.__createLocalDirectory(True) 1411 self.__createLocalDirectory(True)
1104 else: 1412 else:
1413 selectedItems = self.deviceFileTreeWidget.selectedItems()
1414 if selectedItems:
1415 item = selectedItems[0]
1416 defaultPath = (
1417 item.data(0, Qt.ItemDataRole.UserRole)
1418 if item.text(0).endswith("/")
1419 else os.path.dirname(item.data(0, Qt.ItemDataRole.UserRole))
1420 )
1421 else:
1422 defaultPath = self.deviceCwd.text()
1105 dirPath, ok = QInputDialog.getText( 1423 dirPath, ok = QInputDialog.getText(
1106 self, 1424 self,
1107 self.tr("Create Directory"), 1425 self.tr("Create Directory"),
1108 self.tr("Enter directory name:"), 1426 self.tr("Enter directory name:"),
1109 QLineEdit.EchoMode.Normal, 1427 QLineEdit.EchoMode.Normal,
1428 defaultPath,
1110 ) 1429 )
1111 if ok and dirPath: 1430 if ok and dirPath:
1112 self.__fileManager.mkdir(dirPath) 1431 self.__fileManager.mkdir(dirPath)
1113 1432
1114 @pyqtSlot() 1433 @pyqtSlot()
1117 Private slot to delete an empty directory on the device. 1436 Private slot to delete an empty directory on the device.
1118 """ 1437 """
1119 if self.__repl.deviceSupportsLocalFileAccess(): 1438 if self.__repl.deviceSupportsLocalFileAccess():
1120 self.__deleteLocalDirectoryTree(True) 1439 self.__deleteLocalDirectoryTree(True)
1121 else: 1440 else:
1122 if bool(len(self.deviceFileTreeWidget.selectedItems())): 1441 if bool(self.deviceFileTreeWidget.selectedItems()):
1123 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) 1442 dirname = self.deviceFileTreeWidget.selectedItems()[0].data(
1124 cwd = self.deviceCwd.text() 1443 0, Qt.ItemDataRole.UserRole
1125 if cwd: 1444 )
1126 if cwd != "/":
1127 dirname = cwd + "/" + name[:-1]
1128 else:
1129 dirname = "/" + name[:-1]
1130 else:
1131 dirname = name[:-1]
1132 dlg = DeleteFilesConfirmationDialog( 1445 dlg = DeleteFilesConfirmationDialog(
1133 self, 1446 self,
1134 self.tr("Delete Directory"), 1447 self.tr("Delete Directory"),
1135 self.tr("Do you really want to delete this directory?"), 1448 self.tr("Do you really want to delete this directory?"),
1136 [dirname], 1449 [dirname],
1146 """ 1459 """
1147 if self.__repl.deviceSupportsLocalFileAccess(): 1460 if self.__repl.deviceSupportsLocalFileAccess():
1148 self.__deleteLocalDirectoryTree(True) 1461 self.__deleteLocalDirectoryTree(True)
1149 else: 1462 else:
1150 if bool(len(self.deviceFileTreeWidget.selectedItems())): 1463 if bool(len(self.deviceFileTreeWidget.selectedItems())):
1151 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) 1464 dirname = self.deviceFileTreeWidget.selectedItems()[0].data(
1152 cwd = self.deviceCwd.text() 1465 0, Qt.ItemDataRole.UserRole
1153 if cwd: 1466 )
1154 if cwd != "/":
1155 dirname = cwd + "/" + name[:-1]
1156 else:
1157 dirname = "/" + name[:-1]
1158 else:
1159 dirname = name[:-1]
1160 dlg = DeleteFilesConfirmationDialog( 1467 dlg = DeleteFilesConfirmationDialog(
1161 self, 1468 self,
1162 self.tr("Delete Directory Tree"), 1469 self.tr("Delete Directory Tree"),
1163 self.tr("Do you really want to delete this directory tree?"), 1470 self.tr("Do you really want to delete this directory tree?"),
1164 [dirname], 1471 [dirname],
1172 Private slot to delete a file. 1479 Private slot to delete a file.
1173 """ 1480 """
1174 if self.__repl.deviceSupportsLocalFileAccess(): 1481 if self.__repl.deviceSupportsLocalFileAccess():
1175 self.__deleteLocalFile(True) 1482 self.__deleteLocalFile(True)
1176 else: 1483 else:
1177 if bool(len(self.deviceFileTreeWidget.selectedItems())): 1484 if bool(self.deviceFileTreeWidget.selectedItems()):
1178 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) 1485 filename = self.deviceFileTreeWidget.selectedItems()[0].data(
1179 dirname = self.deviceCwd.text() 1486 0, Qt.ItemDataRole.UserRole
1180 if dirname: 1487 )
1181 if dirname != "/":
1182 filename = dirname + "/" + name
1183 else:
1184 filename = "/" + name
1185 else:
1186 filename = name
1187 dlg = DeleteFilesConfirmationDialog( 1488 dlg = DeleteFilesConfirmationDialog(
1188 self, 1489 self,
1189 self.tr("Delete File"), 1490 self.tr("Delete File"),
1190 self.tr("Do you really want to delete this file?"), 1491 self.tr("Do you really want to delete this file?"),
1191 [filename], 1492 [filename],
1199 Private slot to rename a file on the device. 1500 Private slot to rename a file on the device.
1200 """ 1501 """
1201 if self.__repl.deviceSupportsLocalFileAccess(): 1502 if self.__repl.deviceSupportsLocalFileAccess():
1202 self.__renameLocalFile(True) 1503 self.__renameLocalFile(True)
1203 else: 1504 else:
1204 if bool(len(self.deviceFileTreeWidget.selectedItems())): 1505 if bool(self.deviceFileTreeWidget.selectedItems()):
1205 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) 1506 filename = self.deviceFileTreeWidget.selectedItems()[0].data(
1206 dirname = self.deviceCwd.text() 1507 0, Qt.ItemDataRole.UserRole
1207 if dirname: 1508 )
1208 if dirname != "/":
1209 filename = dirname + "/" + name
1210 else:
1211 filename = "/" + name
1212 else:
1213 filename = name
1214 newname, ok = QInputDialog.getText( 1509 newname, ok = QInputDialog.getText(
1215 self, 1510 self,
1216 self.tr("Rename File"), 1511 self.tr("Rename File"),
1217 self.tr("Enter the new path for the file"), 1512 self.tr("Enter the new path for the file"),
1218 QLineEdit.EchoMode.Normal, 1513 QLineEdit.EchoMode.Normal,
1269 else: 1564 else:
1270 msg += self.tr( 1565 msg += self.tr(
1271 "<p>No file systems or file system information available.</p>" 1566 "<p>No file systems or file system information available.</p>"
1272 ) 1567 )
1273 EricMessageBox.information(self, self.tr("Filesystem Information"), msg) 1568 EricMessageBox.information(self, self.tr("Filesystem Information"), msg)
1569
1570 @pyqtSlot()
1571 def __clearDeviceSelection(self):
1572 """
1573 Private slot to clear the local selection.
1574 """
1575 for item in self.deviceFileTreeWidget.selectedItems()[:]:
1576 item.setSelected(False)

eric ide

mercurial