在QT4中使用QTableView制作属性编辑器

来源:互联网 发布:丹尼爱特牌子好吗知乎 编辑:程序博客网 时间:2024/04/28 12:21

最近在用QT写的一个程序中,需要一个对大量参数进行设置的功能。过去用Delphi的经验是,尽量不要使用QListView和TGrid来实现,因为涉及到控件的对齐,界面的重绘等,反而使简单问题变得复杂。因此这次用QT写程序时也想当然,用控件数组的方式来解决。很快程序写好了,界面如下:



显示出的效果还不错,但是遇到了一个严重的问题,那就是速度。由于QT的机制是自动调整控件比例来适应各种系统和窗体大小,而这个调整的功能其实涉及到复杂的运算。另外,QT的控件创建也不如真正控件的创建那么快。因此当控件数量一大,构建整个界面就变得其慢无比了。在我的机器上,像上图这样的界面打开需要1-2秒时间,而且即使到了性能较好的计算机上也没有什么大的改善。这样的效果就连自己都无法忍受,更别说使用者了。

于是考虑改进,参考QT的example中的spinboxdelegate例子,使用QTableView来解决。具体方法是:

1)在控件设计器中放置一个QTableView,并调整好其与其它控件的位置关系。生成ui文件,在程序中制作相应的界面窗体类。

2)从QItemDelegate中继承出一个新的类,它将用来配合上面的QTableView,用来告诉其是否可以针对某个Cell进入编辑模式,以及在编辑该Cell时,使用什么控件,相互的值如何传递等。
具体来说,只要继承QItemDelegate中以下的几个虚函数就可以了:

class enstCatheterNodeInfoDelegate : public QItemDelegate
{
    Q_OBJECT
public:
    enstCatheterNodeInfoDelegate(QObject *parent, enstCatheterInfo *pCatheterInfo);
    QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                const QModelIndex &index) const;
    void setEditorData(QWidget *editor, const QModelIndex &index) const;
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                const QModelIndex &index) const;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
                const QModelIndex &index) const;
};

createEditor()函数,在QTableView中某个Cell被DoubleClick时触发,这时可以用其index参数,获得该Cell的行与列,如果确定该Cell是可以被编辑的,那么就生成一个对应的编辑控件,如:

            QComboBox *cbo = new QComboBox(parent);
            cbo->addItem(" 1 ");
            cbo->addItem(" 2 ");
            cbo->addItem(" 3 ");
            cbo->installEventFilter(const_cast<enstCatheterNodeInfoDelegate*>(this));
            return cbo;

这时,一个ComboBox就会被自动替换到该Cell的位置上了。以上cbo->installEventFilter(),是为了让QItemDelegate能够获得用户在该控件上的Enter,Escape等事件,以便做出相应处理。
如果该Cell不能被编辑,此时返回NULL就可以了。

setEditorData()函数,用来对生成的控件中的值进行初始化,一般来说,这个函数在以上的createEditor()函数结束后会被自动调用,如下:

            QComboBox *cbo = static_cast<QComboBox*>(editor);
            cbo->setCurrentIndex(0);

editor是QWidget类型的参数,它代表createEditor()函数中生成的那个控件,这里必须将其进行类型转换,才可以变成自己实际生成的那个控件类型。如果在createEditor()中根据index.column()不同,生成了不同的编辑控件,那么这里也需要判断index.column(),来将editor参数转换成相应的控件。

接着setModelData(),它会在用户的离开当前编辑中的Cell时被调用,可以在此将编辑控件中被修改了的数据写回到QTableView中,如下:

            QString tmpstr;
            QComboBox *cbo = static_cast<QComboBox*>(editor);
            tmpstr.setNum(cbo->currentIndex());
            model->setData(index, tmpstr);

最后,updateEditorGeometry(),这个函数是用来控制编辑控件的位置的,一般只需要写成:

    editor->setGeometry(option.rect);

编辑控件就会自动被调整到Cell的大小了。

3)在实际的窗体类中,使用如下的代码将以上这个类与QTableView关联起来:

    enstCatheterNodeInfoDelegate *delegate = new enstCatheterNodeInfoDelegate(this);
    tbl1->setItemDelegate(delegate);

4)光这样还不行,由于QTableView实在太底层了,它还需要知道以怎样的方式显示,即指定一个ItemModel,可以选用的类包括:
QDirModel:可以以树状方式显示某个目录下的所有子目录,以及其相关信息。
QProxyModel:用来将旧的Model类型过渡到新类型上的。
QStandardItemModel:最简单的Grid方式的显示Model。
另外,还可以自己从QAbstractListModel, QAbstractProxyModel, QAbstractTableModel来继承出符合自己要求的model。
偷个懒,我就直接使用QStandardItemModel了:

    mItemModel = new QStandradItemMode(this, 3, 10);
    tbl1->setModel(mItemModel);

5)基本完成,最后再加上初始化QTableView中所有文字,以及最终将设置好了的内容保存出来的代码,就可以了,完成的界面显示如下:



但是,仔细看看,还有不少不满意的地方,TableView的ColumnHeader居然自动填上了数字,而左边则多出了一条垂直Header,另外,所有Cell的宽度变成一样的了,多少有点不爽。。经过努力,总算摸索出了解决方法,不过不清楚真正的解决方法是什么,我这个解决方法应该并不好:

首先从QStandardItemModel中继承出自己的类,暂且名叫enstCatheterNodeItemModel了:

class enstCatheterNodeItemModel : public QStandardItemModel
{
    Q_OBJECT
public:
    enstCatheterNodeItemModel(QObject *parent = 0);
};

在其构造函数中,使用:

enstCatheterNodeItemModel::enstCatheterNodeItemModel(QObject *parent)
    : QStandardItemModel(MAX_CATHETERNODE_COUNT, 3, parent)
{
    setHeaderData(0, Qt::Horizontal, tr("Input"));
    setHeaderData(1, Qt::Horizontal, tr("Signal"));
    setHeaderData(2, Qt::Horizontal, tr("Locate"));
}

强制其只能有三列,并指定每个列的标题。

在窗体的构造函数中,进一步调整QTableView:

    QHeaderView *header;
    header = tbl1->horizontalHeader();
    header->resizeSection(0, 260);
    header->resizeSection(1, 260);
    header->resizeSection(2, 50);
    header = tbl1->verticalHeader();
    header->hide();

设置其各个列的宽度,同时将左边的垂直Header隐藏。

总算大功告成,加载延时已经基本感觉不到了。看看最后的效果:



以及另一组编辑器,其中的颜色选择ComboBox并不是QT自带的控件,而是自己实现的,照样可以被做成编辑控件: