当鼠标悬停在带有 QFileSystemModel 的此 QTreeView 中的项目上时如何打印文件路径

How to print the file path when the mouse hovers over an item in this QTreeView with QFileSystemModel

我正在制作一个带有 QFileSystem 模型的自定义 QTreeView,我设置了一个 MouseMoveEvent 来打印悬停项目的路径。

我陷入了困境,并做了各种奇怪的事情来完成这项工作。

这是最新的最小可重现代码:

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

class MainWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.resize(500, 300)
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)
        self.myList = CustomTreeWidget()
        self.myList.model.setRootPath("/Volumes/Home/User/Desktop/testsrc")
        self.myList.setObjectName("/Volumes/Home/User/Desktop/testsrc")
        self.layout.addWidget(self.myList)

class CustomTreeWidget(QTreeView):

    def __init__(self):
        super().__init__()
        self.model = QFileSystemModel()
        self.model.setFilter(QDir.NoDotAndDotDot | QDir.Files)
        self.setModel(self.model)
        self.setAlternatingRowColors(True)
        self.setDragDropMode(QAbstractItemView.DragDrop)
        self.setIndentation(0)
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.setMouseTracking(True)
        self.model.directoryLoaded.connect(self._runwhenloaded)

    def _runwhenloaded(self):
        self.setRootIndex(self.model.index(self.objectName()))
        self.model.setRootPath(self.objectName())

    def mouseMoveEvent(self, event):
        prev = ""
        if self.selectedIndexes():
            prev = self.selectedIndexes()[0]
        x = event.x()
        y = event.y()
        self.setSelection(QRect(x, y, 1, 1), QItemSelectionModel.ClearAndSelect)
        self.setCurrentIndex(self.selectedIndexes()[0])
        print(self.model.filePath(self.currentIndex()))
        if prev:
            self.setCurrentIndex(prev)
        # pos = QCursor.pos()
        # indexat = self.indexAt(pos).row() # why -1?
        # print(indexat) # why -1?
        # print(self.indexAt(pos).row())


if __name__ == "__main__":
    app =  QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())    

显然这个例子根本不合适,因为它会破坏多个选择并在鼠标移动时滚动到先前选择的项目,并且只是一般的 hack。

我经历了很多次迭代,阅读了所有我能接触到的东西,但我还是想不通。

最接近的答案似乎是 ,但它是用 C 写的,我看不懂。

所以问题是:当鼠标悬停在这个QTreeView中的一个项目上时,如何打印文件路径?

一个可能的解决方案是创建一个事件过滤器来跟踪悬停事件,并根据该信息发出具有 QModelIndex 的信号:

import sys

from PyQt5.QtCore import (
    pyqtSignal,
    pyqtSlot,
    Qt,
    QDir,
    QEvent,
    QModelIndex,
    QObject,
    QPersistentModelIndex,
    QStandardPaths,
)
from PyQt5.QtWidgets import (
    QAbstractItemView,
    QApplication,
    QFileSystemModel,
    QMainWindow,
    QTreeView,
)
from PyQt5 import sip


class HoverViewHelper(QObject):
    hovered = pyqtSignal(QModelIndex)

    def __init__(self, view):
        super().__init__(view)

        self._current_index = QPersistentModelIndex()
        if not isinstance(view, QAbstractItemView):
            raise TypeError(f"The {view} must be of type QAbstractItemView")
        self._view = view

        self.view.viewport().setAttribute(Qt.WA_Hover)
        self.view.viewport().installEventFilter(self)

    @property
    def view(self):
        return self._view

    def eventFilter(self, obj, event):
        if sip.isdeleted(self.view):
            return True
        if self.view.viewport() is obj:
            if event.type() in (QEvent.HoverMove, QEvent.HoverEnter):
                p = event.pos()
                index = self.view.indexAt(p)
                self._update_index(index)
            elif event.type() == QEvent.HoverLeave:
                if self._current_index.isValid():
                    self._update_index(QModelIndex())
        return super().eventFilter(obj, event)

    def _update_index(self, index):
        pindex = QPersistentModelIndex(index)
        if pindex != self._current_index:
            self._current_index = pindex
            self.hovered.emit(QModelIndex(self._current_index))


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.model = QFileSystemModel()
        self.model.setRootPath(QDir.rootPath())

        self.view = QTreeView()
        self.view.setModel(self.model)
        path = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)
        self.view.setRootIndex(self.model.index(path))

        self.setCentralWidget(self.view)

        helper = HoverViewHelper(self.view)
        helper.hovered.connect(self.handle_hovered)

    @pyqtSlot(QModelIndex)
    def handle_hovered(self, index):
        if not index.isValid():
            return
        path = self.model.filePath(index)
        print(f"path: {path}")


def main():

    app = QApplication(sys.argv)

    w = MainWindow()
    w.resize(640, 480)
    w.show()

    app.exec_()


if __name__ == "__main__":
    main()