【问题标题】:QSortFilterProxyModel headerDataQSortFilterProxyModel headerData
【发布时间】:2021-09-18 00:56:59
【问题描述】:

我有 2 个 QTableView 连接到 2 个 QSortFilterProxyModel,它们仅连接到 1 个源模型 (QSqlRelationalTableModel)。

在对任何代理模型进行排序或过滤时,它会反映在相应的表视图中。但 当我修改其中一个代理模型的标题数据(装饰角色)时,它会出现在两个表视图中。

我的问题是,标头数据是直接应用于源模型还是仅应用于代理?

我用它来创建一个 sqlite 示例数据库:

conn = sqlite3.connect('customers.db')
c = conn.cursor()
c.execute("PRAGMA foreign_keys=on;")

c.execute("""CREATE TABLE IF NOT EXISTS provinces (
        ProvinceId TEXT PRIMARY KEY, 
        Name TEXT NOT NULL
        )""")

c.execute("""CREATE TABLE IF NOT EXISTS customers (
        CustomerId TEXT PRIMARY KEY, 
        Name TEXT NOT NULL,
        ProvinceId TEXT,
        FOREIGN KEY (ProvinceId) REFERENCES provinces (ProvinceId) 
                ON UPDATE CASCADE
                ON DELETE RESTRICT
        )""")

c.execute("INSERT INTO provinces VALUES ('N', 'Northern')")
c.execute("INSERT INTO provinces VALUES ('E', 'Eastern')")
c.execute("INSERT INTO provinces VALUES ('W', 'Western')")
c.execute("INSERT INTO provinces VALUES ('S', 'Southern')")
c.execute("INSERT INTO provinces VALUES ('C', 'Central')")

c.execute("INSERT INTO customers VALUES ('1', 'customer1', 'N')")
c.execute("INSERT INTO customers VALUES ('2', 'customer2', 'E')")
c.execute("INSERT INTO customers VALUES ('3', 'customer3', 'W')")
c.execute("INSERT INTO customers VALUES ('4', 'customer4', 'S')")
c.execute("INSERT INTO customers VALUES ('5', 'customer5', 'C')")

conn.commit()
conn.close()

这是在为代理模型应用装饰时显示我的问题的窗口:

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.db = QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName("customers.db")
        self.db.open()

        self.model = QSqlRelationalTableModel(self, self.db)
        self.model.setTable("customers")
        self.model.select()

        self.proxy1 = QSortFilterProxyModel()
        self.proxy1.setSourceModel(self.model)

        self.proxy2 = QSortFilterProxyModel()
        self.proxy2.setSourceModel(self.model)

        hBox = QHBoxLayout()

        self.tblView1 = QTableView()
        self.tblView1.setModel(self.proxy1)

        self.tblView2 = QTableView()
        self.tblView2.setModel(self.proxy2)

        hBox.addWidget(self.tblView1)
        hBox.addWidget(self.tblView2)

        icon = QIcon(QPixmap("Resources/icon.png"))

        self.proxy1.setHeaderData(
            1, Qt.Orientation.Horizontal, icon, Qt.ItemDataRole.DecorationRole)

        widget = QWidget()
        widget.setLayout(hBox)
        self.setLayout(hBox)
        self.show()


def main():
    App = QApplication(sys.argv)
    window = Window()
    sys.exit(App.exec_())


if __name__ == '__main__':
    main()

【问题讨论】:

    标签: python pyqt qtableview qsortfilterproxymodel qsqlrelationaltablemodel


    【解决方案1】:

    问题是因为proxymodel的setHeaderData方法调用了sourcemodel的setHeaderData,即相当于调用sourceModel的方法导致它也传播到其他proxymodel。一种可能的解决方案是覆盖代理模型的 headerData 方法,使其仅在该代理模型中返回所需的值。

    import sys
    
    from PyQt5.QtCore import QSortFilterProxyModel, Qt
    from PyQt5.QtGui import QIcon, QPixmap
    from PyQt5.QtWidgets import QWidget, QHBoxLayout, QTableView, QApplication
    from PyQt5.QtSql import QSqlDatabase, QSqlRelationalTableModel
    
    
    class SortFilterProxyModel(QSortFilterProxyModel):
        def headerData(self, section, orientation, role=Qt.DisplayRole):
            if (
                section == 1
                and orientation == Qt.Horizontal
                and role == Qt.ItemDataRole.DecorationRole
            ):
                return QIcon(QPixmap("Resources/icon.png"))
            return super().headerData(section, orientation, role)
    
    
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.db = QSqlDatabase.addDatabase("QSQLITE")
            self.db.setDatabaseName("customers.db")
            self.db.open()
    
            self.model = QSqlRelationalTableModel(self, self.db)
            self.model.setTable("customers")
            self.model.select()
    
            self.proxy1 = SortFilterProxyModel()
            self.proxy1.setSourceModel(self.model)
    
            self.proxy2 = QSortFilterProxyModel()
            self.proxy2.setSourceModel(self.model)
    
            hBox = QHBoxLayout(self)
    
            self.tblView1 = QTableView()
            self.tblView1.setModel(self.proxy1)
    
            self.tblView2 = QTableView()
            self.tblView2.setModel(self.proxy2)
    
            hBox.addWidget(self.tblView1)
            hBox.addWidget(self.tblView2)
    
    
    def main():
        App = QApplication(sys.argv)
        window = Window()
        window.show()
        sys.exit(App.exec_())
    
    
    if __name__ == "__main__":
        main()
    

    【讨论】:

    • 此解决方案部分适用于我的实现。有时,在发出信号 headerDataChanged 之前,标头数据不会更改。剩下的就是:如何手动发出这个信号?
    • @embabi mmm,我不知道你的实现,但试试self.tblView1.horizontalHeader().update()
    • 我设法通过将所需的代理部分标题数据设置为其当前值来使其正常工作,因此发出信号 headerDataChanged 并在表视图中更新。 value = proxy1.headerData(section, orientation, role) 然后proxy1.setHeaderData(section, orientation, value, role)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多