Chinaunix首页 | 论坛 | 博客
  • 博客访问: 536853
  • 博文数量: 108
  • 博客积分: 3242
  • 博客等级: 中校
  • 技术积分: 916
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-07 11:46
文章分类
文章存档

2012年(13)

2010年(95)

分类: C/C++

2010-12-03 11:15:45

前面的例子已经比较清楚的给出了自定义model的方法,就是要覆盖我们所需要的那几个函数就可以了。但是,前面的例子仅仅是简单的展示数据,也就是说数据时只读的。那么,如何能做到读写数据呢?那就要来看进来的例子了。这个例子也是来自C++GUI Programming with Qt 4, 2nd Edition这本书的。
 
还是先来看代码吧:
 
citymodel.h

class CityModel : public QAbstractTableModel
{
        Q_OBJECT
 
public:
        CityModel(QObject *parent = 0);
 
        void setCities(const QStringList &cityNames);
        int rowCount(const QModelIndex &parent) const;
        int columnCount(const QModelIndex &parent) const;
        QVariant data(const QModelIndex &index, int role) const;
        bool setData(const QModelIndex &index, const QVariant &value, int role);
        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
        Qt::ItemFlags flags(const QModelIndex &index) const;
 
private:
        int offsetOf(int row, int column) const;
 
        QStringList cities;
        QVector<int> distances;
};


citymodel.cpp

CityModel::CityModel(QObject *parent)
        : QAbstractTableModel(parent)
{
}
 
int CityModel::rowCount(const QModelIndex & parent) const
{
        return cities.count();
}
 
int CityModel::columnCount(const QModelIndex & parent) const
{
        return cities.count();
}
 
QVariant CityModel::data(const QModelIndex &index, int role) const
{
        if (!index.isValid()) {
                return QVariant();
        }
 
        if (role == Qt::TextAlignmentRole) {
                return int(Qt::AlignRight | Qt::AlignVCenter);
        } else if (role == Qt::DisplayRole) {
                if (index.row() == index.column()) {
                        return 0;
                }
                int offset = offsetOf(index.row(), index.column());
                return distances[offset];
        }
        return QVariant();
}
 
QVariant CityModel::headerData(int section, Qt::Orientation orientation, int role) const
{
        if (role == Qt::DisplayRole) {
                return cities[section];
        }
        return QVariant();
}
 
bool CityModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
        if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) {
                int offset = offsetOf(index.row(), index.column());
                distances[offset] = value.toInt();
 
                QModelIndex transposedIndex = createIndex(index.column(), index.row());
                emit dataChanged(index, index);
                emit dataChanged(transposedIndex, transposedIndex);
                return true;
        }
        return false;
}
 
Qt::ItemFlags CityModel::flags(const QModelIndex &index) const
{
        Qt::ItemFlags flags = QAbstractItemModel::flags(index);
        if (index.row() != index.column()) {
                flags |= Qt::ItemIsEditable;
        }
        return flags;
}
 
void CityModel::setCities(const QStringList &cityNames)
{
        cities = cityNames;
        distances.resize(cities.count() * (cities.count() - 1) / 2);
        distances.fill(0);
        reset();
}
 
int CityModel::offsetOf(int row, int column) const
{
        if (row < column) {
                qSwap(row, column);
        }
        return (row * (row - 1) / 2) + column;
}


代码很长,但实际上和前面我们的那个例子非常相似。这个model也是用于table的,因此还是继承了QAbstractTableModel。CityModel内部有两个数据源:一个QStringList类型的对象,一个QVector<int>类型的对象。前者用于保存城市的名字,需要用户显示的给出;后者是model内部维护的一个存放int的向量。这个CityModel就是要在table中显示两个城市之间的距离。同前面的例子一样,如果我们要把所有的数据都保存下来,显然会造成数据的冗余:城市A到城市B的距离同城市B到城市A的距离是一样的!因此我们还是自定义一个model。同样这个CityModel有个简单的空构造函数,rowCount()和columnCount()函数也是返回list的长度。data()函数根据role的不同返回不同的值。由于在table中坐标是由row和column给出的,因此需要有一个二维坐标到一维坐标的转换,这就是offsetOf()函数的作用。我们把主要精力放在setData()函数上面。

bool CityModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
        if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) {
                int offset = offsetOf(index.row(), index.column());
                distances[offset] = value.toInt();
 
                QModelIndex transposedIndex = createIndex(index.column(), index.row());
                emit dataChanged(index, index);
                emit dataChanged(transposedIndex, transposedIndex);
                return true;
        }
        return false;
}


这个函数在用户编辑数据时会自动调用。也就是说,这时我们的数据已经不是只读的了。函数开始是一个长长的判断:index要是合法的;index的row和column不相等,也就是说两个城市是不同的;数据想的role是Qt::EditRole。如果满足了这三个条件,才会执行下面的操作。首先,由row和column坐标定位到表中的数据项在vector中的位置。然后用户新修改的数据被作为参数value传入,所以我们要把这个参数赋值给distances。createIndex()函数根据column和row值生成一个QModelIndex对象。请注意这里的顺序:row和column是颠倒的!这就把坐标为(row, column)的点关于主对角线对称的那个点(column, row)的index找到了。还记得我们的需求吗?当我们修改了一个数据时,对应的数据也要被修改,这就是这个功能的实现。我们需要emit dataChanged()信号,这个信号接收两个参数:一个是被修改的数据的左上角的坐标,一个是被修改的数据的右下角的坐标。为什么会有两个坐标呢?因此我们修改的数据不一定只是一个。像这里,我们只修改了一个数据,因此这两个值是相同的。数据更新了,我们用这个信号通知view刷新,这样就可以显示新的数据了。最后,如果函数数据修改成功就返回true,否则返回false。
 
最后,我们在main()函数中显示出来这个model:

int main(int argc, char *argv[])
{
        QApplication a(argc, argv);
        QStringList cities;
        cities << "Arvika" << "Boden" << "Eskilstuna" << "Falun";
 
        CityModel cityModel;
        cityModel.setCities(cities);
 
        QTableView tableView;
        tableView.setModel(&cityModel);
        tableView.setAlternatingRowColors(true);
        tableView.setWindowTitle(QObject::tr("Cities"));
        tableView.show();
        return a.exec();
}


这样,我们就把这个model做完了。最后来看看效果吧!
 
阅读(4445) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~