微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将带有列标题的 QTreeView 改造成 QTableView?

如何解决如何将带有列标题的 QTreeView 改造成 QTableView?

我有一个带有列标题过滤器的 QTreeView,但想使用 QTableView。

问题:我不知道如何重新设计 QTableView 的标头功能。 如果我只是将使用的类从 QTreeView() 切换到 QTableView() 我会得到很多错误 喜欢AttributeError: 'QTableView' object has no attribute 'setHeader'

目前看起来像这样(见下面的 MRE):

enter image description here

我想构建一个带有列标题过滤器的 TableView,如下所示: (由“DB Browser for SQLite”提供)

enter image description here

在第一个回复的返工后,当我删除时,我有以下内容 self.treeView.verticalHeader().hide()

enter image description here

MRE:

import sys
import re
from PyQt5 import QtWidgets,QtGui,QtCore,Qtsql

COUNT_PERS_COLS = 3
col_persID,col_persLAST_NAME,col_persFirsT_NAME = range(COUNT_PERS_COLS)

db = Qtsql.QsqlDatabase.addDatabase("QsqlITE")
db.setDatabaseName(':memory:')

modelQuery = Qtsql.QsqlQueryModel()
modelTable = Qtsql.QsqlRelationalTableModel()

def _human_key(key):
    parts = re.split(r'(\d*\.\d+|\d+)',key)
    return tuple((e.swapcase() if i % 2 == 0 else float(e))
            for i,e in enumerate(parts))

class FilterHeader(QtWidgets.QHeaderView):
    filteractivated = QtCore.pyqtSignal()

    def __init__(self,parent):
        super().__init__(QtCore.Qt.Horizontal,parent)
        self._editors = []
        self._padding = 4
        self.setStretchLastSection(True)        
        self.setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        self.setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        self.setSortIndicatorShown(False)
        self.setSectionsMovable(True)
        self.sectionResized.connect(self.adjustPositions)
        parent.horizontalScrollBar().valueChanged.connect(self.adjustPositions)

    def setFilterBoxes(self,count):
        while self._editors:
            editor = self._editors.pop()
            editor.deleteLater()
        for index in range(count):
            editor = QtWidgets.QLineEdit(self.parent())            
            editor.setPlaceholderText('Filter')
            editor.setClearButtonEnabled(True)
            editor.returnpressed.connect(self.filteractivated.emit)
            self._editors.append(editor)
        self.adjustPositions()

    def sizeHint(self):
        size = super().sizeHint()
        if self._editors:
            height = self._editors[0].sizeHint().height()
            size.setHeight(size.height() + height + self._padding)
        return size

    def updateGeometries(self):
        if self._editors:
            height = self._editors[0].sizeHint().height()
            self.setViewportMargins(0,height + self._padding)
        else:
            self.setViewportMargins(0,0)
        super().updateGeometries()
        self.adjustPositions()

    def adjustPositions(self):
        for index,editor in enumerate(self._editors):
            height = editor.sizeHint().height()
            editor.move(
                self.sectionPosition(index) - self.offset() + 2,height + (self._padding // 2))
            editor.resize(self.sectionSize(index),height)

    def filterText(self,index):
        if 0 <= index < len(self._editors):
            return self._editors[index].text()
        return ''

    def setFilterText(self,index,text):
        if 0 <= index < len(self._editors):
            self._editors[index].setText(text)

    def clearFilters(self):
        for editor in self._editors:
            editor.clear()        


class HumanProxyModel(QtCore.QSortFilterProxyModel):
    def lessthan(self,source_left,source_right):
        data_left = source_left.data()
        data_right = source_right.data()
        if type(data_left) == type(data_right) == str:
            return _human_key(data_left) < _human_key(data_right)
        return super(HumanProxyModel,self).lessthan(source_left,source_right)

    @property
    def filters(self):
        if not hasattr(self,"_filters"):
            self._filters = []
        return self._filters

    @filters.setter
    def filters(self,filters):
        print(f"filters() called.")        

        self._filters = filters
        self.invalidatefilter()                

    def filteracceptsRow(self,sourceRow,sourceParent):        
        for i,text in self.filters:
            if 0 <= i < self.columnCount():
                ix = self.sourceModel().index(sourceRow,i,sourceParent)                
                data = ix.data()
                if text not in data:
                    return False            
        return True        

class winMain(QtWidgets.QMainWindow):
    def __init__(self,parent=None):        
        super().__init__(parent)                
        self.setupUi()
        self.setGeometry(300,200,700,500)        

        self.show()        

    def createPersonModel(self,parent):        
        model = QtGui.QStandardItemmodel(0,COUNT_PERS_COLS,parent)                
        model.setHorizontalHeaderLabels(['ID','Last Name','First Name'])

        return model

    def addPerson(self,model,id,last_name,first_name):        
        model.insertRow(0)        
        model.setData(model.index(0,col_persID),id)
        model.setData(model.index(0,col_persLAST_NAME),last_name)
        model.setData(model.index(0,col_persFirsT_NAME),first_name)

    def handleFilteractivated(self):                
        header = self.treeView.header()
        filters = []

        for i in range(header.count()):
            text = header.filterText(i)
            if text:        
                filters.append((i,text))

        proxy = self.treeView.model()
        proxy.filters = filters        

    def setupUi(self):
        self.centralwidget = QtWidgets.QWidget(self)        
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)        

        self.treeView = QtWidgets.QTreeView(self.centralwidget)        

        self.treeView.setSortingEnabled(True)
        self.treeView.setAlternatingRowColors(True)        
        self.treeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.treeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.treeView.setAnimated(True)
        self.treeView.setItemsExpandable(True)

        self.horizontalLayout.addWidget(self.treeView)
        self.setCentralWidget(self.centralwidget)

        header = FilterHeader(self.treeView)        
        self.treeView.setHeader(header)        

        self.statusBar = QtWidgets.QStatusBar()
        self.setStatusBar(self.statusBar)        

        modelTable.setTable("person")
        self.treeView.setModel(modelTable)

        proxy = HumanProxyModel(self)
        proxy.setSourceModel(modelTable)
        self.treeView.setModel(proxy)        

        header.setFilterBoxes(modelTable.columnCount())
        header.filteractivated.connect(self.handleFilteractivated)        


def create_sample_data():     
    modelQuery.setQuery("""CREATE TABLE IF NOT EXISTS country (                                    
                                    id   INTEGER PRIMARY KEY UNIQUE NOT NULL,name TEXT
                                    )""")

    modelQuery.setQuery("""CREATE TABLE IF NOT EXISTS person (
                                   id         INTEGER PRIMARY KEY UNIQUE NOT NULL,persId     TEXT,lastName   TEXT,firstName  TEXT,country_id INTEGER NOT NULL DEFAULT 3,FOREIGN KEY (country_id) REFERENCES country(id)
                                   )""")

    modelQuery.setQuery("INSERT INTO country (id,name) VALUES (0,'None')")    
    modelQuery.setQuery("INSERT INTO country (id,name) VALUES (1,'Angola')")    
    modelQuery.setQuery("INSERT INTO country (id,name) VALUES (2,'Serbia')")
    modelQuery.setQuery("INSERT INTO country (id,name) VALUES (3,'Georgia')")

    modelQuery.setQuery("INSERT INTO person (id,persId,lastName,firstName,country_id) VALUES (1,'1001','Martin','Robert',1)")
    modelQuery.setQuery("INSERT INTO person (id,country_id) VALUES (2,'1002','Smith','Brad',2)")
    modelQuery.setQuery("INSERT INTO person (id,country_id) VALUES (3,'1003','Angelina',3)")

if __name__ == '__main__':                         
    app = QtWidgets.QApplication(sys.argv)         

    create_sample_data()        

    window = winMain()    
    sys.exit(app.exec_())

添加了“sqlite 数据库浏览器”中的第二个屏幕截图:

enter image description here

解决方法

QTreeView 只有一个标题(水平标题),而 QTableView 有 2 个。此外,QTableView 没有分支,因此它也没有 setAnimated()setItemsExpandable() 方法。

def handleFilterActivated(self):
    # header = self.treeView.header()
    header = self.treeView.horizontalHeader()
    filters = []

    for i in range(header.count()):
        text = header.filterText(i)
        if text:
            filters.append((i,text))

    proxy = self.treeView.model()
    proxy.filters = filters

def setupUi(self):
    self.centralwidget = QtWidgets.QWidget(self)
    self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)

    self.treeView = QtWidgets.QTableView(self.centralwidget)

    self.treeView.setSortingEnabled(True)
    self.treeView.setAlternatingRowColors(True)
    self.treeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
    self.treeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
    # self.treeView.setAnimated(True)
    # self.treeView.setItemsExpandable(True)

    self.horizontalLayout.addWidget(self.treeView)
    self.setCentralWidget(self.centralwidget)

    header = FilterHeader(self.treeView)
    # self.treeView.setHeader(header)
    self.treeView.setHorizontalHeader(header)
    self.treeView.verticalHeader().hide()

    self.statusBar = QtWidgets.QStatusBar()
    self.setStatusBar(self.statusBar)

    modelTable.setTable("person")
    modelTable.select()
    self.treeView.setModel(modelTable)

    proxy = HumanProxyModel(self)
    proxy.setSourceModel(modelTable)
    self.treeView.setModel(proxy)

    header.setFilterBoxes(modelTable.columnCount())
    header.filterActivated.connect(self.handleFilterActivated)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。