当鼠标悬停在带有 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()
我正在制作一个带有 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。
我经历了很多次迭代,阅读了所有我能接触到的东西,但我还是想不通。
最接近的答案似乎是
所以问题是:当鼠标悬停在这个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()