赞
踩
模型视图委托(MVD)是Qt中特有的设计模式,类似MVC设计模式,将MVC设计模式中的Controller当做MVD中的Delegate,两者的概念基本相同。不同的是委托不是独立存在,而是包含在视图里面。 模型视图委托设计模式中,模型负责存储和管理数据;视图负责显示数据,其中界面的框架和基础信息是视图负责,具体数据的显示是委托负责;委托不仅仅负责数据的显示,还有一个重要的功能是负责数据的编辑,如在视图中双击就可以编辑数据。
QT当中model-view-delegate(模型-视图-代理),此结构实现数据和界面的分离。
Qt的模型-视图结构分为三部分:模型(mode)-视图(view)-代理(Delegate) ,其中模型与数据源通信,并为其它部件提供接口;视图从模型中引用数据条的模型索引(Modellndex),在视图当中,代理负责绘制数据条目,比如编辑条目,代理和模型进行直接通信。
1、模型 (model): 实现自定义模型可以通过QAbstractltemModel类继承,也可以通过QAbstractListModel和QAbstractTableModel类继承实现列表模型或者表格模型
2、视图(view): 实现自定义的视图View,可以继承子QAbstractltemView类,对所需要的虚拟函数进行重定义。
3、代理 (delegate) :在表格当中嵌入各种不同的控件,通过表格中控件对编辑的内容进行操作。表格插入控件方式,控件始终显示。
本实例基于QT的委托代理机制实现的Qt模型视图代理(Model-View-Delegate)使用示例。以QTableView为基础,实现表头排序,列表复选框,插入按钮、下拉框、进度条。在Qt中,QTableView是一个用于显示表格数据的控件,可以使用Qt的模型视图代理(Model-View-Delegate)设计模式来定制QTableView的外观和行为。
student.h 学生信息
- #ifndef STUDENT_H
- #define STUDENT_H
-
- #include <QtCore>
-
- struct Student
- {
- bool checked = false;
- quint16 id = 0;
- QString name;
- quint16 age = 0;
- QString gender = QObject::tr("男");
- quint16 achievement= 0;
- qint16 process = 50;
- };
-
- Q_DECLARE_METATYPE(Student);//将自定义类型声明为元类型(MetaType),以便在信号与槽机制中通过传递
- Q_DECLARE_METATYPE(Student*);
-
- #endif // STUDENT_H

comboboxdelegate.h
- #ifndef COMBOBOXDELEGATE_H
- #define COMBOBOXDELEGATE_H
-
- #include <QStyledItemDelegate>
-
- //下拉框委托类
- class ComboBoxDelegate : public QStyledItemDelegate
- {
- Q_OBJECT
- public:
- explicit ComboBoxDelegate(QObject *parent = nullptr)
- : QStyledItemDelegate(parent)
- {}
-
- protected:
- QWidget *createEditor(QWidget *parent,
- const QStyleOptionViewItem &,
- const QModelIndex &) const Q_DECL_OVERRIDE;
- void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
- void setModelData(QWidget *editor,
- QAbstractItemModel *model,
- const QModelIndex &index) const Q_DECL_OVERRIDE;
- };
-
- #endif // COMBOBOXDELEGATE_H

comboboxdelegate.cpp
- #include "comboboxdelegate.h"
- #include "student.h"
-
- #include <QtWidgets>
-
- QWidget *ComboBoxDelegate::createEditor(QWidget *parent,
- const QStyleOptionViewItem &,
- const QModelIndex &) const
- {
- QComboBox *comboBox = new QComboBox(parent);
- comboBox->addItems(QStringList() << tr("男") << tr("女"));
- return comboBox;
- }
-
- void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
- {
- QComboBox *comboBox = qobject_cast<QComboBox *>(editor);
- comboBox->setCurrentIndex(index.data(Qt::EditRole).toInt());
- }
-
- void ComboBoxDelegate::setModelData(QWidget *editor,
- QAbstractItemModel *model,
- const QModelIndex &index) const
- {
- QComboBox *comboBox = qobject_cast<QComboBox *>(editor);
- model->setData(index, comboBox->currentText(), Qt::EditRole);
- }

buttondelegate.h
- #ifndef BUTTONDELEGATE_H
- #define BUTTONDELEGATE_H
-
- #include <QStyledItemDelegate>
-
- //按钮委托类
- class ButtonDelegate : public QStyledItemDelegate
- {
- public:
- ButtonDelegate(QObject* parent = nullptr);
-
- void paint(QPainter *painter,
- const QStyleOptionViewItem &option,
- const QModelIndex &index) const override;
-
- protected:
- bool editorEvent(QEvent *event, QAbstractItemModel *model,
- const QStyleOptionViewItem &option,
- const QModelIndex &index) override;
-
- private:
- QScopedPointer<QStyleOptionButton> m_buttonPtr;
- };
-
- #endif // BUTTONDELEGATE_H

buttondelegate.cpp
- #include "buttondelegate.h"
- #include "student.h"
-
- #include <QPainter>
- #include <QApplication>
- #include <QMouseEvent>
- #include <QtWidgets>
-
- ButtonDelegate::ButtonDelegate(QObject *parent)
- : QStyledItemDelegate(parent)
- , m_buttonPtr(new QStyleOptionButton)
- {
-
- }
-
- void ButtonDelegate::paint(QPainter *painter,
- const QStyleOptionViewItem &option,
- const QModelIndex &index) const
- {
- int w = qMin(option.rect.width(), option.rect.height()) / 10.0;
- m_buttonPtr->rect = option.rect.adjusted(w, w, -w, -w);
- m_buttonPtr->text = index.model()->data(index).toString();
- m_buttonPtr->state |= QStyle::State_Enabled;
-
- painter->save();
-
- if (option.state & QStyle::State_Selected) {
- painter->fillRect(option.rect, option.palette.highlight());
- painter->setBrush(option.palette.highlightedText());
- }
-
- QPushButton button;
- qApp->style()->drawControl(QStyle::CE_PushButton, m_buttonPtr.data(), painter, &button);
-
- painter->restore();
- }
-
- bool ButtonDelegate::editorEvent(QEvent *event,
- QAbstractItemModel *model,
- const QStyleOptionViewItem &option,
- const QModelIndex &index)
- {
- int w = qMin(option.rect.width(), option.rect.height()) / 10.0;
-
- switch (event->type()) {
- case QEvent::MouseButtonPress:{
- QMouseEvent* mouseEvent =(QMouseEvent*)event;
- if (option.rect.adjusted(w, w, -w, -w).contains(mouseEvent->pos())) {
- m_buttonPtr->state |= QStyle::State_Sunken;
- }
- } break;
- case QEvent::MouseButtonRelease:{
- QMouseEvent* mouseEvent =(QMouseEvent*)event;
- if (option.rect.adjusted(w, w, -w, -w).contains(mouseEvent->pos())) {
- m_buttonPtr->state &= (~QStyle::State_Sunken);
-
- Student* stu = model->data(index, Qt::UserRole).value<Student*>();
- if(stu){
- QString details = tr("This Student id = %1, name = %2, age = %3, "
- "gender = %4, achievement = %5")
- .arg(stu->id)
- .arg(stu->name)
- .arg(stu->age)
- .arg(stu->gender)
- .arg(stu->achievement);
- QDialog dialog;
- QHBoxLayout *layout = new QHBoxLayout(&dialog);
- layout->addWidget(new QLabel(details, &dialog));
- dialog.exec();
- }
- }
- }
- break;
- default: break;
- }
- return true;
- }

progressbardelegate.h
- #ifndef PROGRESSBARDELEGATE_H
- #define PROGRESSBARDELEGATE_H
-
- #include <QStyledItemDelegate>
-
- //进度条委托类
- class ProgressBarDelegate : public QStyledItemDelegate
- {
- public:
- ProgressBarDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}
-
- void paint(QPainter *painter,
- const QStyleOptionViewItem &option,
- const QModelIndex &index) const;
- };
-
- #endif // PROGRESSBARDELEGATE_H

progressbardelegate.cpp
- #include "progressbardelegate.h"
-
- #include <QPainter>
- #include <QtWidgets>
-
- void ProgressBarDelegate::paint(QPainter *painter,
- const QStyleOptionViewItem &option,
- const QModelIndex &index) const
- {
- QStyleOptionViewItem viewOption(option);
- initStyleOption(&viewOption, index);
- if (option.state.testFlag(QStyle::State_HasFocus))
- viewOption.state = viewOption.state ^ QStyle::State_HasFocus;
-
- QStyledItemDelegate::paint(painter, viewOption, index);
-
- int value = index.model()->data(index).toUInt();
- if (value < 0)
- value = 0;
- else if (value > 100)
- value = 100;
- int w = qMin(option.rect.width(), option.rect.height()) / 10.0;
- QStyleOptionProgressBar progressBarOption;
- progressBarOption.initFrom(option.widget);
- progressBarOption.rect = option.rect.adjusted(w, w, -w, -w);
- progressBarOption.minimum = 0;
- progressBarOption.maximum = 100;
- progressBarOption.textAlignment = Qt::AlignCenter;
- progressBarOption.textVisible = true;
- progressBarOption.progress = value;
- progressBarOption.text = tr("%1%").arg(progressBarOption.progress);
-
- painter->save();
- if (option.state & QStyle::State_Selected) {
- painter->fillRect(option.rect, option.palette.highlight());
- painter->setBrush(option.palette.highlightedText());
- }
-
- QProgressBar progressBar;
- qApp->style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter, &progressBar);
-
- painter->restore();
- }

sortfilterproxymodel.h
- #ifndef SORTFILTERPROXYMODEL_H
- #define SORTFILTERPROXYMODEL_H
-
- #include <QSortFilterProxyModel>
-
- //排序代理
- class SortFilterProxyModel : public QSortFilterProxyModel
- {
- public:
- SortFilterProxyModel(QObject *parent = nullptr)
- : QSortFilterProxyModel(parent) {}
-
- protected:
- bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; //重定义排序规则
- };
-
- #endif // SORTFILTERPROXYMODEL_H

sortfilterproxymodel.cpp
- #include "sortfilterproxymodel.h"
-
- #include <QDateTime>
-
- bool SortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
- {
- QVariant leftData = sourceModel()->data(left);
- QVariant rightData = sourceModel()->data(right);
-
- if (leftData.userType() == QMetaType::QDateTime) {
- return leftData.toDateTime() < rightData.toDateTime();
- } else if(leftData.userType() == QMetaType::Int) {
- return leftData.toInt() > rightData.toInt();
- }
- QString leftString = leftData.toString();
- QString rightString = rightData.toString();
- return QString::localeAwareCompare(leftString, rightString) < 0;
- }

studenttablemodel.h
- #ifndef STUDENTTABLEMODEL_H
- #define STUDENTTABLEMODEL_H
-
- #include <QAbstractTableModel>
-
- struct Student;
- class StuedentTableModel : public QAbstractTableModel
- {
- Q_OBJECT
- public:
- explicit StuedentTableModel(QObject *parent = nullptr): QAbstractTableModel(parent){}
-
- int rowCount(const QModelIndex & = QModelIndex()) const { return m_students.count(); }
- int columnCount(const QModelIndex & = QModelIndex()) const { return 7; }
-
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
- QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
- Qt::ItemFlags flags(const QModelIndex &index) const;
- void setStudents(const QList<Student *> &students);
-
- private:
- QList<Student *> m_students;
- };
-
- #endif // STUDENTTABLEMODEL_H

studenttablemodel.cpp
- #include "studenttablemodel.h"
- #include "student.h"
-
- enum Property { ID, NAME, AGE, GENDER, ACHIEVEMENT, MENUBUTTON, PROCESS };
-
- QVariant StuedentTableModel::data(const QModelIndex &index, int role) const
- {
- if (!index.isValid())
- return false;
-
- int row = index.row();
- int col = index.column();
-
- Student *stu = m_students.at(row);
- switch (role) {
- case Qt::TextAlignmentRole: return Qt::AlignCenter;
- case Qt::CheckStateRole:
- switch (col) {
- case ID: return stu->checked;
- default: break;
- }
- break;
- case Qt::DisplayRole:
- case Qt::EditRole: { //双击为空需添加
- switch (col) {
- case ID: return stu->id;
- case NAME: return stu->name;
- case AGE: return stu->age;
- case GENDER: return stu->gender;
- case ACHIEVEMENT: return stu->achievement;
- case MENUBUTTON: return tr("Detail");
- case PROCESS: return stu->process;
- default: break;
- }
- case Qt::UserRole: {
- switch (col) {
- case MENUBUTTON: return QVariant::fromValue(stu);
- default: break;
- }
- }
- }
- default: break;
- }
- return QVariant();
- }
-
- bool StuedentTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
- {
- if (!index.isValid())
- return false;
-
- int row = index.row();
- int col = index.column();
-
- Student *stu = m_students[row];
- switch (role) {
- case Qt::CheckStateRole:
- switch (col) {
- case ID:
- stu->checked = !stu->checked;
- emit dataChanged(index, index);
- return true;
- default: break;
- }
- break;
- case Qt::EditRole:
- switch (col) {
- case ID: stu->id = value.toUInt(); break;
- case NAME: stu->name = value.toString(); break;
- case AGE: stu->age = value.toUInt(); break;
- case GENDER: stu->gender = value.toString(); break;
- case ACHIEVEMENT: stu->achievement = value.toUInt(); break;
- case PROCESS: stu->process = value.toUInt(); break;
- }
- emit dataChanged(index, index);
- return true;
- default: break;
- }
- return false;
- }
-
- QVariant StuedentTableModel::headerData(int section, Qt::Orientation orientation, int role) const
- {
- const QStringList names = {tr("ID"),
- tr("姓名"),
- tr("年龄"),
- tr("性别"),
- tr("成绩"),
- tr("详情"),
- tr("进度")};
- if (section < 0 || section >= names.size())
- return QVariant();
-
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
- return names.at(section);
- return QVariant();
- }
-
- Qt::ItemFlags StuedentTableModel::flags(const QModelIndex &index) const
- {
- Qt::ItemFlags flags = QAbstractTableModel::flags(index);
- flags |= Qt::ItemIsEditable;
- if (index.column() == ID)
- flags |= Qt::ItemIsUserCheckable;
- return flags;
- }
-
- void StuedentTableModel::setStudents(const QList<Student *> &students)
- {
- beginResetModel(); //重置数据之前调用,会自动触发 modelAboutToBeReset 信号
- m_students = students;
- endResetModel(); //重置数据完成后调用,会自动触发 modelReset 信号
- }

stuedenttable.h
- #ifndef STUDENTTABLE_H
- #define STUDENTTABLE_H
-
- #include <QAbstractTableModel>
- #include <QStyledItemDelegate>
- #include <QTableView>
-
- struct Student;
- class StuedentTableModel;
- class StudentsTable : public QTableView
- {
- Q_OBJECT
- public:
- StudentsTable(QWidget *parent = nullptr);
-
- void setStudents(const QList<Student *> &students);
-
- protected:
- void contextMenuEvent(QContextMenuEvent *event) override;
-
- signals:
- void insertItem();
- void removeItem();
-
- private:
- void initMenu();
-
- StuedentTableModel *m_stuModel;
- QMenu *m_menu;
- };
-
- #endif // STUDENTTABLE_H

stuedenttable.cpp
- #include "stuedenttable.h"
- #include "buttondelegate.h"
- #include "comboboxdelegate.h"
- #include "progressbardelegate.h"
- #include "sortfilterproxymodel.h"
- #include "studenttablemodel.h"
-
- #include <QtWidgets>
-
- StudentsTable::StudentsTable(QWidget *parent)
- : QTableView(parent)
- , m_stuModel(new StuedentTableModel(this))
- , m_menu(new QMenu(this))
- {
- setShowGrid(true);
- setWordWrap(false);
- setAlternatingRowColors(true);
- verticalHeader()->setVisible(false);
- verticalHeader()->setDefaultSectionSize(30);
- horizontalHeader()->setStretchLastSection(true);
- horizontalHeader()->setDefaultSectionSize(90);
- horizontalHeader()->setMinimumSectionSize(35);
- horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
- setSelectionBehavior(QAbstractItemView::SelectItems);
- setSelectionMode(QAbstractItemView::SingleSelection);
- setContextMenuPolicy(Qt::DefaultContextMenu);
-
- //设置委托
- setItemDelegateForColumn(3, new ComboBoxDelegate(this)); //下拉框
- setItemDelegateForColumn(5, new ButtonDelegate(this)); //按钮
- setItemDelegateForColumn(6, new ProgressBarDelegate(this)); //进度条
-
- //设置代理实现自定义排序
- setSortingEnabled(true);
- SortFilterProxyModel *sortModel = new SortFilterProxyModel(this);
- sortModel->setSourceModel(m_stuModel);
- setModel(sortModel);
-
- initMenu();
- }
-
- void StudentsTable::setStudents(const QList<Student *> &students)
- {
- m_stuModel->setStudents(students);
- }
-
- void StudentsTable::contextMenuEvent(QContextMenuEvent *event)
- {
- if (!currentIndex().isValid())
- return;
- m_menu->exec(mapToGlobal(event->pos()));
- }
-
- void StudentsTable::initMenu()
- {
- m_menu->addAction(tr("insert"), this, &StudentsTable::insertItem);
- m_menu->addAction(tr("remove"), this, &StudentsTable::removeItem);
- m_menu->addAction(tr("rename"), this, [this] { edit(currentIndex().siblingAtColumn(0)); });
- }

以下是一个简单的示例代码,演示了如何在Qt中使用StudentsTable控件:
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
-
- #include <QMainWindow>
-
- struct Student;
- class StudentsTable;
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
-
- public:
- MainWindow(QWidget *parent = nullptr);
- ~MainWindow();
-
- private slots:
- void onInsertItem();
- void onRemoveItem();
-
- private:
- void init();
- void setupUI();
- StudentsTable *m_table;
- QList<Student *> m_students;
- };
- #endif // MAINWINDOW_H

- #include "mainwindow.h"
- #include "student.h"
- #include "studenttablemodel.h"
- #include "stuedenttable.h"
-
- #include <QtWidgets>
-
- MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
- {
- setupUI();
- init();
- resize(800, 300);
- }
-
- MainWindow::~MainWindow()
- {
- if (!m_students.isEmpty()) {
- qDeleteAll(m_students);
- m_students.clear();
- }
- }
-
- void MainWindow::onInsertItem()
- {
- int row = m_table->currentIndex().row();
- Student *stu = new Student;
- stu->id = m_students.size();
-
- if (row < 0 || row >= m_students.size())
- m_students.append(stu);
- else
- m_students.insert(row, stu);
-
- m_table->setStudents(m_students);
- }
-
- void MainWindow::onRemoveItem()
- {
- QModelIndex index = m_table->currentIndex();
- if (!index.isValid())
- return;
- int row = index.row();
- delete m_students.takeAt(row);
- m_table->setStudents(m_students);
- }
-
- void MainWindow::init()
- {
- m_students.append(new Student{true, 0, "Jason", 15, "男", 66, 10});
- m_students.append(new Student{false, 1, "Lily", 13, "女", 85, 20});
- m_students.append(new Student{true, 2, "Odin", 16, "女", 76, 30});
- m_students.append(new Student{false, 3, "Willion", 12, "男", 89, 40});
- m_students.append(new Student{true, 4, "Nieo", 14, "男", 77, 50});
- m_table->setStudents(m_students);
- m_table->selectRow(m_students.size() - 1);
- }
-
- void MainWindow::setupUI()
- {
- QPushButton *addBtn = new QPushButton(tr("增加"), this);
- QPushButton *removeBtn = new QPushButton(tr("删除"), this);
- m_table = new StudentsTable(this);
-
- QHBoxLayout *hLayout = new QHBoxLayout;
- hLayout->addStretch(1);
- hLayout->addWidget(addBtn);
- hLayout->addWidget(removeBtn);
- QVBoxLayout *layout = new QVBoxLayout;
- layout->addLayout(hLayout);
- layout->addWidget(m_table);
- QFrame *frame = new QFrame(this);
- frame->setLayout(layout);
- setCentralWidget(frame);
-
- connect(m_table, &StudentsTable::insertItem, this, &MainWindow::onInsertItem);
- connect(m_table, &StudentsTable::removeItem, this, &MainWindow::onRemoveItem);
- connect(addBtn, &QPushButton::clicked, this, &MainWindow::onInsertItem);
- connect(removeBtn, &QPushButton::clicked, this, &MainWindow::onRemoveItem);
- }

到此本文基于QTableView的Model View Delegate使用方法就介绍到这了。
总的来说,Qt的模型视图代理提供了一种灵活的方式来显示和编辑数据,在处理大量数据和复杂数据结构时特别有用。它将数据与界面分离,使得数据的表示和样式能够独立于数据本身进行修改。这种模式不仅提高了开发效率,还使得代码更加清晰和易于维护。
谢谢您的阅读,希望本文能为您带来一些帮助和启发。如果您有任何问题或意见,请随时与我联系。祝您度过美好的一天!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。