PyQt5 QTreeView访问NAS服务器时删除与拖放后无法刷新求助
NAS路径下QTreeView删除与拖放后无法自动刷新的解决方案
问题描述
使用PyQt5的QTreeView开发简易文件管理器时,本地文件夹路径(示例:C:\Users\USER\Documents\projects\someFile)下执行创建、删除等操作后,TreeView可正常显示变更;但NAS服务器路径(示例:\\my-ip-add\web\data\someFile)下,删除文件/文件夹以及拖放操作后,实际文件已完成删除或移动,但TreeView无法自动刷新。目前创建操作已实现正常刷新,需解决删除和拖放操作后的刷新问题。
解决思路与修改方案
1. 修复删除操作的刷新逻辑
删除文件/文件夹后,已删除的路径不再存在,此时刷新父目录才能让模型感知到内容变化。替换原代码中刷新已删除路径的逻辑,改为获取父目录索引并刷新。
2. 完善拖放操作后的刷新
拖放完成后,分别刷新源文件的父目录和目标目录,确保两处的文件结构都能同步更新。
3. 强制刷新模型(针对网络路径)
对于NAS这类网络共享路径,主动调用self.model_base.refresh()(Qt 5.10+支持)可以强制模型重新扫描目录,确保视图显示最新状态。
修改后的完整代码
import os import sys from PyQt5.QtWidgets import QTreeView, QFileSystemModel, QApplication, QMenu, QAbstractItemView, QAction, QFileDialog, QMessageBox from PyQt5.QtCore import * from PyQt5.QtGui import * import options import shutil class Tree(QTreeView): def __init__(self, path): QTreeView.__init__(self) self.settings = options.Settings() self.model_base = QFileSystemModel() self.path = path self.model_base.setRootPath(self.path) self.setModel(self.model_base) self.setRootIndex(self.model_base.index(self.path)) self.model_base.setReadOnly(False) self.setSelectionMode(self.SingleSelection) self.setDragDropMode(QAbstractItemView.InternalMove) self.setDragEnabled(True) self.setAcceptDrops(True) self.setDropIndicatorShown(True) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.open_menu) self.doubleClicked.connect(self.OpenFileFromTree) def dragEnterEvent(self, event): m = event.mimeData() if m.hasUrls(): for url in m.urls(): if url.isLocalFile(): event.accept() return event.ignore() def dropEvent(self, event): if event.source(): # 记录源路径的父目录 source_indexes = self.selectedIndexes() source_parent_paths = set() for idx in source_indexes: parent_idx = idx.parent() if idx.parent().isValid() else idx source_parent_paths.add(self.model_base.filePath(parent_idx)) # 执行默认拖放 QTreeView.dropEvent(self, event) # 刷新源父目录和目标目录 target_idx = self.indexAt(event.pos()) if not self.model_base.isDir(target_idx): target_idx = target_idx.parent() target_path = self.model_base.filePath(target_idx) # 刷新所有相关目录 for path in source_parent_paths: self.model_base.setRootPath(path) if hasattr(self.model_base, 'refresh'): self.model_base.refresh(self.model_base.index(path)) self.model_base.setRootPath(target_path) if hasattr(self.model_base, 'refresh'): self.model_base.refresh(self.model_base.index(target_path)) event.acceptProposedAction() else: ix = self.indexAt(event.pos()) if not self.model().isDir(ix): ix = ix.parent() pathDir = self.model().filePath(ix) m = event.mimeData() if m.hasUrls(): urlLocals = [url for url in m.urls() if url.isLocalFile()] accepted = False source_parent_paths = set() for urlLocal in urlLocals: path = urlLocal.toLocalFile() info = QFileInfo(path) source_parent_paths.add(info.dir().absolutePath()) n_path = QDir(pathDir).filePath(info.fileName()) o_path = info.absoluteFilePath() if n_path == o_path: continue if info.isDir(): QDir().rename(o_path, n_path) else: qfile = QFile(o_path) if QFile(n_path).exists(): n_path += "(copy)" qfile.rename(n_path) accepted = True if accepted: # 刷新源父目录和目标目录 self.model_base.setRootPath(pathDir) if hasattr(self.model_base, 'refresh'): self.model_base.refresh(self.model_base.index(pathDir)) for path in source_parent_paths: self.model_base.setRootPath(path) if hasattr(self.model_base, 'refresh'): self.model_base.refresh(self.model_base.index(path)) event.acceptProposedAction() def open_menu(self): menu = QMenu() NewFolderAction = QAction('New Folder', self) NewFolderAction.triggered.connect(self.create_folder) NewFileAction = QAction('New File', self) NewFileAction.triggered.connect(self.create_file) DeleteAction = QAction('Delete', self) DeleteAction.triggered.connect(self.delete_file) RenameAction = QAction('Rename', self) menu.addAction(NewFolderAction) menu.addAction(NewFileAction) menu.addAction(DeleteAction) menu.exec_(QCursor.pos()) def delete_file(self): box = QMessageBox() box.setIcon(QMessageBox.Question) box.setWindowTitle('ΠΡΟΣΟΧΗ!') box.setText('Are you sure?') box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) buttonY = box.button(QMessageBox.Yes) buttonY.setText('Yes') buttonN = box.button(QMessageBox.No) buttonN.setText('Cancel') box.exec_() if box.clickedButton() == buttonY: try: index = self.currentIndex() file_path = self.model_base.filePath(index) # 获取父目录路径 parent_path = os.path.dirname(file_path) if os.path.isfile(file_path): os.remove(file_path) else: shutil.rmtree(file_path, ignore_errors=True) # 刷新父目录 self.model_base.setRootPath(parent_path) # 如果Qt版本支持,强制刷新父目录索引 if hasattr(self.model_base, 'refresh'): parent_index = self.model_base.index(parent_path) self.model_base.refresh(parent_index) except Exception as e: options.SaveLogs('delete_file' + str(e)) pass def create_file(self): index = self.currentIndex() file_path = self.model_base.filePath(index) file_name = QFileDialog.getSaveFileName(self, "Επιλογή αρχείου", file_path) if file_name[0]: with open(file_name[0], 'w') as f: new_file_index = self.model_base.index(file_name[0]) self.expand(new_file_index) self.setCurrentIndex(new_file_index) def create_folder(self): try: index = self.currentIndex() if not self.model_base.isDir(index): index = index.parent() path = self.model_base.filePath(index) new_folder = QFileDialog.getSaveFileName(self, "Select Folder", path) if new_folder[0]: new_folder_path = os.path.join(path, new_folder[0]) QtCore.QDir().mkdir(new_folder_path) new_folder_index = self.model_base.index(new_folder_path) self.expand(new_folder_index) self.setCurrentIndex(new_folder_index) except Exception as e: options.SaveLogs('create_folder ' + str(e)) pass def OpenFileFromTree(self): try: index = self.currentIndex() file_path = self.model_base.filePath(index) p = file_path.replace("//", "\\\\").replace("/", "\\") os.startfile(p) except Exception as e: options.SaveLogs('OpenFileFromTree ' + str(e)) pass if __name__ == '__main__': app = QApplication(sys.argv) tree = Tree(r"\\my-ip\web\data\someFile") tree.show() sys.exit(app.exec_())
关键修改说明
- 删除操作:删除后不再刷新已删除的路径,改为刷新其父目录,同时调用
refresh()(Qt5.10+支持)强制模型更新该目录的内容。 - 拖放操作:内部拖放时记录源文件的父目录,外部拖入时记录源文件的父目录,完成拖放后同时刷新源父目录和目标目录,确保两处的视图都同步更新。
- 兼容Qt版本:通过
hasattr判断是否支持refresh()方法,避免低版本Qt报错。
内容的提问来源于stack exchange,提问作者Paris Thiva
相关产品推荐
相关产品推荐

