如何从链接到 QTableView 的模型中插入和删除行 [英] How to insert and remove row from model linked to QTableView

查看:33
本文介绍了如何从链接到 QTableView 的模型中插入和删除行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

removeRows() 通过删除所选行按预期工作.但是insertRows() 存在问题.由于某种原因,新项目不会出现在所选的索引号处.导致此问题的原因是什么?

The removeRows() works as intended by deleting the selected row. But there is a problem with 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_())

推荐答案

insertRows() 方法出错.传入的 row 参数变量(选择的是 QModelIndex.row() 编号)在 for 循环 中意外重新声明:

Mistake was in insertRows() method. The incoming row argument variable (which is selected QModelIndex.row() number) was accidentally re-declared in for loop:

for row in range(rows):

我已将参数 row 变量重命名为 postion 并在下面发布了固定的工作代码(proxyB 排序属性已被注释掉. 它将 ViewB 项目显示为未排序.这样更容易看到真实"的项目顺序:

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:

对代码进行了一些调整.在幕后"发生了很多事情:在 insertRows()removeRows() 完成它们的工作之前或之后.

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.

例如:

当我们调用这些方法时,提供的第一个参数必须是 QModelIndex 的行号.方法将使用它作为起点"或起始行号",从中添加(插入)或删除索引:与第二个整数参数所说的一样多.

When we call these methods the first argument supplied must be a QModelIndex's row number. 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.

众所周知,代理模型modelIndexes的行号和列号与与它们相关的sourceModel的行号和列号不匹配模型索引.有趣的是,在这两种方法甚至接收到行号参数之前,有一个从 proxy 的行号到 sourceModel 的行号的转换.从打印输出可以看出:buttonClicked() 方法发送"第 0 行,而 insertRows() 方法打印出它收到的不是 0 行号(如果它提供了从代理驱动的 TableView 中获取"的模型索引,并且启用并激活了排序或过滤).

As we know the proxy model's modelIndexess row and column numbers do not match to the associated with them the sourceModel's 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. 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).

除此之外,这两种方法如何从 modelself.items 变量中删除或弹出数据,还有一些复杂"机制发生.removeRows() 如果行号不按顺序跟随,则方法返回"自身以完成工作.完整的代码发布如下:

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. removeRows() method kind of "returns" back to itself to finish a job if the row numbers do not follow in a sequence. 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 "
		 ...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 "
		 ...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 '
 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_())

这篇关于如何从链接到 QTableView 的模型中插入和删除行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆