定义
早期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);