The code below creates QTableView
driven by self.myModel
(QAbstractTableModel
).
'Show All' self.checkBox
is linked to self.myModel.cbChanged()
method.
Question: How to modify this code so 'QTableView' gets refreshed as soon as checkbox is checked?
The goal: when the checkbox is checked we want the odd numbered items to be displayed. And the even numbered items to be hidden.
When the checkbox is off (unchecked) we want the even numbered items to be displayed. All the odd numbered items are hidden.
import sys, os
from PyQt4 import QtCore, QtGui
app=QtGui.QApplication(sys.argv)
class TableModel(QtCore.QAbstractTableModel):
def __init__(self):
QtCore.QAbstractTableModel.__init__(self)
self.items=['One','Two','Three','Four','Five','Six','Seven']
self.cb_status=True
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.items)
def columnCount(self, index=QtCore.QModelIndex()):
return 1
def data(self, index, role):
if not index.isValid() or not (0<=index.row()<len(self.items)):
return QtCore.QVariant()
item=str(self.items[index.row()])
if role==QtCore.Qt.DisplayRole and self.cb_status:
return item
else:
return QtCore.QVariant()
def cbChanged(self, arg=None):
self.cb_status=arg
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
mainLayout=QtGui.QHBoxLayout()
self.setLayout(mainLayout)
self.viewA=QtGui.QTableView()
self.viewA.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)
self.myModel=TableModel()
self.viewA.setModel(self.myModel)
self.checkBox=QtGui.QCheckBox("Show All")
self.checkBox.stateChanged.connect(self.myModel.cbChanged)
self.checkBox.setChecked(self.myModel.cb_status)
mainLayout.addWidget(self.viewA)
mainLayout.addWidget(self.checkBox)
self.show()
view=Window()
sys.exit(app.exec_())
For this purpose, you can use the QSortFilterProxyModel
class. This way, we don't tamper with the actual source model's structure or it's data. We just map the main source to this proxy model, which the view uses to display filtered/sorted data. We can affect the data in the proxy model as we please, without the risk of tampering the source model.
Here is your source code modified to use this:
import sys, os
from PyQt4 import QtCore, QtGui
app=QtGui.QApplication(sys.argv)
class TableModel(QtCore.QAbstractTableModel):
def __init__(self):
QtCore.QAbstractTableModel.__init__(self)
self.items=['One','Two','Three','Four','Five','Six','Seven']
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.items)
def columnCount(self, index=QtCore.QModelIndex()):
return 1
def data(self, index, role):
if not index.isValid() or not (0<=index.row()<len(self.items)):
return QtCore.QVariant()
item=str(self.items[index.row()])
if role==QtCore.Qt.DisplayRole:
return item
else:
return QtCore.QVariant()
class MySortFilterProxyModel(QtGui.QSortFilterProxyModel):
def __init__(self):
super(MySortFilterProxyModel, self).__init__()
self.cb_status=True
def cbChanged(self, arg=None):
self.cb_status=arg
print self.cb_status
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
print_when_odd_flag = self.cb_status
is_odd = True
index = self.sourceModel().index(sourceRow, 0, sourceParent)
print "My Row Data: %s" % self.sourceModel().data(index, role=QtCore.Qt.DisplayRole)
if (sourceRow + 1) % 2 == 0:
is_odd = False
if print_when_odd_flag:
if is_odd:
return True
else:
return False
else:
if not is_odd:
return True
else:
return False
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
mainLayout=QtGui.QHBoxLayout()
self.setLayout(mainLayout)
self.viewA=QtGui.QTableView()
self.viewA.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)
self.myModel=TableModel()
self.sortModel = MySortFilterProxyModel()
self.sortModel.setSourceModel(self.myModel)
self.viewA.setModel(self.sortModel)
self.checkBox=QtGui.QCheckBox("Show All")
self.checkBox.stateChanged.connect(self.sortModel.cbChanged)
self.checkBox.setChecked(self.sortModel.cb_status)
mainLayout.addWidget(self.viewA)
mainLayout.addWidget(self.checkBox)
self.show()
view=Window()
sys.exit(app.exec_())
As you can see, I have removed all connection from UI and the main source model. The main source model does not care about whether the checkbox is set or not. This keeps it decoupled. It's cleaner. The proxy model has been given this responsibility now. The filterAcceptsRow()
does the main heavy lifting of displaying the right row based on whether the index of the row shown is odd or even based on the checkbox status.
I have added a few print statements to it, just in case you want to alter the logic based on the data and not the index.
Check out the docs on QSortFilterProxyModel and some examples here.
Hope this was useful.