跟我一起学QT10:项视图类

原创
2014/11/21 21:16
阅读数 415

0. 源代码下载

1. 使用项视图来选择和查看数据

https://github.com/leichaojian/qt/tree/master/flowchartsymbolpicker

2. 使用项视图类来新增数据

https://github.com/leichaojian/qt/tree/master/CoordinateSetter

3. 使用预定义模型之QStringListModel

https://github.com/leichaojian/qt/tree/master/TeamLeadersDialog

4. 使用预定义模型之QDirModel

https://github.com/leichaojian/qt/tree/master/DirectoryViewer

5. 使用预定义模型之QSortFilterProxyModel

https://github.com/leichaojian/qt/tree/master/ColorNamesDialog

6. 实现自定义模型之显示数据

https://github.com/leichaojian/qt/tree/master/CurrencyModel

7. 实现自定义模型之修改数据

https://github.com/leichaojian/qt/tree/master/CityModel


8. 实现自定义委托

https://github.com/leichaojian/qt/tree/master/TrackEditor


0. 基础知识汇总

1. MVC(model-view-controller:模型-视图-控制器)模型:模型(model)代表数据集,它对需要查看数据的获取以及任何存储的改变负责。每种类型的数据集都有自己的模型,但不管底层的数据集是什么样子,模型提供给视图(view)的API都是相同的。视图代表的是面向用户的那些数据。在同一时间,任何大数据集只有有限的部分是可见的,所以这个有限的部分就是视图所请求的那部分数据。控制器是用户和视图之间的媒介,它把用户的操作转换为浏览或者编辑数据的请求,这部分数据是根据需要由视图传送给模型的数据。

2. QT中的MV模型:QT没有使用控制器,而是使用委托:用于对项的如何显示和如何编辑提供精细控制。

3. QT中MV模型的优势:可以将一个模型注册到多个视图中,则多个视图会自动保持同步。而且如果决定改变底层数据集的存储方式,只需要修改模型,而视图仍将能够继续正常工作。

1. 使用项视图类来选择和查看数据(使用QListWidget)

1. 关键代码

//视图
    listWidget = new QListWidget;
    listWidget->setIconSize(QSize(60, 60));

    //创建一个项视图类来查看数据
    QMapIterator<int, QString> i(symbolMap);
    while (i.hasNext()) {
        i.next();
        //为每一个数据构建一个项视图(MV结构:M为模型数据:i.value(),而V为视图:listWidget)
        QListWidgetItem *item = new QListWidgetItem(i.value(),
                                                    listWidget);
        //设定一个Icon(视图)
        item->setIcon(iconForSymbol(i.value()));
        //将ID保存到QListWidgetItem中去---Qt::UserRole为自定义角色(模型数据)
        item->setData(Qt::UserRole, i.key());
    }

    而当我们选择了当前的图标,并点击OK的时候,会触发done函数,而函数中的id为map数据中的key()值:


void flowchartsymbolpicker::done(int result)
{
    id = -1;
    if (result = QDialog::Accepted) {
        QListWidgetItem *item = listWidget->currentItem();
        if (item) {
            id = item->data(Qt::UserRole).toInt();
        }
    }
    QDialog::done(result);
}


2. 程序效果图

2. 使用项视图类来新增数据(使用QTableWidget)

1. 关键代码

void CoordinateSetter::addRow()
{
    int row = tableWidget->rowCount();

    //在row后面插入一行,此时tableWidget为新增的一行
    tableWidget->insertRow(row);

    //设置第一列
    QTableWidgetItem *item0 = new QTableWidgetItem;
    item0->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
    tableWidget->setItem(row, 0, item0);

    //设置第二列
    QTableWidgetItem *item1 = new QTableWidgetItem;
    item1->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
    tableWidget->setItem(row, 1, item1);

    tableWidget->setCurrentItem(item0);
}

    如何将数据写入到table表中呢:


//创建0行两列
    tableWidget = new QTableWidget(0, 2);
    tableWidget->setHorizontalHeaderLabels(
                QStringList() << tr("X") << tr("Y"));

    //增加一行,然后往新增的行上写入数据
    for (int row = 0; row < coordinates->count(); ++row) {
        QPointF point = coordinates->at(row);
        addRow();
        tableWidget->item(row, 0)->setText(QString::number(point.x()));;
        tableWidget->item(row, 1)->setText(QString::number(point.y()));;
    }


2. 程序效果图


3. 使用预定义模型之QStringListModel

1. 关键代码

//新建一个QStringListModel模型
    model = new QStringListModel(this);
    //初始化模型数据
    model->setStringList(leaders);

    //listView为视图,数据从模型model中获取
    listView = new QListView;
    listView->setModel(model);
    listView->setEditTriggers(QAbstractItemView::AnyKeyPressed
                              | QAbstractItemView::DoubleClicked);



void TeamLeadersDialog::insert()
{
    //模型用于操作实际的数据--如新增一行,删除一行,然后通过视图来显示
    int row = listView->currentIndex().row();
    model->insertRows(row, 1);

    //但是新增一行的内容必须在视图上进行填写
    QModelIndex index = model->index(row);
    listView->setCurrentIndex(index);
    listView->edit(index);
}

void TeamLeadersDialog::del()
{
    model->removeRows(listView->currentIndex().row(), 1);
}


2. 程序效果图


4. 使用预定义模型之QDirModel

1. 关键代码

//新建模型QDirModel,并且设置只读属性,排序按照目录优先,不区分大小写和以名字排序
    model = new QDirModel;
    model->setReadOnly(false);
    model->setSorting(QDir::DirsFirst | QDir::IgnoreCase | QDir::Name);

    //新建视图QTreeView
    treeView = new QTreeView;
    //将视图和模型关联起来
    treeView->setModel(model);
    //设置视图的属性
    treeView->header()->setStretchLastSection(true);
    treeView->header()->setSortIndicator(0, Qt::AscendingOrder);
    treeView->header()->setSortIndicatorShown(true);
    treeView->header()->setSectionsClickable(true);

    QModelIndex index = model->index(QDir::currentPath());
    //设定模型数据可扩展
    treeView->expand(index);
    treeView->scrollTo(index);
    treeView->resizeColumnToContents(0);

void DirectoryViewer::createDirectory()
{
    //找到要创建文件夹的位置
    QModelIndex index = treeView->currentIndex();
    if (!index.isValid())
        return;

    QString dirName = QInputDialog::getText(this,
                              tr("Create Directory"),
                              tr("Directory name"));
    if (!dirName.isEmpty()) {
        //创建文件夹
        if (!model->mkdir(index, dirName).isValid())
            QMessageBox::information(this, tr("Create Directory"),
                    tr("Failed to create the directory"));
    }
}

void DirectoryViewer::remove()
{
    QModelIndex index = treeView->currentIndex();
    if (!index.isValid())
        return;

    bool ok;
    //判断是文件夹还是文件
    if (model->fileInfo(index).isDir()) {
        ok = model->rmdir(index);
    } else {
        ok = model->remove(index);
    }
    if (!ok)
        QMessageBox::information(this, tr("Remove"),
                tr("Failed to remove %1").arg(model->fileName(index)));
}

2. 程序效果图

5. 使用预定义模型之QSortFilterProxyModel

1. 关键代码

//新建模型并以所有的颜色作为模型数据
    sourceModel = new QStringListModel(this);
    sourceModel->setStringList(QColor::colorNames());

    //新建QSortFilterProxyModel模型,以sourceModel作为基础模型
    proxyModel = new QSortFilterProxyModel(this);
    proxyModel->setSourceModel(sourceModel);
    proxyModel->setFilterKeyColumn(0);

    //新建QListView视图
    listView = new QListView;
    listView->setModel(proxyModel);
    listView->setEditTriggers(QAbstractItemView::NoEditTriggers);

    filterLabel = new QLabel(tr("&Filter:"));
    filterLineEdit = new QLineEdit;
    filterLabel->setBuddy(filterLineEdit);

//设置过滤器
void ColorNamesDialog::reapplyFilter()
{
    QRegExp::PatternSyntax syntax =
            QRegExp::PatternSyntax(syntaxComboBox->itemData(
                    syntaxComboBox->currentIndex()).toInt());
    QRegExp regExp(filterLineEdit->text(), Qt::CaseInsensitive, syntax);
    proxyModel->setFilterRegExp(regExp);
}

2. 程序效果图

1. 在每个模型中,每一个数据元素都有一个模型索引和一套属性,称为角色(role),这些角色保存任意值。

2. 对于列表模型,唯一和索引部分相关的就是行号,可以通过QModelIndex::row()得到。对于表模型,与索引部分相关的就是行号和列号,分别可以通过QModelIndex::row()和QModelIndex::column()得到。对于列表模型和表模型,每一个项的父对象都是根,通常由一个无效的QModelIndex表示。

3. 树模型的父对象是根,但是其他每一个项的父对象都是继承树中的其他一些项。这些父对象可以通过QModelIndex::parent()得到。每一个项都有自己的角色数据,都有0个或多个子对象,每一个项都有属于自己的东西。因为项可以拥有其他项作为子对象,这样它就可以用来显示递归的数据结构。


6. 实现自定义模型之显示数据

1. 关键代码

//role为角色,不同的角色执行不同的动作,通过索引index来进行数据的读取
QVariant CurrencyModel::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) {   //否则,返回数据
        QString rowCurrency = currencyAt(index.row());
        QString columnCurrency = currencyAt(index.column());

        //0不能被除
        if (currencyMap.value(rowCurrency) == 0.0) {
            return "####";
        }

        double amount = currencyMap.value(columnCurrency)
                / currencyMap.value(rowCurrency);

        return QString("%1").arg(amount, 0, 'f', 4);
    }

    return QVariant();
}

2. 程序效果图

7. 实现自定义模型之修改数据

1. 关键代码

QVariant CityModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    //当为对其模式的时候,则对其table表
    if (role == Qt::TextAlignmentRole) {
        return int(Qt::AlignRight | Qt::AlignVCenter);
    } else if (role == Qt::DisplayRole) {
        //为同一个城市,则距离为0
        if (index.row() == index.column())
            return 0;
        //得到不同城市之间的距离
        int offset = offsetOf(index.row(), index.column());
        return distances[offset];
    }
    return QVariant();
}

//用户编辑一项的时候,会触发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();

        //createIndex()函数用于产生一个模型索引。我们需要使用它获得主对角线另外一侧和当前
        //正在被设置的项所对应项的模型索引,参数顺序是行号在列好之前,所以我们得颠倒一下顺序:column,row
        QModelIndex transposedIndex = createIndex(index.column(),
                                                  index.row());
        //修改整个区域(index,index)来刷新界面
        emit dataChanged(index, index);
        emit dataChanged(transposedIndex, transposedIndex);
        return true;
    }
    return false;
}

2. 程序效果图

8. 实现自定义委托

    代码不甚了解,直接保存程序,供后续学习。程序效果图如下:

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部