什么是MVC架構:
MVC模式(Model–view–controller)是軟件工程中的一種軟件架構模式,把軟件系統分為三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。
MVC模式最早由Trygve Reenskaug在1978年提出[1],是施樂帕羅奧多研究中心(Xerox PARC)在20世紀80年代為程序語言Smalltalk發明的一種軟件架構。MVC模式的目的是實現一種動態的程序設計,使后續對程序的修改和擴展簡化,并且使程序某一部分的重復利用成為可能。除此之外,此模式透過對復雜度的簡化,使程序結構更加直觀。軟件系統透過對自身基本部分分離的同時也賦予了各個基本部分應有的功能。專業人員可以依據自身的專長分組:
模型(Model) - 程序員編寫程序應有的功能(實現算法等等)、數據庫專家進行數據管理和數據庫設計(可以實現具體的功能)。
視圖(View) - 界面設計人員進行圖形界面設計。
控制器(Controller)- 負責轉發請求,對請求進行處理。
Qt的MVD架構又是什么?
模型(Model) - 程序員編寫程序應有的功能(實現算法等等)、數據庫專家進行數據管理和數據庫設計(可以實現具體的功能)。
視圖(View) - 界面設計人員進行圖形界面設計。
代理(Delegate)- 負責轉發請求,對請求進行處理
UI樣式實現:
其實你就只要去實現下面的代碼,你就能完全使用MVD架構,隨心所欲
class TableModel : public QAbstractTableModel
{Q_OBJECT
public:enum Columns { Element = 0, Value, Count = Value + 1 };explicit TableModel(QObject *parent = nullptr);Qt::ItemFlags flags(const QModelIndex &index) const;int rowCount(const QModelIndex &parent = QModelIndex()) const;int columnCount(const QModelIndex &parent = QModelIndex()) const;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole);void addElement(const QString &element, int value);private:};
class TableDelegate : public QStyledItemDelegate
{Q_OBJECT
public:explicit TableDelegate(QObject *parent = nullptr);QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;void setEditorData(QWidget *editor, const QModelIndex &index) const;void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};//--------------------cpp---------------------
#include "tablemodel.h"TableModel::TableModel(QObject *parent) : QAbstractTableModel(parent)
{
}Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
{return QAbstractTableModel::flags(index);
}int TableModel::rowCount(const QModelIndex &parent) const
{return 2;
}int TableModel::columnCount(const QModelIndex &parent) const
{return Count;
}QVariant TableModel::data(const QModelIndex &index, int role) const
{return QVariant();
}QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{return QAbstractTableModel::headerData(section, orientation, role);
}bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{if (!index.isValid() || index.row() < 0 || index.row() >= mElements.count()){return false;}return false;
}
void TableModel::addElement(const QString &element, int value)
{
}TableDelegate::TableDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}QWidget *TableDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const
{return QStyledItemDelegate::createEditor(parent, option, index);
}void TableDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{QStyledItemDelegate::setEditorData(editor, index);
}void TableDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{QStyledItemDelegate::setModelData(editor, model, index);
}void TableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{QStyledItemDelegate::paint(painter, option, index);
}void TableDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,const QModelIndex &index) const
{Q_UNUSED(index)editor->setGeometry(option.rect);
}
Model
class QAbstractItemModel {virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const = 0;virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0;virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) = 0;virtual int rowCount(const QModelIndex &parent = QModelIndex()) const = 0;virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0;virtual Qt::ItemFlags flags(const QModelIndex &index) const = 0;
};
實現上面的代碼,你就能使用MVD架構,理解Qt的框架,
View 與 Model 的綁定流程
- 存儲指針:View 內部保存QAbstractItemModel*類型的指針指向用戶的 Model 對象
- 信號連接:View 自動連接 Model 的信號(如?dataChanged())到自身的槽(如?update()),實現數據變化時的視圖更新
- 數據請求:當視圖需要渲染時,會通過 Model 指針調用虛函數(如data(),?rowCount()))
// 偽代碼:QAbstractItemView 的渲染流程
void QAbstractItemView::paintEvent() {for (int row = 0; row < model->rowCount(); ++row) { // 多態調用 model->rowCount()QVariant data = model->data(index(row)); // 多態調用 model->data()delegate->paint(painter, data); // 多態調用 delegate->paint()}
}
Delegate 的動態調用機制
- 渲染階段:View 在繪制每個數據項時,調用delegate->paint(painter, option, index)
- 編輯階段:當用戶雙擊單元格時,調用delegate->createEditor()創建編輯器
- 數據回寫:編輯器關閉時,調用delegate->setModelData()將數據寫回 Model
// 偽代碼:委托的編輯器創建過程
void QAbstractItemView::edit(const QModelIndex &index) {QWidget* editor = delegate->createEditor(parent, option, index); // 多態調用connect(editor, &QWidget::destroyed, [this, index]() {delegate->setModelData(editor, model, index); // 多態調用});
}