选择 QTreeView 的最顶层项目

伊万·勒布朗

我有一个Treeview(继承自QTreeView)模型QFileSystemModel这个Treeview对象被添加到另一个带有QPushButton. 该按钮将通过 a 打开一个目录QFileDialog,并调用Treeviewset_directory方法。该方法将模型的根路径设置为所选目录,并将修改树视图的根索引。然后选择树视图中最顶部的项目。

但是,第一次选择目录时,不会选择最顶部的项目。注释掉第 11 行:self.model.directoryLoaded.connect(self.set_directory),解决了问题。但这意味着该set_directory方法被调用两次:

# default directory opened
<class 'NoneType'>
# open new directory, set_directory called twice
<class 'NoneType'>
<class 'PyQt5.QtWidgets.QFileSystemModel'>

如命令行输出所示,该QModelIndex.model()方法在第一次选择目录时返回 NoneType。如何在不调用该set_directory方法两次的情况下将树视图的当前索引设置为最顶部的项目QModelIndex当目录未被访问时,为什么模型是 NoneType ?

主要脚本如下:

from PyQt5.QtWidgets import QPushButton, QTreeView, QFileSystemModel, QWidget, QFileDialog, QApplication, QHBoxLayout
import sys

class Treeview(QTreeView):

    def __init__(self, parent=None):
        super(QTreeView, self).__init__(parent)
        self.model = QFileSystemModel()
        self.setModel(self.model)
        self.set_directory(".")
        # self.model.directoryLoaded.connect(self.set_directory)

    def set_directory(self, path):
        self.setRootIndex(self.model.setRootPath(path))
        self.setCurrentIndex(self.model.index(0, 0, self.rootIndex()))
        print(type(self.model.index(0, 0, self.rootIndex()).model()))

class MainWidget(QWidget):

    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)
        self.tview = Treeview(self)
        vlayout = QHBoxLayout(self)
        vlayout.addWidget(self.tview)
        open_button = QPushButton(self)
        open_button.clicked.connect(self.open_dir)
        vlayout.addWidget(open_button)

    def open_dir(self):
        filepath = QFileDialog.getExistingDirectory(self)
        if filepath:
            self.tview.set_directory(filepath)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    widget = MainWidget()
    widget.show()
    sys.exit(app.exec_())

音乐家

存在三个问题:

  • directoryLoaded每当第一次加载目录的内容时调用,这在单独的线程上异步发生;如果“根”目录的内容还没有被加载,它就没有子目录,所以任何访问根索引子目录的尝试都将返回无效索引(这就是你得到的原因None);
  • 每当第一次在树中展开目录时也会发送信号,如果您将其连接到,则会产生问题set_directory:如果您打开文件夹“home”然后展开目录“documents”,模型将加载所有它的内容并directoryLoaded为“文档”文件夹发出,然后set_directory将再次依次调用
  • 文件系统模型还对项目进行排序(默认情况下按字母顺序排列,目录在前),这意味着需要更多时间来获取您认为是第一项的内容:出于性能原因,操作系统返回目录的内容取决于在文件系统上,这取决于它的实现:有时按创建/修改时间排序,其他按块的物理位置/索引等排序;

考虑到上述情况,您不能依赖directoryLoaded(当然您不应该将它连接到set_directory),但是您可以使用该layoutChanged信号,因为它总是在排序完成时发出(并且 QFileSystemModel 总是在根更改时对模型进行排序);唯一的问题是您必须仅在需要时才这样做。

解决方案是创建一个函数来尝试设置顶部项目,如果它无效,则它将自身连接layoutChanged信号;此时,当模型完成其工作并且顶部索引可用时,将发出信号。使用标志可以帮助我们知道信号是否已连接,然后断开它,这在您需要支持排序的情况下很重要。

class Treeview(QTreeView):
    layoutCheck = False
    def __init__(self, parent=None):
        super(QTreeView, self).__init__(parent)
        self.model = QFileSystemModel()
        self.setModel(self.model)
        self.set_directory(QDir.currentPath())
        self.setSortingEnabled(True)

    def setTopIndex(self):
        topIndex = self.model.index(0, 0, self.rootIndex())
        print(topIndex.isValid(), topIndex.model())
        if topIndex.isValid():
            self.setCurrentIndex(topIndex)
            if self.layoutCheck:
                self.model.layoutChanged.disconnect(self.setTopIndex)
                self.layoutCheck = False
        else:
            if not self.layoutCheck:
                self.model.layoutChanged.connect(self.setTopIndex)
                self.layoutCheck = True

    def set_directory(self, path):
        self.setRootIndex(self.model.setRootPath(path))
        self.setTopIndex()

请考虑layoutCheck标志非常重要,原因有很多:

  • 如前面所解释的,layoutChanged始终在型号排序射出; 这不仅在尝试使用标题进行排序时发生,而且在添加文件或目录时也会发生;
  • 信号连接不是排他的,你甚至可以多次将相同的信号连接到同一个槽/函数,结果是该函数将被调用与被连接的次数一样多;如果你不是很小心,你可能会冒着递归的风险;
  • 该标志也适用于空目录,避免了上述递归风险;如果一个新的根目录被打开并且它是空的,它显然会返回一个无效的顶部项目的索引(因为没有),但在任何情况下信号都会断开:如果另一个目录(包含内容)被打开但尚未打开已加载,信号不会再次连接,相反,如果内容已经加载,无论如何它都会断开连接;

一种可能性是使用Qt.UniqueConnection连接类型和一个try块来断开连接,但是,虽然这种方法有效并且是一致的,但它有点麻烦:只要连接始终与设置配对,使用基本的布尔标志要简单得多并且更容易阅读和理解。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章