Skip to content

正点原子QT之项目式视图组(基于模型) ​

9. 项目式视图组(基于模型) ​

(1) List View:清单视图
(2) Tree View:树形视图
(3) Table View:表视图
(4) Column View:列表视图
(5) Undo View:撤销列表视图

9.1 QListView:清单视图 ​

QListView 继承 QAbstractItemView,被 QListWidget 和 QUndoView 继承。 QListView 类提供模型上的列表或图标视图。 QListView 以简单的非分层列表或图标集合的形式显示存储在模型中的项。该类用于提供以前由 QListBox 和 QIconView 类提供的列表和图标视图,但是使用了 Qt 的模型/视图体系结构提供的更灵活的方法。

案例:清单图 ​

例 40_qlistview,清单图(难度:简单)。使用一个 ListView 控件,展示如何在列表中插入数据,其中表中的内容可编辑,可删除。

cpp
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置主窗口位置与大小 */
    this->setGeometry(0, 0, 800, 480);

    /* 实例化 */
    listView = new QListView(this);
    /* 将listView居中 */
    setCentralWidget(listView);

    QStringList strList;
    strList<<"高三(1)班"<<"高三(2)班"<<"高三(3)班";

    /* 实例化,字符串模型 */
    stringListModel = new QStringListModel(strList);

    /* 向表中插入一段数据 */
    listView->setModel(stringListModel);
    /* 设置为视图为图标模式 */
    listView->setViewMode(QListView::IconMode);
    /* 设置为不可拖动 */
    listView->setDragEnabled(false);
}

MainWindow::~MainWindow()
{
}

9.2 QTreeView:树形视图 ​

QTreeView 继承 QAbstractItemView,被 QTreeWidget 继承。 QTreeView 类提供树视图的默认模型/视图实现。 QTreeView 实现了模型项的树表示。该类用于提供以前由 QListView 类提供的标准分层列表,但是使用了 Qt 的模型/视图体系结构提供的更灵活的方法。

案例:仿 word 标题 ​

例 41_qtreeview,仿 word 标题(难度:简单)。要使一个 QTreeView 能够显示数据,需要构造一个 model 并设置 QTreeView。

cpp
#include "mainwindow.h"

#include <QStandardItemModel>
#include <QStandardItem>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置窗口的位置与大小 */
    this->setGeometry(0, 0, 800, 480);
    /* 实例化QTreeView对象 */
    treeView = new QTreeView(this);
    /* 居中 */
    setCentralWidget(treeView);

    /* 构建Model */
    QStandardItemModel *sdiModel = new QStandardItemModel(treeView);
    sdiModel->setHorizontalHeaderLabels(
                QStringList()<<QStringLiteral("标题")
                << QStringLiteral("名称")
                );

    for(int i = 0; i < 5; i++) {
        /* 一级标题 */
        QList<QStandardItem*> items1;
        QStandardItem* item1 =
                new QStandardItem(QString::number(i));
        QStandardItem* item2 =
                new QStandardItem(QStringLiteral("一级标题"));
        /* 添加项一 */
        items1.append(item1);
        /* 添加项二 */
        items1.append(item2);
        /* appendRow方法添加到model上 */
        sdiModel->appendRow(items1);

        for(int j = 0; j < 5; j++) {
            /* 在一级标题后面插入二级标题 */
            QList<QStandardItem*> items2;
            QStandardItem* item3 =
                    new QStandardItem(QString::number(j));
            QStandardItem* item4 =
                    new QStandardItem(QStringLiteral("二级标题"));
            items2.append(item3);
            items2.append(item4);
            /* 使用appendRow方法添加到item1上 */
            item1->appendRow(items2);
        }
    }
    /* 设置Model给treeView */
    treeView->setModel(sdiModel);
}

MainWindow::~MainWindow()
{
}

9.3 QTableView:表视图 ​

QTableView 继承 QAbstractItemView,被 QTableWidget 继承。 QTableView 类提供了表视图的默认模型/视图实现。 QTableView 实现了一个表视图,用于显示来自模型的项。该类用于提供以前由 QTable 类提供的标准表,但使用 Qt 的模型/视图体系结构提供的更灵活的方法。

案例:表格视图 ​

例 42_qtableview,表格视图(难度:简单)。要使一个 QTableView 能够显示数据,需要构造一个 model 并设置给 QTableView。

cpp
#include "mainwindow.h"
#include <QStandardItemModel>
#include <QHeaderView>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置窗口的位置与大小 */
    this->setGeometry(0, 0, 800, 480);
    tableView = new QTableView(this);
    setCentralWidget(tableView);
    /* 显示网格线 */
    tableView->setShowGrid(true);

    QStandardItemModel* model = new QStandardItemModel();
    QStringList labels =
            QObject::tr("语文,数学,英语").simplified().split(",");
    /* 设置水平头标签 */
    model->setHorizontalHeaderLabels(labels);

    /* item */
    QStandardItem* item = 0;
    /* model插入项内容 */
    for(int i = 0; i < 5; i++){
        item = new QStandardItem("80");
        model->setItem(i, 0, item);
        item = new QStandardItem("99");
        model->setItem(i, 1, item);
        item = new QStandardItem("100");
        model->setItem(i, 2, item);
    }
    /* 将model设置给tableView */
    tableView->setModel(model);
    /* 平均分列 */
    tableView->horizontalHeader()
            ->setSectionResizeMode(QHeaderView::Stretch);
    /* 平均分行 */
    tableView->verticalHeader()
            ->setSectionResizeMode(QHeaderView::Stretch);
}

MainWindow::~MainWindow()
{
}

9.4 QColumnView:列表视图 ​

ColumnView 继承 QAbstractItemView。QColumnView 在许多 QListViews 中显示一个模型,每个 QListViews 对应树中的每个层次结构。这有时被称为级联列表。 QColumnView 类是模型/视图类之一,是 Qt 模型/视图框架的一部分。

案例:收货地址 ​

例 43_qcolumnview,收货地址(难度:简单)。使用一个 QColumnView,向其插入多级QStandardItem。这样就可以模拟成一个多级联的视图。与我们在像某宝,某东里填写的收货地址十分类似。

cpp
#include "mainwindow.h"
#include <QStandardItem>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置主窗体显示位置与大小 */
    this->setGeometry(0, 0, 800, 480);
    QStandardItemModel *model = new QStandardItemModel;

    /* 省份 */
    QStandardItem *province = new QStandardItem("广东省");

    /* 城市 */
    QStandardItem *city1 = new QStandardItem("茂名市");
    QStandardItem *city2 = new QStandardItem("中山市");

    /* 添加城市到省份下 */
    province->appendRow(city1);
    province->appendRow(city2);

    QStandardItem *town1 = new QStandardItem("电白镇");
    QStandardItem *town2 = new QStandardItem("南头镇");

    /* 添加城镇到城市下 */
    city1->appendRow(town1);
    city2->appendRow(town2);

    columnView = new QColumnView;

    /* 建立model */
    model->appendRow(province);

    /* 设置model */
    columnView->setModel(model);

    /* 设置居中 */
    setCentralWidget(columnView);
}

MainWindow::~MainWindow()
{
}

9.5 QUndoView:撤销列表视图 ​

QUndoView 继承 QlistView。 QUndoView 类显示 QUndoStack 的内容。 QUndoView 是一个QListView,它显示在撤销堆栈上推送的命令列表。总是选择最近执行的命令。

案例:仿 PS 历史记录 ​

例 44_qundoview,仿 PS 历史记录(难度:一般)。如果大家学习过 PS,都知道 PS 里有个历史记录面板,点击就会撤回到历史记录的步骤。

cpp
#include "mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置主窗体显示的位置与大小 */
    this->setGeometry(0, 0, 800, 480);

    /* 实例一个水平布局,用于左侧按钮区域与右侧历史记录面板 */
    hLayout = new QHBoxLayout();

    /* 实例一个水平布局,用于左侧标签与按钮 */
    vLayout = new QVBoxLayout();

    /* 主Widget, 因为MainWindow自带一个布局,
     * 我们要新建一个Widget容纳新布局
     */
    mainWidget = new QWidget();

    /* 用于存放命令行栈 */
    undoStack = new QUndoStack(this);

    /* 用于容纳左侧标签与按钮布局 */
    widget = new QWidget();

    /* 历史记录面板实例化 */
    undoView = new QUndoView(undoStack);

    /* 实例化一个按钮,用于加一操作 */
    pushButton = new QPushButton();

    /* 标签,用于显示计算结果 */
    label = new QLabel();

    /* 设置widget的大小 */
    widget->setMinimumSize(400, 480);

    /* 将两个widget添加到水平布局 */
    hLayout->addWidget(widget);
    hLayout->addWidget(undoView);

    /* 初始化count的值 */
    count = 0;

    /* 显示初始化计算结果 */
    label->setText("计算结果:" + QString::number(count));
    label->setAlignment(Qt::AlignCenter);

    /* 左侧布局 */
    vLayout->addWidget(label);
    vLayout->addWidget(pushButton);

    /* 左侧布局控件的高度设置 */
    label->setMaximumHeight(this->height() / 5);
    pushButton->setMaximumHeight(this->height() / 5);

    /* 按钮文件设置 */
    pushButton->setText("加1");

    /* 设置widget的布局为vLayout */
    widget->setLayout(vLayout);

    /* 将主窗体的布局设置为hLayout */
    mainWidget->setLayout(hLayout);

    /* 设置mainWidget为主窗体的居中widget */
    this->setCentralWidget(mainWidget);

    /* 信号槽连接,按钮点击,执行加一操作 */
    connect(pushButton, SIGNAL(clicked()), this,
            SLOT(pushButtonClieked()));

    /* 信号槽连接,历史记录项index发生变化,显示count大小 */
    connect(undoStack, SIGNAL(indexChanged(int) ),
            this, SLOT(showCountValue(int)));
}

/* 入栈操作会自动调用addCommand的redo */
void MainWindow::pushButtonClieked()
{
    /* 变量值加一 */
    count++;

    /* value指向count的地址 */
    int *value = &count;

    /* 用重写的addCommand类实例化 */
    QUndoCommand *add = new addCommand(value);

    /* 入栈 */
    undoStack->push(add);
}

void MainWindow::showCountValue(int)
{
    /* 标签用于显示计算结果 */
    label->setText("计算结果:" + QString::number(count));
}

MainWindow::~MainWindow()
{

}
cpp
#include "command.h"
#include <QDebug>

addCommand::addCommand(int *value, QUndoCommand *parent)
{
    /* 使用Q_UNUSED,避免未使用的数据类型 */
    Q_UNUSED(parent);

    /* undoView显示的操作信息 */
    setText("进行了加1操作");

    /* value的地址赋值给new_count */
    new_count = value;

    /* 让构造函数传过来的*new_count的值赋值给old_count */
    old_count = *new_count;
}

/* 执行stack push时或者重做操作时会自动调用 */
void addCommand::redo()
{
    /* 重新赋值给new_count */
    *new_count = old_count;

    /* 打印出*new_count的值 */
    qDebug()<<"redo:"<<*new_count<<endl;
}

/* 回撤操作时执行 */
void addCommand::undo()
{
    /* 回撤操作每次应减一 */
    (*new_count)--;

    /* 打印出*new_count的值 */
    qDebug()<<"undo:"<<*new_count<<endl;
}

addCommand::~addCommand()
{

}