I am trying to implement some kind of list view within a PySide GUI which gives the user the opportunity to enable/disable some entries of the list before finally processing the list.
I decided to use a QTableView and QAbstractTableModel with a CheckBoxDelegate class which renders a checkbox for each row in the table view. Checking and unchecking an entry will set the enabled attribute of the underlying list's object accordingly. This allows me to easily skip entries when processing.
I want to draw a centered checkbox. Thus i am using a subclass of QCheckbox within the CheckBoxDelegate based on this SO question http://ift.tt/1JOtSY5. Now my problem is that i am getting two checkboxes in column 0. But i dont understand why...
This is my code
# -*- coding: UTF-8 -*-
import sys
from sip import setdestroyonexit
from PySide import QtCore
from PySide import QtGui
def do_action(obj):
print "do stuff for", obj.data_value
class MyObject(object):
"""docstring for BatchObject"""
def __init__(self, data_value, enabled=True):
self.data_value = data_value
self.enabled = enabled
self.result = None
self.action = ''
class MyCheckBox(QtGui.QCheckBox):
def __init__(self, parent):
QtGui.QCheckBox.__init__(self, parent)
# create a centered checkbox
self.cb = QtGui.QCheckBox(parent)
cbLayout = QtGui.QHBoxLayout(self)
cbLayout.addWidget(self.cb, 0, QtCore.Qt.AlignCenter)
self.cb.clicked.connect(self.stateChanged)
def isChecked(self):
return self.cb.isChecked()
def setChecked(self, value):
self.cb.setChecked(value)
@QtCore.Slot()
def stateChanged(self):
print "sender", self.sender()
self.clicked.emit()
class CheckBoxDelegate(QtGui.QItemDelegate):
"""
A delegate that places a fully functioning QCheckBox in every
cell of the column to which it's applied
"""
def __init__(self, parent):
QtGui.QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
cb = MyCheckBox(parent)
cb.clicked.connect(self.stateChanged)
return cb
def paint(self, painter, option, index):
value = index.data()
if value:
value = QtCore.Qt.Checked
else:
value = QtCore.Qt.Unchecked
self.drawCheck(painter, option, option.rect, value)
self.drawFocus(painter, option, option.rect)
def setEditorData(self, editor, index):
""" Update the value of the editor """
editor.blockSignals(True)
editor.setChecked(index.model().checked_state(index))
editor.blockSignals(False)
def setModelData(self, editor, model, index):
""" Send data to the model """
model.setData(index, editor.isChecked(), QtCore.Qt.EditRole)
@QtCore.Slot()
def stateChanged(self):
print "sender", self.sender()
self.commitData.emit(self.sender())
class TableView(QtGui.QTableView):
"""
A simple table to demonstrate the QCheckBox delegate.
"""
def __init__(self, *args, **kwargs):
QtGui.QTableView.__init__(self, *args, **kwargs)
# Set the delegate for column 0 of our table
self.setItemDelegateForColumn(0, CheckBoxDelegate(self))
class MyWindow(QtGui.QWidget):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
# setGeometry(x_pos, y_pos, width, height)
self.setGeometry(300, 200, 640, 480)
self.setWindowTitle("CheckBoxDelegate with two Checkboxes?")
self.object_list = [
MyObject('Task 1'),
MyObject('Task 2'),
MyObject('Task 3'),
]
self.header = ['Active', 'Data value', 'Result', 'Action']
table_model = MyTableModel(self,
self.object_list,
['enabled', 'data_value', 'result', 'action'],
self.header)
self.table_view = TableView()
self.table_view.setModel(table_model)
active_col = self.header.index('Active')
for row in range(0, table_model.rowCount()):
self.table_view.openPersistentEditor(table_model.index(row, active_col))
action_col = self.header.index('Action')
for i, bo in enumerate(self.object_list):
btn = QtGui.QPushButton(self.table_view)
btn.setText("View")
self.table_view.setIndexWidget(table_model.index(i, action_col), btn)
btn.clicked.connect(lambda obj=bo: do_action(obj))
# set font
font = QtGui.QFont("Calibri", 10)
self.table_view.setFont(font)
# set column width to fit contents (set font first!)
self.table_view.resizeColumnsToContents()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.table_view)
self.setLayout(layout)
class MyTableModel(QtCore.QAbstractTableModel):
def __init__(self, parent, rows, columns, header, *args):
QtCore.QAbstractTableModel.__init__(self, parent, *args)
self.rows = rows
self.columns = columns
self.header = header
self.CB_COL = 0
assert len(columns) == len(header), "Header names dont have the same " \
"length as supplied columns"
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.rows)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.columns)
def checked_state(self, index):
if not index.isValid():
return None
elif index.column() == self.CB_COL:
attr_name = self.columns[index.column()]
row = self.rows[index.row()]
return getattr(row, attr_name)
else:
return None
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return None
elif role == QtCore.Qt.DisplayRole:
attr_name = self.columns[index.column()]
row = self.rows[index.row()]
if index.column() == self.CB_COL:
# no text for checkbox column's
return None
else:
return getattr(row, attr_name)
elif role == QtCore.Qt.CheckStateRole:
return None
else:
return None
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
attr_name = self.columns[index.column()]
row = self.rows[index.row()]
if ((index.column() == self.CB_COL)
and (value != self.rows[index.row()].enabled)):
if value:
print "Enabled",
else:
print "Disabled",
print self.rows[index.row()].data_value
setattr(row, attr_name, value)
self.emit(QtCore.SIGNAL("dataChanged(const QModelIndex&, const QModelIndex &)"),
index, index)
return True
else:
return False
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.header[col]
return None
def flags(self, index):
if (index.column() == self.CB_COL):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
else:
return QtCore.Qt.ItemIsEnabled
if __name__ == "__main__":
# avoid crash on exit
setdestroyonexit(False)
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
Can anybody give me an explanation why this happens (and how i could fix it)?
Aucun commentaire:
Enregistrer un commentaire