简体   繁体   English

如何从链接到QTableView的模型中插入和删除行

[英]How to insert and remove row from model linked to QTableView

The removeRows() works as intended by deleting the selected row. removeRows()通过删除所选行按预期工作。 But there is a problem with insertRows() . 但是insertRows()存在问题。 By some reason the new items do not appear at the index-number selected. 由于某种原因,新项目不会出现在所选索引编号处。 What causes this problem? 是什么导致此问题?

在此处输入图片说明

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys

class Model(QAbstractTableModel):
    def __init__(self, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.items = ['Item_003','Item_000','Item_005','Item_004','Item_001']
        self.numbers=[20,10,30,50,40]
        self.added=0
    def rowCount(self, parent=QModelIndex()):
        return len(self.items)      
    def columnCount(self, parent=QModelIndex()):
        return 2

    def data(self, index, role):
        if not index.isValid(): return QVariant()
        elif role != Qt.DisplayRole:
            return QVariant()

        row=index.row()
        column=index.column()

        if column==0:
            if row<len(self.items):
                return QVariant(self.items[row])
        elif column==1:
            if row<len(self.numbers):
                return QVariant( self.numbers[row] )
        else:
            return QVariant()

    def removeRows(self, row, rows=1, index=QModelIndex()):
        print "Removing at row: %s"%row
        self.beginRemoveRows(QModelIndex(), row, row + rows - 1)
        self.items = self.items[:row] + self.items[row + rows:]
        self.endRemoveRows()
        return True

    def insertRows(self, row, rows=1, index=QModelIndex()):
        print "Inserting at row: %s"%row
        self.beginInsertRows(QModelIndex(), row, row + rows - 1)
        for row in range(rows):
            self.items.insert(row + row, "New Item %s"%self.added)
            self.added+=1
        self.endInsertRows()
        return True

class Proxy(QSortFilterProxyModel):
    def __init__(self):
        super(Proxy, self).__init__()

class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        vLayout=QVBoxLayout(self)
        self.setLayout(vLayout)

        hLayout=QHBoxLayout()
        vLayout.insertLayout(0, hLayout)

        tableModel=Model(self)               

        proxyA=Proxy()
        proxyA.setSourceModel(tableModel)
        proxyB=Proxy()
        proxyB.setSourceModel(tableModel)

        self.ViewA=QTableView(self)
        self.ViewA.setModel(proxyA)
        self.ViewA.clicked.connect(self.viewClicked)
        self.ViewA.setSortingEnabled(True)
        self.ViewA.sortByColumn(0, Qt.AscendingOrder)


        self.ViewB=QTableView(self) 
        self.ViewB.setModel(proxyB)
        self.ViewB.clicked.connect(self.viewClicked)
        self.ViewB.setSortingEnabled(True)
        self.ViewB.sortByColumn(0, Qt.AscendingOrder)

        hLayout.addWidget(self.ViewA)
        hLayout.addWidget(self.ViewB)

        insertButton=QPushButton('Insert Row Above Selection')
        insertButton.setObjectName('insertButton')
        insertButton.clicked.connect(self.buttonClicked)
        removeButton=QPushButton('Remove Selected Item')
        removeButton.setObjectName('removeButton')
        removeButton.clicked.connect(self.buttonClicked)

        vLayout.addWidget(insertButton)
        vLayout.addWidget(removeButton)


    def viewClicked(self, indexClicked):
        print 'indexClicked() row: %s  column: %s'%(indexClicked.row(), indexClicked.column() )
        proxy=indexClicked.model()

    def buttonClicked(self):
        button=self.sender()
        if not button: return

        tableView=None
        if self.ViewA.hasFocus(): tableView=self.ViewA
        elif self.ViewB.hasFocus(): tableView=self.ViewB
        if not tableView: return

        indexes=tableView.selectionModel().selectedIndexes()

        for index in indexes:
            if not index.isValid(): continue
            if button.objectName()=='removeButton':
                tableView.model().removeRows(index.row(), 1, QModelIndex())

            elif button.objectName()=='insertButton':
                tableView.model().insertRows(index.row(), 1, QModelIndex())

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

Mistake was in insertRows() method. 错误是在insertRows()方法中。 The incoming row argument variable (which is selected QModelIndex.row() number) was accidentally re-declared in for loop : 意外在for loop重新声明了传入的row参数变量(已选择QModelIndex.row()号):

for row in range(rows):

I've renamed the argument row variable to postion and the fixed working code is posted below (the proxyB sorting attribute was commented out. It displays the ViewB items as unsorted. This way it is easier to see a "real" items order: 我已经改名为参数row变量postion和固定的工作代码下面贴(在proxyB 。分类属性被注释掉它显示ViewB 。项目作为未分类的这种方式很容易看到一个“真实”的项目订单:

Edited later: 以后编辑:

There was a few more tweaks made to the code. 对代码进行了一些其他调整。 There is a lot happening "behind of scenes": before or after insertRows() and removeRows() do their job. “幕后”发生了很多事情:在insertRows()removeRows() insertRows()之前或之后。

For example: 例如:

When we call these methods the first argument supplied must be a QModelIndex 's row number. 当我们调用这些方法时,提供的第一个参数必须是QModelIndex的行号。 It is going to be used by the methods as a "starting point" or "starting row number" from where the indexes will be added (inserted) or removed: as many as the second integer argument says. 方法将使用它作为“起点”或“起点行号”,从该起点将添加(插入)或删除索引:与第二个整数参数说的一样多。

As we know the proxy model 's modelIndexes s row and column numbers do not match to the associated with them the sourceModel 's modelIndexes. 众所周知, proxy modelmodelIndexes的行号和列号与与之关联的sourceModel的modelIndexes不匹配。 Interesting but there is a translation from proxy 's row numbers to sourceModel 's ones before the row-number-argument is even received by those two methods. 有趣的是,在这两种方法甚至没有接收到行号参数之前,存在从proxy的行号到sourceModel的转换。 It can be seen from a print out: the buttonClicked() method "sends" row 0 while the insertRows() method prints out that it received other than 0 row number (if it was supplied an modelIndex "taken" from by-Proxy-driven TableView with sorting or filtering enabled and active). 从打印输出中可以看出: buttonClicked()方法“发送”第0行,而insertRows()方法打印出它接收的行号不是0(如果是从by-Proxy-驱动的TableView,并启用并启用了排序或过滤功能)。

Aside from it there is some "intricate" mechanism happening on how these two methods remove or pop the data from the model 's self.items variable. 除此之外,这两种方法如何从modelself.items变量中删除或弹出数据,还存在一些“复杂”的机制。 removeRows() method kind of "returns" back to itself to finish a job if the row numbers do not follow in a sequence. 如果行号未按顺序排列,则removeRows()方法会“返回”自身以完成作业。 The fully working code is posted below: 完整的工作代码如下:

在此处输入图片说明

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys

class Model(QAbstractTableModel):
    def __init__(self, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.items = ['Item_A000','Item_B001','Item_A002','Item_B003','Item_B004']
        self.numbers=[20,10,30,50,40]
        self.added=0
    def rowCount(self, parent=QModelIndex()):
        return len(self.items)      
    def columnCount(self, parent=QModelIndex()):
        return 2

    def data(self, index, role):
        if not index.isValid(): return QVariant()
        elif role != Qt.DisplayRole:
            return QVariant()

        row=index.row()
        column=index.column()

        if column==0:
            if row<len(self.items):
                return QVariant(self.items[row])
        elif column==1:
            if row<len(self.numbers):
                return QVariant( self.numbers[row] )
        else:
            return QVariant()

    def removeRows(self, position, rows=1, index=QModelIndex()):
        print "\n\t\t ...removeRows() Starting position: '%s'"%position, 'with the total rows to be deleted: ', rows
        self.beginRemoveRows(QModelIndex(), position, position + rows - 1)       
        self.items = self.items[:position] + self.items[position + rows:]
        self.endRemoveRows()

        return True

    def insertRows(self, position, rows=1, index=QModelIndex()):
        print "\n\t\t ...insertRows() Starting position: '%s'"%position, 'with the total rows to be inserted: ', rows
        indexSelected=self.index(position, 0)
        itemSelected=indexSelected.data().toPyObject()

        self.beginInsertRows(QModelIndex(), position, position + rows - 1)
        for row in range(rows):
            self.items.insert(position + row,  "%s_%s"% (itemSelected, self.added))
            self.added+=1
        self.endInsertRows()
        return True

class Proxy(QSortFilterProxyModel):
    def __init__(self):
        super(Proxy, self).__init__()

    def filterAcceptsRow(self, rowProc, parentProc):  
        modelIndex=self.sourceModel().index(rowProc, 0, parentProc)
        item=self.sourceModel().data(modelIndex, Qt.DisplayRole).toPyObject()

        if item and 'B' in item:
            return True
        else: return False


class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        vLayout=QVBoxLayout(self)
        self.setLayout(vLayout)

        hLayout=QHBoxLayout()
        vLayout.insertLayout(0, hLayout)

        tableModel=Model(self)               

        proxyB=Proxy()
        proxyB.setSourceModel(tableModel)

        self.ViewA=QTableView(self)
        self.ViewA.setModel(tableModel)
        self.ViewA.clicked.connect(self.viewClicked)

        self.ViewB=QTableView(self) 
        self.ViewB.setModel(proxyB)
        self.ViewB.clicked.connect(self.viewClicked)
        self.ViewB.setSortingEnabled(True)
        self.ViewB.sortByColumn(0, Qt.AscendingOrder)
        self.ViewB.setSelectionBehavior(QTableView.SelectRows)

        hLayout.addWidget(self.ViewA)
        hLayout.addWidget(self.ViewB)

        insertButton=QPushButton('Insert Row Above Selection')
        insertButton.setObjectName('insertButton')
        insertButton.clicked.connect(self.buttonClicked)
        removeButton=QPushButton('Remove Selected Item')
        removeButton.setObjectName('removeButton')
        removeButton.clicked.connect(self.buttonClicked)

        vLayout.addWidget(insertButton)
        vLayout.addWidget(removeButton)

    def getZeroColumnSelectedIndexes(self, tableView=None):
        if not tableView: return
        selectedIndexes=tableView.selectedIndexes()
        if not selectedIndexes: return
        return [index for index in selectedIndexes if not index.column()]

    def viewClicked(self, indexClicked):
        print 'indexClicked() row: %s  column: %s'%(indexClicked.row(), indexClicked.column() )
        proxy=indexClicked.model()

    def buttonClicked(self):
        button=self.sender()
        if not button: return

        tableView=None
        if self.ViewA.hasFocus(): tableView=self.ViewA
        elif self.ViewB.hasFocus(): tableView=self.ViewB
        if not tableView: print 'buttonClicked(): not tableView'; return

        zeroColumnSelectedIndexes=self.getZeroColumnSelectedIndexes(tableView)
        if not zeroColumnSelectedIndexes: print 'not zeroColumnSelectedIndexes'; return

        firstZeroColumnSelectedIndex=zeroColumnSelectedIndexes[0]
        if not firstZeroColumnSelectedIndex or not firstZeroColumnSelectedIndex.isValid():
            print 'buttonClicked(): not firstZeroColumnSelectedIndex.isValid()'; return            

        startingRow=firstZeroColumnSelectedIndex.row()
        print '\n buttonClicked() startingRow =', startingRow

        if button.objectName()=='removeButton':            
            tableView.model().removeRows(startingRow, len(zeroColumnSelectedIndexes), QModelIndex())

        elif button.objectName()=='insertButton':
            tableView.model().insertRows(startingRow, len(zeroColumnSelectedIndexes), QModelIndex())


if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM