views:

95

answers:

2

Hello there,

first off, im new to python and pyqt so please bear with me.

Im using a QTableView with a QSqlTableModel everything works as intended. The last column of the view contains only 0 and 1 as value which i want to display as checkbox and this column should be editable.

Ive read that you should subclass QItemDelegate which i did. Unluckily my table wont show the last column as a checkbox.

I tried to set the delegate only for the last column (the way i would prefer) using setItemDelegateForColumn(), it didnt work. So i modified it and set it for the entire QTableView using setItemDelegate() reacting only to requests to the last column. It still wont work. Wont work means there are no error messages it just wont do what i say ;) It seems that none of the methods i reimplemented gets ever called except init(). So i guess im missing something fundamental.

Ive extracted the relevant lines of code, KSCheckBoxDelegate is my subclass. This is the version where the delegate is set up for the entire QTableView.


-- code from applications main class --
self.taglist = QTableView()
self.tagmodel = QSqlTableModel()
self.tagmodel.setTable("data")
self.tagmodel.select()
self.taglist.setModel(self.tagmodel)
print self.taglist.itemDelegate()
myDel = KSCheckBoxDelegate(self)
myDel.colnumber = 3
self.taglist.setItemDelegate(myDel)


-- KSCheckBoxDelegate.py --
from PyQt4.QtGui import *

class KSCheckBoxDelegate(QStyledItemDelegate):

    colnumber = None

    def __init__ (self, parent = None):
        print "KSCheckBoxDelegate::init"
        QStyledItemDelegate.__init__(self, parent)

    def createEditor(self, parent, option, index):
        print "KSCheckBoxDelegate::createEditor"
        if index.column()==self.colnumber:
            return QCheckBox(self)
        else:
            return QStyledItemDelegate.createEditor(self, parent, option, index)

    def setEditorData (self, editor, index):
        print "KSCheckBoxDelegate::setEditorData"
        if index.column() == self.colnumber:
            cont = index.model().data(index, Qt.DisplayRole).toString()
            if cont == "1":
                editor.setCheckState(Qt.Checked)
            else:
                editor.setCheckState(Qt.UnChecked)
        else:
            QStyledItemDelegate.setEditorData(self,editor, index)

    def setModelData (self, editor, model, index):
        print "KSCheckBoxDelegate::setModelData"
        if index.column() == self.colnumber:
            if editor.checkBox.checkState() == Qt.Checked:
                model.setData(index, 1)
            else:
                model.setData(index, 0)
        else:
            QStyledItemDelegate.setModelData(self, editor, model, index)

Any hints for me on that issue?

Furthermore i have difficulties with the currentChanged() signal of the QTableViews selectionModel. Im printing the top right coordinates of the selection. I keep getting wrong indexes (not invalid) when clicking with the left mouse button. Using the cursor keys gets me the right indexes. Using selectionChanged() has the same behaviour. Im actually getting the coordinates of the second last selected cell of the QTableView. For instance im clicking on the coordinates <1,1> <2,1> the second click would show me the coordinates <1,1>.


selInd = self.taglist.selectionModel().selectedIndexes()
if(len(selInd) > 0):
    self.xPosData=selInd[0].column()
    self.yPosData=selInd[0].row()

Fixed that by myself, with using QTableView.currentIndex() instead of selectionModel.selectedIndexes() :)

And last off using OnManualSubmit as editStrategy doesnt throw an error (return false) when calling submitAll() but doesnt save the data either. It works with choosing OnFieldChange as editStrategy. Which i can live with but is not was i have intended to do.

Thanks for your time.

Horst

A: 

I think it would be simpler to create your own model basing QSqlTableModel, and for your 0/1 column return QVariant() for QDisplayRole and return Qt::Checked/Qt::Unchecked for Qt::CheckStateRole depending on value. For all other cases return QSqlTableModel::data

class MySqlTableModel: public QSqlTableModel
{
public:
// contructors
    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole)
    {
        if(index.column() == 3 /* your 0/1 column */)
        {
            if(role == Qt::DisplayRole)
            {
                return QVariant();
            }
            else if(role == Qt::CheckStateRole)
            {
                QString value = QSqlTableModel::data(index, Qt::DisplayRole).toString();
                return value == "1" ? Qt::Checked : Qt::Unchecked;
            }
        }

        return QSqlTableModel::data(index, role);
    }
};

I know it's C++ code, but logic is still same, so just readjust it for Python

Kamil Klimek
Just a short update and another question, im actually back to applying the delegate only to the column, after starting my app this column stil, shows 0 and 1 BUT if i double click a cell im getting a checkbox which represents the values correctly. Unluckily i cannot edit the checkbox (its not clickable). Do i need to derive my own model and overload the flags() method ? i did to no avail :/ Furthermore i now think i need to reimplement the paint() method of my delegate to have it show checkboxes, is this right ?
Horst Helge
Did you try my solution?
Kamil Klimek
I tried it Kamil, thanks for your effort. unluckily i did not mage to get it into a working state :/ - Im actually running with a QSQLTableModel, a QItemDelegate assigned to column 3 and it works :) Im only missing the ability to edit the checkboxes atm.
Horst Helge
A: 

Hello there,

at least i managed to have my delegate show checkboxes with the correct state in display and edit mode. Because i couldnt find a complete description on how to do it ill share it. Maybe other ppl trying to do something similar without having worked with qt ever before.

Only one thing is missing, thats the items being editable, i guess its related to the flags question i asked in my opening post.

-- KSCheckBoxDelegate.py --


class KSCheckBoxDelegate(QStyledItemDelegate):
    def __init__ (self, parent = None):
        print "KSCheckBoxDelegate::init"
        QStyledItemDelegate.__init__(self, parent)

    def createEditor(self, parent, option, index):
        print "KSCheckBoxDelegate::createEditor"
        return QCheckBox(parent)

    def setEditorData (self, editor, index):
        print "KSCheckBoxDelegate::setEditorData"
        cont = index.model().data(index, Qt.DisplayRole).toString()
        if cont == "1":
            editor.setCheckState(Qt.Checked)
        else:
            editor.setCheckState(Qt.Unchecked)

    def setModelData (self, editor, model, index):
        print "KSCheckBoxDelegate::setModelData"
        if editor.checkState() == Qt.Checked:
            model.setData(index, 1)
        else:
            model.setData(index, 0)

    def paint (self, painter, option, index):
        myopt = QStyleOptionViewItemV4(option)
        newopt = QStyleOptionButton()
        newopt.rect = myopt.rect
        newopt.palette = myopt.palette
        if index.data().toBool():
            newopt.state |= QStyle.State_On
        QApplication.style().drawControl(QStyle.CE_CheckBox, newopt, painter)

    def sizeHint (self, option, index):
        return 18
Horst Helge