无法使自定义ItemDelegate工作

0 投票
2 回答
2697 浏览
提问于 2025-04-16 04:41

首先,我是新手,刚开始学习Python和PyQt,请多多包涵。

我正在使用QTableView和QSqlTableModel,一切都按预期工作。视图的最后一列只包含0和1,我想把它们显示为复选框,并且这一列应该是可以编辑的。

我听说应该创建一个QItemDelegate的子类,我确实做了。不幸的是,我的表格没有把最后一列显示为复选框。

我尝试只为最后一列设置代理(这是我更喜欢的方式),使用setItemDelegateForColumn(),但没有成功。于是我修改了代码,为整个QTableView设置了代理,使用setItemDelegate(),只对最后一列的请求做反应。结果还是不行。这里的“不行”是指没有错误信息,它就是不按照我说的去做;)似乎我重写的所有方法除了init()都没有被调用,所以我想我可能漏掉了什么基本的东西。

我提取了相关的代码行,KSCheckBoxDelegate是我的子类。这是为整个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)

对此问题有什么提示吗?

此外,我在处理QTableView的selectionModel的currentChanged()信号时也遇到困难。我正在打印选中区域的右上角坐标。当我用左键点击时,总是得到错误的索引(不是无效的)。而使用方向键时能得到正确的索引。使用selectionChanged()也有同样的表现。我实际上得到了QTableView中倒数第二个选中单元格的坐标。例如,我点击坐标<1,1>和<2,1>,第二次点击会显示坐标<1,1>。


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

我自己解决了这个问题,使用QTableView.currentIndex()代替selectionModel.selectedIndexes() :)

最后,使用OnManualSubmit作为编辑策略时,调用submitAll()不会抛出错误(返回false),但也不保存数据。选择OnFieldChange作为编辑策略时就可以正常工作。我可以接受这个,但这不是我原本想要的效果。

感谢你的时间。

霍斯特

2 个回答

0

至少我成功让我的代理在显示和编辑模式下显示带有正确状态的复选框。因为我找不到完整的说明来做到这一点,所以我决定分享一下。也许其他人也在尝试做类似的事情,但之前从未接触过qt。

只有一件事还缺失,那就是这些项目是可编辑的,我想这和我在开头提问的标志问题有关。

-- 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
1

我觉得创建一个自己的模型,基于QSqlTableModel会更简单。在你的0/1列中,针对QDisplayRole返回QVariant(),而针对Qt::CheckStateRole则根据值返回Qt::Checked或Qt::Unchecked。对于其他情况,直接返回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);
    }
};

我知道这是C++代码,但逻辑是一样的,所以只需要把它调整一下就能用在Python上。

撰写回答