You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 18:50:38