当前位置:   article > 正文

QTableView使用示例-Qt模型视图委托(MVD)(Model-View-Delegate)

QTableView使用示例-Qt模型视图委托(MVD)(Model-View-Delegate)

         模型视图委托(MVD)是Qt中特有的设计模式,类似MVC设计模式,将MVC设计模式中的Controller当做MVD中的Delegate,两者的概念基本相同。不同的是委托不是独立存在,而是包含在视图里面。 模型视图委托设计模式中,模型负责存储和管理数据;视图负责显示数据,其中界面的框架和基础信息是视图负责,具体数据的显示是委托负责;委托不仅仅负责数据的显示,还有一个重要的功能是负责数据的编辑,如在视图中双击就可以编辑数据。

一、MVD简介

        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 学生信息

  1. #ifndef STUDENT_H
  2. #define STUDENT_H
  3. #include <QtCore>
  4. struct Student
  5. {
  6. bool checked = false;
  7. quint16 id = 0;
  8. QString name;
  9. quint16 age = 0;
  10. QString gender = QObject::tr("男");
  11. quint16 achievement= 0;
  12. qint16 process = 50;
  13. };
  14. Q_DECLARE_METATYPE(Student);//将自定义类型声明为元类型(MetaType),以便在信号与槽机制中通过传递
  15. Q_DECLARE_METATYPE(Student*);
  16. #endif // STUDENT_H
1、下拉框委托类

comboboxdelegate.h

  1. #ifndef COMBOBOXDELEGATE_H
  2. #define COMBOBOXDELEGATE_H
  3. #include <QStyledItemDelegate>
  4. //下拉框委托类
  5. class ComboBoxDelegate : public QStyledItemDelegate
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit ComboBoxDelegate(QObject *parent = nullptr)
  10. : QStyledItemDelegate(parent)
  11. {}
  12. protected:
  13. QWidget *createEditor(QWidget *parent,
  14. const QStyleOptionViewItem &,
  15. const QModelIndex &) const Q_DECL_OVERRIDE;
  16. void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
  17. void setModelData(QWidget *editor,
  18. QAbstractItemModel *model,
  19. const QModelIndex &index) const Q_DECL_OVERRIDE;
  20. };
  21. #endif // COMBOBOXDELEGATE_H

 comboboxdelegate.cpp

  1. #include "comboboxdelegate.h"
  2. #include "student.h"
  3. #include <QtWidgets>
  4. QWidget *ComboBoxDelegate::createEditor(QWidget *parent,
  5. const QStyleOptionViewItem &,
  6. const QModelIndex &) const
  7. {
  8. QComboBox *comboBox = new QComboBox(parent);
  9. comboBox->addItems(QStringList() << tr("男") << tr("女"));
  10. return comboBox;
  11. }
  12. void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
  13. {
  14. QComboBox *comboBox = qobject_cast<QComboBox *>(editor);
  15. comboBox->setCurrentIndex(index.data(Qt::EditRole).toInt());
  16. }
  17. void ComboBoxDelegate::setModelData(QWidget *editor,
  18. QAbstractItemModel *model,
  19. const QModelIndex &index) const
  20. {
  21. QComboBox *comboBox = qobject_cast<QComboBox *>(editor);
  22. model->setData(index, comboBox->currentText(), Qt::EditRole);
  23. }
2、按钮委托类

buttondelegate.h

  1. #ifndef BUTTONDELEGATE_H
  2. #define BUTTONDELEGATE_H
  3. #include <QStyledItemDelegate>
  4. //按钮委托类
  5. class ButtonDelegate : public QStyledItemDelegate
  6. {
  7. public:
  8. ButtonDelegate(QObject* parent = nullptr);
  9. void paint(QPainter *painter,
  10. const QStyleOptionViewItem &option,
  11. const QModelIndex &index) const override;
  12. protected:
  13. bool editorEvent(QEvent *event, QAbstractItemModel *model,
  14. const QStyleOptionViewItem &option,
  15. const QModelIndex &index) override;
  16. private:
  17. QScopedPointer<QStyleOptionButton> m_buttonPtr;
  18. };
  19. #endif // BUTTONDELEGATE_H

 buttondelegate.cpp

  1. #include "buttondelegate.h"
  2. #include "student.h"
  3. #include <QPainter>
  4. #include <QApplication>
  5. #include <QMouseEvent>
  6. #include <QtWidgets>
  7. ButtonDelegate::ButtonDelegate(QObject *parent)
  8. : QStyledItemDelegate(parent)
  9. , m_buttonPtr(new QStyleOptionButton)
  10. {
  11. }
  12. void ButtonDelegate::paint(QPainter *painter,
  13. const QStyleOptionViewItem &option,
  14. const QModelIndex &index) const
  15. {
  16. int w = qMin(option.rect.width(), option.rect.height()) / 10.0;
  17. m_buttonPtr->rect = option.rect.adjusted(w, w, -w, -w);
  18. m_buttonPtr->text = index.model()->data(index).toString();
  19. m_buttonPtr->state |= QStyle::State_Enabled;
  20. painter->save();
  21. if (option.state & QStyle::State_Selected) {
  22. painter->fillRect(option.rect, option.palette.highlight());
  23. painter->setBrush(option.palette.highlightedText());
  24. }
  25. QPushButton button;
  26. qApp->style()->drawControl(QStyle::CE_PushButton, m_buttonPtr.data(), painter, &button);
  27. painter->restore();
  28. }
  29. bool ButtonDelegate::editorEvent(QEvent *event,
  30. QAbstractItemModel *model,
  31. const QStyleOptionViewItem &option,
  32. const QModelIndex &index)
  33. {
  34. int w = qMin(option.rect.width(), option.rect.height()) / 10.0;
  35. switch (event->type()) {
  36. case QEvent::MouseButtonPress:{
  37. QMouseEvent* mouseEvent =(QMouseEvent*)event;
  38. if (option.rect.adjusted(w, w, -w, -w).contains(mouseEvent->pos())) {
  39. m_buttonPtr->state |= QStyle::State_Sunken;
  40. }
  41. } break;
  42. case QEvent::MouseButtonRelease:{
  43. QMouseEvent* mouseEvent =(QMouseEvent*)event;
  44. if (option.rect.adjusted(w, w, -w, -w).contains(mouseEvent->pos())) {
  45. m_buttonPtr->state &= (~QStyle::State_Sunken);
  46. Student* stu = model->data(index, Qt::UserRole).value<Student*>();
  47. if(stu){
  48. QString details = tr("This Student id = %1, name = %2, age = %3, "
  49. "gender = %4, achievement = %5")
  50. .arg(stu->id)
  51. .arg(stu->name)
  52. .arg(stu->age)
  53. .arg(stu->gender)
  54. .arg(stu->achievement);
  55. QDialog dialog;
  56. QHBoxLayout *layout = new QHBoxLayout(&dialog);
  57. layout->addWidget(new QLabel(details, &dialog));
  58. dialog.exec();
  59. }
  60. }
  61. }
  62. break;
  63. default: break;
  64. }
  65. return true;
  66. }
3、 进度条委托类

progressbardelegate.h

  1. #ifndef PROGRESSBARDELEGATE_H
  2. #define PROGRESSBARDELEGATE_H
  3. #include <QStyledItemDelegate>
  4. //进度条委托类
  5. class ProgressBarDelegate : public QStyledItemDelegate
  6. {
  7. public:
  8. ProgressBarDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}
  9. void paint(QPainter *painter,
  10. const QStyleOptionViewItem &option,
  11. const QModelIndex &index) const;
  12. };
  13. #endif // PROGRESSBARDELEGATE_H

 progressbardelegate.cpp

  1. #include "progressbardelegate.h"
  2. #include <QPainter>
  3. #include <QtWidgets>
  4. void ProgressBarDelegate::paint(QPainter *painter,
  5. const QStyleOptionViewItem &option,
  6. const QModelIndex &index) const
  7. {
  8. QStyleOptionViewItem viewOption(option);
  9. initStyleOption(&viewOption, index);
  10. if (option.state.testFlag(QStyle::State_HasFocus))
  11. viewOption.state = viewOption.state ^ QStyle::State_HasFocus;
  12. QStyledItemDelegate::paint(painter, viewOption, index);
  13. int value = index.model()->data(index).toUInt();
  14. if (value < 0)
  15. value = 0;
  16. else if (value > 100)
  17. value = 100;
  18. int w = qMin(option.rect.width(), option.rect.height()) / 10.0;
  19. QStyleOptionProgressBar progressBarOption;
  20. progressBarOption.initFrom(option.widget);
  21. progressBarOption.rect = option.rect.adjusted(w, w, -w, -w);
  22. progressBarOption.minimum = 0;
  23. progressBarOption.maximum = 100;
  24. progressBarOption.textAlignment = Qt::AlignCenter;
  25. progressBarOption.textVisible = true;
  26. progressBarOption.progress = value;
  27. progressBarOption.text = tr("%1%").arg(progressBarOption.progress);
  28. painter->save();
  29. if (option.state & QStyle::State_Selected) {
  30. painter->fillRect(option.rect, option.palette.highlight());
  31. painter->setBrush(option.palette.highlightedText());
  32. }
  33. QProgressBar progressBar;
  34. qApp->style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter, &progressBar);
  35. painter->restore();
  36. }
 4、排序代理

sortfilterproxymodel.h

  1. #ifndef SORTFILTERPROXYMODEL_H
  2. #define SORTFILTERPROXYMODEL_H
  3. #include <QSortFilterProxyModel>
  4. //排序代理
  5. class SortFilterProxyModel : public QSortFilterProxyModel
  6. {
  7. public:
  8. SortFilterProxyModel(QObject *parent = nullptr)
  9. : QSortFilterProxyModel(parent) {}
  10. protected:
  11. bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; //重定义排序规则
  12. };
  13. #endif // SORTFILTERPROXYMODEL_H

      sortfilterproxymodel.cpp  

  1. #include "sortfilterproxymodel.h"
  2. #include <QDateTime>
  3. bool SortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
  4. {
  5. QVariant leftData = sourceModel()->data(left);
  6. QVariant rightData = sourceModel()->data(right);
  7. if (leftData.userType() == QMetaType::QDateTime) {
  8. return leftData.toDateTime() < rightData.toDateTime();
  9. } else if(leftData.userType() == QMetaType::Int) {
  10. return leftData.toInt() > rightData.toInt();
  11. }
  12. QString leftString = leftData.toString();
  13. QString rightString = rightData.toString();
  14. return QString::localeAwareCompare(leftString, rightString) < 0;
  15. }
5、学生数据表数据模型

 studenttablemodel.h

  1. #ifndef STUDENTTABLEMODEL_H
  2. #define STUDENTTABLEMODEL_H
  3. #include <QAbstractTableModel>
  4. struct Student;
  5. class StuedentTableModel : public QAbstractTableModel
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit StuedentTableModel(QObject *parent = nullptr): QAbstractTableModel(parent){}
  10. int rowCount(const QModelIndex & = QModelIndex()) const { return m_students.count(); }
  11. int columnCount(const QModelIndex & = QModelIndex()) const { return 7; }
  12. QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
  13. bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
  14. QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
  15. Qt::ItemFlags flags(const QModelIndex &index) const;
  16. void setStudents(const QList<Student *> &students);
  17. private:
  18. QList<Student *> m_students;
  19. };
  20. #endif // STUDENTTABLEMODEL_H

 studenttablemodel.cpp

  1. #include "studenttablemodel.h"
  2. #include "student.h"
  3. enum Property { ID, NAME, AGE, GENDER, ACHIEVEMENT, MENUBUTTON, PROCESS };
  4. QVariant StuedentTableModel::data(const QModelIndex &index, int role) const
  5. {
  6. if (!index.isValid())
  7. return false;
  8. int row = index.row();
  9. int col = index.column();
  10. Student *stu = m_students.at(row);
  11. switch (role) {
  12. case Qt::TextAlignmentRole: return Qt::AlignCenter;
  13. case Qt::CheckStateRole:
  14. switch (col) {
  15. case ID: return stu->checked;
  16. default: break;
  17. }
  18. break;
  19. case Qt::DisplayRole:
  20. case Qt::EditRole: { //双击为空需添加
  21. switch (col) {
  22. case ID: return stu->id;
  23. case NAME: return stu->name;
  24. case AGE: return stu->age;
  25. case GENDER: return stu->gender;
  26. case ACHIEVEMENT: return stu->achievement;
  27. case MENUBUTTON: return tr("Detail");
  28. case PROCESS: return stu->process;
  29. default: break;
  30. }
  31. case Qt::UserRole: {
  32. switch (col) {
  33. case MENUBUTTON: return QVariant::fromValue(stu);
  34. default: break;
  35. }
  36. }
  37. }
  38. default: break;
  39. }
  40. return QVariant();
  41. }
  42. bool StuedentTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
  43. {
  44. if (!index.isValid())
  45. return false;
  46. int row = index.row();
  47. int col = index.column();
  48. Student *stu = m_students[row];
  49. switch (role) {
  50. case Qt::CheckStateRole:
  51. switch (col) {
  52. case ID:
  53. stu->checked = !stu->checked;
  54. emit dataChanged(index, index);
  55. return true;
  56. default: break;
  57. }
  58. break;
  59. case Qt::EditRole:
  60. switch (col) {
  61. case ID: stu->id = value.toUInt(); break;
  62. case NAME: stu->name = value.toString(); break;
  63. case AGE: stu->age = value.toUInt(); break;
  64. case GENDER: stu->gender = value.toString(); break;
  65. case ACHIEVEMENT: stu->achievement = value.toUInt(); break;
  66. case PROCESS: stu->process = value.toUInt(); break;
  67. }
  68. emit dataChanged(index, index);
  69. return true;
  70. default: break;
  71. }
  72. return false;
  73. }
  74. QVariant StuedentTableModel::headerData(int section, Qt::Orientation orientation, int role) const
  75. {
  76. const QStringList names = {tr("ID"),
  77. tr("姓名"),
  78. tr("年龄"),
  79. tr("性别"),
  80. tr("成绩"),
  81. tr("详情"),
  82. tr("进度")};
  83. if (section < 0 || section >= names.size())
  84. return QVariant();
  85. if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
  86. return names.at(section);
  87. return QVariant();
  88. }
  89. Qt::ItemFlags StuedentTableModel::flags(const QModelIndex &index) const
  90. {
  91. Qt::ItemFlags flags = QAbstractTableModel::flags(index);
  92. flags |= Qt::ItemIsEditable;
  93. if (index.column() == ID)
  94. flags |= Qt::ItemIsUserCheckable;
  95. return flags;
  96. }
  97. void StuedentTableModel::setStudents(const QList<Student *> &students)
  98. {
  99. beginResetModel(); //重置数据之前调用,会自动触发 modelAboutToBeReset 信号
  100. m_students = students;
  101. endResetModel(); //重置数据完成后调用,会自动触发 modelReset 信号
  102. }
 6、学生数据表

stuedenttable.h

  1. #ifndef STUDENTTABLE_H
  2. #define STUDENTTABLE_H
  3. #include <QAbstractTableModel>
  4. #include <QStyledItemDelegate>
  5. #include <QTableView>
  6. struct Student;
  7. class StuedentTableModel;
  8. class StudentsTable : public QTableView
  9. {
  10. Q_OBJECT
  11. public:
  12. StudentsTable(QWidget *parent = nullptr);
  13. void setStudents(const QList<Student *> &students);
  14. protected:
  15. void contextMenuEvent(QContextMenuEvent *event) override;
  16. signals:
  17. void insertItem();
  18. void removeItem();
  19. private:
  20. void initMenu();
  21. StuedentTableModel *m_stuModel;
  22. QMenu *m_menu;
  23. };
  24. #endif // STUDENTTABLE_H

 stuedenttable.cpp

  1. #include "stuedenttable.h"
  2. #include "buttondelegate.h"
  3. #include "comboboxdelegate.h"
  4. #include "progressbardelegate.h"
  5. #include "sortfilterproxymodel.h"
  6. #include "studenttablemodel.h"
  7. #include <QtWidgets>
  8. StudentsTable::StudentsTable(QWidget *parent)
  9. : QTableView(parent)
  10. , m_stuModel(new StuedentTableModel(this))
  11. , m_menu(new QMenu(this))
  12. {
  13. setShowGrid(true);
  14. setWordWrap(false);
  15. setAlternatingRowColors(true);
  16. verticalHeader()->setVisible(false);
  17. verticalHeader()->setDefaultSectionSize(30);
  18. horizontalHeader()->setStretchLastSection(true);
  19. horizontalHeader()->setDefaultSectionSize(90);
  20. horizontalHeader()->setMinimumSectionSize(35);
  21. horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
  22. setSelectionBehavior(QAbstractItemView::SelectItems);
  23. setSelectionMode(QAbstractItemView::SingleSelection);
  24. setContextMenuPolicy(Qt::DefaultContextMenu);
  25. //设置委托
  26. setItemDelegateForColumn(3, new ComboBoxDelegate(this)); //下拉框
  27. setItemDelegateForColumn(5, new ButtonDelegate(this)); //按钮
  28. setItemDelegateForColumn(6, new ProgressBarDelegate(this)); //进度条
  29. //设置代理实现自定义排序
  30. setSortingEnabled(true);
  31. SortFilterProxyModel *sortModel = new SortFilterProxyModel(this);
  32. sortModel->setSourceModel(m_stuModel);
  33. setModel(sortModel);
  34. initMenu();
  35. }
  36. void StudentsTable::setStudents(const QList<Student *> &students)
  37. {
  38. m_stuModel->setStudents(students);
  39. }
  40. void StudentsTable::contextMenuEvent(QContextMenuEvent *event)
  41. {
  42. if (!currentIndex().isValid())
  43. return;
  44. m_menu->exec(mapToGlobal(event->pos()));
  45. }
  46. void StudentsTable::initMenu()
  47. {
  48. m_menu->addAction(tr("insert"), this, &StudentsTable::insertItem);
  49. m_menu->addAction(tr("remove"), this, &StudentsTable::removeItem);
  50. m_menu->addAction(tr("rename"), this, [this] { edit(currentIndex().siblingAtColumn(0)); });
  51. }

五、使用示例

以下是一个简单的示例代码,演示了如何在Qt中使用StudentsTable控件:

mainwindow.h
  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QMainWindow>
  4. struct Student;
  5. class StudentsTable;
  6. class MainWindow : public QMainWindow
  7. {
  8. Q_OBJECT
  9. public:
  10. MainWindow(QWidget *parent = nullptr);
  11. ~MainWindow();
  12. private slots:
  13. void onInsertItem();
  14. void onRemoveItem();
  15. private:
  16. void init();
  17. void setupUI();
  18. StudentsTable *m_table;
  19. QList<Student *> m_students;
  20. };
  21. #endif // MAINWINDOW_H
mainwindow.cpp
  1. #include "mainwindow.h"
  2. #include "student.h"
  3. #include "studenttablemodel.h"
  4. #include "stuedenttable.h"
  5. #include <QtWidgets>
  6. MainWindow::MainWindow(QWidget *parent)
  7. : QMainWindow(parent)
  8. {
  9. setupUI();
  10. init();
  11. resize(800, 300);
  12. }
  13. MainWindow::~MainWindow()
  14. {
  15. if (!m_students.isEmpty()) {
  16. qDeleteAll(m_students);
  17. m_students.clear();
  18. }
  19. }
  20. void MainWindow::onInsertItem()
  21. {
  22. int row = m_table->currentIndex().row();
  23. Student *stu = new Student;
  24. stu->id = m_students.size();
  25. if (row < 0 || row >= m_students.size())
  26. m_students.append(stu);
  27. else
  28. m_students.insert(row, stu);
  29. m_table->setStudents(m_students);
  30. }
  31. void MainWindow::onRemoveItem()
  32. {
  33. QModelIndex index = m_table->currentIndex();
  34. if (!index.isValid())
  35. return;
  36. int row = index.row();
  37. delete m_students.takeAt(row);
  38. m_table->setStudents(m_students);
  39. }
  40. void MainWindow::init()
  41. {
  42. m_students.append(new Student{true, 0, "Jason", 15, "男", 66, 10});
  43. m_students.append(new Student{false, 1, "Lily", 13, "女", 85, 20});
  44. m_students.append(new Student{true, 2, "Odin", 16, "女", 76, 30});
  45. m_students.append(new Student{false, 3, "Willion", 12, "男", 89, 40});
  46. m_students.append(new Student{true, 4, "Nieo", 14, "男", 77, 50});
  47. m_table->setStudents(m_students);
  48. m_table->selectRow(m_students.size() - 1);
  49. }
  50. void MainWindow::setupUI()
  51. {
  52. QPushButton *addBtn = new QPushButton(tr("增加"), this);
  53. QPushButton *removeBtn = new QPushButton(tr("删除"), this);
  54. m_table = new StudentsTable(this);
  55. QHBoxLayout *hLayout = new QHBoxLayout;
  56. hLayout->addStretch(1);
  57. hLayout->addWidget(addBtn);
  58. hLayout->addWidget(removeBtn);
  59. QVBoxLayout *layout = new QVBoxLayout;
  60. layout->addLayout(hLayout);
  61. layout->addWidget(m_table);
  62. QFrame *frame = new QFrame(this);
  63. frame->setLayout(layout);
  64. setCentralWidget(frame);
  65. connect(m_table, &StudentsTable::insertItem, this, &MainWindow::onInsertItem);
  66. connect(m_table, &StudentsTable::removeItem, this, &MainWindow::onRemoveItem);
  67. connect(addBtn, &QPushButton::clicked, this, &MainWindow::onInsertItem);
  68. connect(removeBtn, &QPushButton::clicked, this, &MainWindow::onRemoveItem);
  69. }

        到此本文基于QTableView的Model View Delegate使用方法就介绍到这了。

        总的来说,Qt的模型视图代理提供了一种灵活的方式来显示和编辑数据,在处理大量数据和复杂数据结构时特别有用。它将数据与界面分离,使得数据的表示和样式能够独立于数据本身进行修改。这种模式不仅提高了开发效率,还使得代码更加清晰和易于维护。

        谢谢您的阅读,希望本文能为您带来一些帮助和启发。如果您有任何问题或意见,请随时与我联系。祝您度过美好的一天!

六、源代码下载

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/961362
推荐阅读
相关标签
  

闽ICP备14008679号