Qt 没有整体实现“标准”MVC 模式——您需要从头开始重新实现这样的框架。 Qt 提供了一个模型视图框架,它为 MVVM 提供了足够的功能,但这不是 MVC-by-the-book。
在 Qt 的实现中,视图和控制器是混合在一起的。视图是向用户显示模型的内容和用户在委托的帮助下用来与模型交互的内容。
因此,一个单独的控制器的问题基本上没有实际意义,因为没有一个。在 Qt 中,具体视图是您通常不会从中派生的独立小部件。相反,您将视图集成(具有)到一个更大的小部件中,该小部件包含其他控件。
Qt 提供了一些标准视图(列表视图、表格视图和树视图)。还有QDataWidgetMapper 允许您将一个索引从模型映射到任何小部件的用户属性。还有多种型号可供选择。抽象模型是您自己实现的基础。然后是QStandardItemModel,它提供了灵活的数据树/表存储。最后,QSqlQueryModel 和 QSqlRelationalTableModel 将 SQL 数据库公开为模型。
在下面的示例中,比较器以视图模型的形式实现 - 一种使用比较结果修改底层图像提供模型的代理。对于要显示的图像,需要在Qt::DecorationRole 下提供它们。主窗口只是 视图(is-a),不需要子类化。
#include <QApplication>
#include <QTableView>
#include <QIdentityProxyModel>
#include <QStandardItemModel>
#include <QPainter>
/** Adds image comparison results to a table/tree with pairs of images in each row.
*
* This is a viewmodel that expects a table or tree with row containing pairs of images in the
* first two columns. Comparison results are visualized as the added last column.
* A null result is provided for rows that don't have two images as their first two columns.
*/
class Comparator : public QIdentityProxyModel {
Q_OBJECT
bool isLastColumn(const QModelIndex & proxyIndex) const {
return proxyIndex.column() == columnCount(proxyIndex.parent()) - 1;
}
QModelIndex indexInColumn(int column, const QModelIndex & proxyIndex) const {
return index(proxyIndex.row(), column, proxyIndex.parent());
}
/** Compares the two images, returning their difference..
* Both images are expanded to the larger of their sizes. Missing data is filled with
* transparent pixels. The images can be in any format. The difference is in ARGB32. */
QImage compare(const QImage & left, const QImage & right) const {
QImage delta(left.size().expandedTo(right.size()), QImage::Format_ARGB32);
delta.fill(Qt::transparent);
QPainter p(&delta);
p.setRenderHint(QPainter::Antialiasing);
p.drawImage(0, 0, left);
p.setCompositionMode(QPainter::CompositionMode_Difference);
p.drawImage(0, 0, right);
return delta;
}
public:
Comparator(QObject * parent = 0) : QIdentityProxyModel(parent) {}
QModelIndex index(int row, int column, const QModelIndex &parent) const Q_DECL_OVERRIDE {
if (column != columnCount(parent) - 1)
return QIdentityProxyModel::index(row, column, parent);
return createIndex(row, column, parent.internalPointer());
}
int columnCount(const QModelIndex &parent) const Q_DECL_OVERRIDE {
return sourceModel()->columnCount(mapToSource(parent)) + 1;
}
QVariant data(const QModelIndex &proxyIndex, int role) const Q_DECL_OVERRIDE {
if (isLastColumn(proxyIndex)) {
QVariant left = data(indexInColumn(0, proxyIndex), role);
QVariant right = data(indexInColumn(1, proxyIndex), role);
if (!left.canConvert<QImage>() || !right.canConvert<QImage>()) return QVariant();
return QVariant::fromValue(compare(left.value<QImage>(), right.value<QImage>()));
}
return QAbstractProxyModel::data(proxyIndex, role);
}
};
QImage sector(qreal diameter, qreal size, qreal start, qreal end, const QColor & color)
{
QImage image(size, size, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setBrush(color);
p.drawPie(QRectF(size-diameter, size-diameter, diameter, diameter),
qRound(start*16), qRound((end-start)*16));
return image;
}
QStandardItem * imageItem(const QImage & image) {
QScopedPointer<QStandardItem> item(new QStandardItem);
item->setEditable(false);
item->setSelectable(false);
item->setData(QVariant::fromValue(image), Qt::DecorationRole);
item->setSizeHint(image.size());
return item.take();
}
typedef QList<QStandardItem*> QStandardItemList;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStandardItemModel images;
Comparator comparator;
QTableView view;
comparator.setSourceModel(&images);
view.setModel(&comparator);
images.appendRow(QStandardItemList()
<< imageItem(sector(150, 160, 30, 100, Qt::red))
<< imageItem(sector(150, 160, 60, 120, Qt::blue)));
images.appendRow(QStandardItemList()
<< imageItem(sector(40, 45, 0, 180, Qt::darkCyan))
<< imageItem(sector(40, 45, 180, 360, Qt::cyan)));
view.resizeColumnsToContents();
view.resizeRowsToContents();
view.adjustSize();
view.show();
return a.exec();
}
#include "main.moc"