Qt / Model_View_Delegate

2022-04-12 18:25:13 浏览数 (1)

定义

早期Qt:传统的窗口部件,其本身内部包含,用于存储数据的内置容器。(直观,但是同步数据较低效)

当前Qt:模型 / 视图,model / View,窗口部件如Widget无需维护内部的数据容器,其通过标准的接口获得外部数据。(更高效,避免数据重复)

模型/视图编程的典型代表:Smalltalk语言设计的,大数据集可视化方法—模型—视图—控制器(Model-View-Controller,MVC)。

  • Model(模型):处理数据的逻辑部分,通常负责在数据库中存取数据
  • View(视图):用于数据的显示部分,通常视图的依靠模型的数据而创建的。
  • Controller(控制器):处理用户的交互问题。负责从View读取数据,控制用户的输入,并间接向Model发送数据。

Qt中的应用

Qt的Model /View可以理解是对MVC的变形,将控制器替换成了稍微有些不同的抽象:委托(delegate)。Qt对每种类型的视图都提供了默认的委托,这对绝大多数应用程序而言已经足够了,所以通常我们不需要注意它。

Qt:Model—View—Delegate

对于Qt的Model /View我们可以简单的划分为3种使用级别:

①Model /View的简便类:如QListWidget、QTableWidget、QTreeWidget

②Model /View的预定义模型:如QStringListModel、QStanderItemModel、QFileSystemMode等模型以及数据库模型

③Model /View的自定义模型。可以自定义委托

Ⅰ QTableWidget(不区分模型 / 视图)——已封装好,固定

QTableWidget中每一个项都使用一个QTableWidgetItem表示,tableWidget->item()->setText()则用来设置QTableWidgetItem的内容。

代码语言:javascript复制
CoordinateSetter::CoordinateSetter(QList<QPointF> *coords,
                                   QWidget *parent)
    : QDialog(parent)
{
    coordinates = coords;

    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);
        slot_addRow();
        tableWidget->item(row, 0)->setText(QString::number(point.x()));
        tableWidget->item(row, 1)->setText(QString::number(point.y()));
    }
    setWindowTitle(tr("Coordinate Setter"));
}

QTableWidget::insertRow()插入一个新行,使用QTableWidgetItem 创建两个Item,之后使用QTableWidget::setItem()将他们添加到列表中。

代码语言:javascript复制
void CoordinateSetter::slot_addRow()
{
    int row = tableWidget->rowCount();
    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);
}

最后,用户点击Ok按钮时,则会清空传递给这个对话框的人坐标,并且根据这个QTableWidget的所有Item创建一个新的坐标集。运行结果如下:

添加描述

Ⅱ Model(QDirModel / QFileSystemModel)结合View(ListView TableView)

Model不关心数据源,数据源可以是数据库或是其他形式,根据数据的特点选择合适的Model,最后再根据要展现给用户的形式选取一个View。按照上述的结构,也就达到了UI、业务逻辑和数据分离的效果了。

代码语言:javascript复制
//create Model
mp_fileSystemModel = new QFileSystemModel;
mp_fileSystemModel->setRootPath(QDir::currentPath());、

//tableView add the model
void Widget::on_pbn_tableView_clicked()
{
    ui->tableView->setModel(mp_fileSystemModel);
    ui->tableView->setRootIndex(mp_fileSystemModel->index(QDir::currentPath()));
}

ListView

添加描述

TableView

添加描述

QTreeView

代码语言:javascript复制
    //create and set Model
    mp_dirModel = new QDirModel;
    //可编辑
    mp_dirModel->setReadOnly(false);
    //初始排序属性
    mp_dirModel->setSorting(QDir::DirsFirst | QDir::IgnoreCase | QDir::Name);
    
    //create View and bind 
    mp_treeView = new QTreeView(this);
    mp_treeView->setModel(mp_dirModel);
    mp_treeView->setRootIndex(mp_dirModel->index(QDir::currentPath()));
    mp_treeView->header()->setStretchLastSection(true);
    mp_treeView->header()->setSortIndicator(0, Qt::AscendingOrder);
    mp_treeView->header()->setSortIndicatorShown(true);
    mp_treeView->header()->setSectionsClickable(true);
    //设置treeView的模型以及treeView的View属性。

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

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

添加描述

Ⅲ 自定义委托

像Qt里的事件一样,我们都是继承一个已经实现好了类,之后根据需要再实现自己的操作。

这里我想要实现的是双击单元格时,通过combox进行选择。QTableWidget没有这样功能,所以需要通过委托来实现,再所以就需要继承QItemDelegate,之后重新实现一些函数,和Qt 重新封装的OpenGL又有些类似。继承QItemDelegate,我们可以使用一些默认的功能,如果继承QAbstractItemDelegate,那就要从头开始实现更多的东西了。

1.自定义委托

代码语言:javascript复制
//create ComboBoxDelegate #public QItemDelegate
class ComboBoxDelegate : public QItemDelegate
{
public:
    ComboBoxDelegate();

public:
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};

//Use QComboBox to achieve corresponding function
QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    //Use class QComboBox, proxy use through QItemDelegate
    QComboBox *editor = new QComboBox(parent);
    if(index.column() == 2){
            editor->addItem(QString::fromLocal8Bit("17"));
            editor->addItem(QString::fromLocal8Bit("18"));
            editor->setCurrentIndex(0);
            return editor;
     } else {
            return QItemDelegate::createEditor(parent,option,index);
    }
}

2.使用自定义委托

代码语言:javascript复制
ui->tableWidget->setItemDelegateForColumn(2,new ComboBoxDelegate);
qt

0 人点赞