我正在開發一個包含 QTreeView、QStandardItemModel 和 QSortFilterProxyModel 的 PyQt5 應用程式。TreeView 在某些行的最后一列上也有一個 QToolBar。
我為示例制作了一個簡化版本:

這是源代碼:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class view(QWidget):
def __init__(self):
super(view, self).__init__()
self.tree = QTreeView(self)
layout = QVBoxLayout(self)
layout.addWidget(self.tree)
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(["Col 0", "Col 1", "Col 2", "Toolbar"])
self.tree.header().setDefaultSectionSize(180)
self.tree.setModel(self.model)
self.importData()
self.tree.expandAll()
def importData(self, root=None):
for i in range(3):
parent1 = QStandardItem("Family {}".format(i))
for j in range(3):
self.createRow(parent1, i, j)
def createRow(self, parent, i, j):
child1 = QStandardItem("Child {}".format(i * 3 j))
child2 = QStandardItem("row: {}, col: {}".format(i, j 1))
child3 = QStandardItem("row: {}, col: {}".format(i, j 2))
child4 = QStandardItem("")
parent.appendRow([child1, child2, child3, child4])
self.model.appendRow(parent)
toolbar = QToolBar()
toolbar.addWidget(QLabel("Toolbar Btn: "))
toolbar.addWidget(QPushButton("Btn"))
self.tree.setIndexWidget(child4.index(), toolbar)
if __name__ == "__main__":
app = QApplication(sys.argv)
view = view()
view.setGeometry(300, 100, 600, 300)
view.setWindowTitle("QTreeview Example")
view.show()
sys.exit(app.exec_())
現在我想使用 QLineEdit 小部件和 QSortFilterProxyModel 添加過濾器,但正如您在下方看到的工具列被洗掉了。有人可以解釋為什么以及如何解決這個問題嗎?

到目前為止,這是我的代碼:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class view(QWidget):
def __init__(self):
super(view, self).__init__()
self.tree = QTreeView(self)
layout = QVBoxLayout(self)
self.filter = QLineEdit()
self.filter.textChanged.connect(self.onTextChanged)
layout.addWidget(self.filter)
layout.addWidget(self.tree)
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(["Col 0", "Col 1", "Col 2", "Toolbar"])
self.proxyModel = QSortFilterProxyModel(
self.tree, recursiveFilteringEnabled=True
)
self.proxyModel.setSourceModel(self.model)
self.tree.header().setDefaultSectionSize(180)
self.tree.setModel(self.proxyModel)
self.importData()
self.tree.expandAll()
def importData(self, root=None):
for i in range(3):
parent1 = QStandardItem("Family {}".format(i))
for j in range(3):
self.createRow(parent1, i, j)
def createRow(self, parent, i, j):
child1 = QStandardItem("Child {}".format(i * 3 j))
child2 = QStandardItem("row: {}, col: {}".format(i, j 1))
child3 = QStandardItem("row: {}, col: {}".format(i, j 2))
child4 = QStandardItem("")
parent.appendRow([child1, child2, child3, child4])
self.model.appendRow(parent)
toolbar = QToolBar()
toolbar.addWidget(QLabel("Toolbar Btn: "))
toolbar.addWidget(QPushButton("Btn"))
self.tree.setIndexWidget(child4.index(), toolbar)
def onTextChanged(self, text):
self.proxyModel.setFilterRegExp(text)
if __name__ == "__main__":
app = QApplication(sys.argv)
view = view()
view.setGeometry(300, 100, 600, 300)
view.setWindowTitle("QTreeview Example")
view.show()
sys.exit(app.exec_())
謝謝你的幫助 !
uj5u.com熱心網友回復:
您的代碼中存在三個問題。
首先,不應該在createRow()函式中添加父級,importData()而應該在添加子級之前添加。
Qt 在輸出中警告你:
StdErr: QStandardItem::insertRows: Ignoring duplicate insertion of item ...
然后,用于的索引setIndexWidget() 必須基于視圖的模型(即代理),而您正在使用源模型,因此索引無效,因為它屬于另一個模型。
最后,當使用過濾器時,過濾后的索引和它們的索引小部件將被完全破壞,因此當您取消設定過濾器時,這些小部件將不會被恢復。
解決方案是在子索引實際添加到模型時設定小部件,這可以通過rowsInserted()在驗證父索引有效(否則它們將是頂級項)之后連接到信號來完成。
請注意,QToolBar 不適合這種用途,而應使用標準的 QWidget(或 QFrame)。
class view(QWidget):
def __init__(self):
# ...
self.proxyModel.rowsInserted.connect(self.updateWidgets)
def importData(self, root=None):
for i in range(3):
parent1 = QStandardItem("Family {}".format(i))
self.model.appendRow(parent1)
for j in range(3):
self.createRow(parent1, i, j)
def createRow(self, parent, i, j):
child1 = QStandardItem("Child {}".format(i * 3 j))
child2 = QStandardItem("row: {}, col: {}".format(i, j 1))
child3 = QStandardItem("row: {}, col: {}".format(i, j 2))
child4 = QStandardItem("")
parent.appendRow([child1, child2, child3, child4])
def updateWidgets(self, parent, first, last):
if not parent.isValid():
return
for row in range(first, last 1):
toolbar = QWidget()
layout = QHBoxLayout(toolbar)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(QLabel("Toolbar Btn: "))
layout.addWidget(QPushButton("Btn"))
childIndex = self.proxyModel.index(row, 3, parent)
self.tree.setIndexWidget(childIndex, toolbar)
注意:類和常量應始終具有大寫的名稱。
uj5u.com熱心網友回復:
問題部分與操作發生的順序有關。考慮代碼...
def createRow(self, parent, i, j):
child1 = QStandardItem("Child {}".format(i * 3 j))
child2 = QStandardItem("row: {}, col: {}".format(i, j 1))
child3 = QStandardItem("row: {}, col: {}".format(i, j 2))
child4 = QStandardItem("")
parent.appendRow([child1, child2, child3, child4])
self.model.appendRow(parent)
toolbar = QToolBar()
toolbar.addWidget(QLabel("Toolbar Btn: "))
toolbar.addWidget(QPushButton("Btn"))
self.tree.setIndexWidget(child4.index(), toolbar)
到時候createRow被稱為樹視圖的源模型是代理——而不是QStandardItemModel. 因此呼叫...
self.tree.setIndexWidget(child4.index(), toolbar)
將失敗,因為QModelIndex回傳的 bychild4.index()未被視圖識別為屬于其源模型。
相反,您需要將child4.index()from回傳的值映射QStandardItemModel到QSortFilterProxyModel類似...
self.tree.setIndexWidget(self.proxyModel.mapFromSource(child4.index()), toolbar)
順便說一句,這條線...
self.model.appendRow(parent)
increateRow由于parent被多次添加而導致警告。這條線應該移出createRow和移入importData. 正確的importData實作createRow將是......
def importData(self, root=None):
for i in range(3):
parent1 = QStandardItem("Family {}".format(i))
self.model.appendRow(parent1)
for j in range(3):
self.createRow(parent1, i, j)
def createRow(self, parent, i, j):
child1 = QStandardItem("Child {}".format(i * 3 j))
child2 = QStandardItem("row: {}, col: {}".format(i, j 1))
child3 = QStandardItem("row: {}, col: {}".format(i, j 2))
child4 = QStandardItem("")
parent.appendRow([child1, child2, child3, child4])
toolbar = QToolBar()
toolbar.addWidget(QLabel("Toolbar Btn: "))
toolbar.addWidget(QPushButton("Btn"))
self.tree.setIndexWidget(self.proxyModel.mapFromSource(child4.index()), toolbar)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/429955.html
