Qt 模型-视图-控制器
首先,我想说我已经阅读了有关 Qt 和 MVC 的所有其他问题,但是我找不到我要找的内容.因此,除非您在旧问题中找到了实际回答我的问题的内容,否则请不要将它们与我联系起来.我也在 qt.dgia.com 和 qt.project.com 中搜索过,但还是没有运气.
First of all I would like to say that I' ve already read all the other questions about Qt and MVC, but I couldn't find what I'm looking for. So please, unless you find something in the old questions that actually answer my question, don't link them to me. I also searched in qt.digia.com and qt.project.com but again, no luck.
现在来解决我的问题.我必须实现一个简单的图像比较器,它可以并排显示图像,以便可以比较它们.我必须使用 MVC 来做到这一点.我的问题是我从未使用过 Qt,我对如何将它与 MVC 一起使用感到有些困惑.
So now to my problem. I have to implement a simple image comparator that shows to image side by side so that they can be compared. I have to use MVC to do this. My problem is that I've never used Qt and I'm a bit confused on how to use it with MVC.
特别是,我想知道 MainWindow 应该从哪个子类化.是视图还是模型,还是两者兼而有之?这是我的想法. MainWindow 是我的类图中的一个视图,但我不确定,因为它也有模型的元素,因为它实际上存储了数据信息.你有什么建议?那么如何设计其他类呢?谢谢.
In particular, I am wondering what MainWindow should be subclassed from. It is the View or the Model, or both? This is what I thought. MainWindow is a View in my class diagram, but I'm not sure of that, because it also has elements of a model, since it actually stores data information. What do you suggest? Then how would design the other classes? Thank you.
推荐答案
Qt 并未整体实现标准"MVC 模式 - 您需要从头开始重新实现这样的框架.Qt 提供了一个模型视图框架,为 MVVM 提供了足够的功能,但这不是 MVC 的书本.
Qt does not implement the "standard" MVC pattern as a whole - you would need to reimplement such a framework from scratch. Qt offers a model-view framework that offers enough functionality for MVVM, but that's not MVC-by-the-book.
在 Qt 的实现中,视图和控制器混合在一起.视图是向用户显示模型以及用户在代理的帮助下与模型交互的内容.
In Qt's implementation, the view and the controller are mingled together. The view is what shows the model to the user and what the user uses to interact with the model, with help of delegates.
因此,单独控制器的问题基本上没有实际意义,因为没有.在 Qt 中,具体视图是您通常不会从中派生的独立小部件.相反,您将视图集成(has-a)到一个包含其他控件的更大的小部件中.
Thus, the question of a separate controller is essentially moot, as there isn't one. In Qt, a concrete view is a stand-alone widget that you normally don't derive from. Instead, you integrate (has-a) the view into a larger widget that holds other controls.
Qt 提供了一些标准视图(列表视图、表格视图和树视图).还有 QDataWidgetMapper
可以让您将模型中的一个索引映射到任何小部件的用户属性.还有多种型号可供选择.抽象模型是您自己实现的基础.然后是 QStandardItemModel
,它提供了灵活的数据树/表存储.最后,QSqlQueryModel
和 QSqlRelationalTableModel
将 SQL 数据库作为模型公开.
Qt provides some standard views (a list view, a table view and a tree view). There's also the QDataWidgetMapper
that lets you map one index from a model onto the user property of any widget. There are also several models to choose from. The abstract models are bases for your own implementations. Then there's the QStandardItemModel
that provides a flexible tree/table storage of data. Finally, QSqlQueryModel
and QSqlRelationalTableModel
expose SQL databases as models.
在下面的示例中,比较器被实现为一个视图模型 - 一个代理,用比较的结果修改底层图像提供模型.对于要显示的图像,需要在 Qt::DecorationRole
下提供它们.主窗口只是视图(is-a),不需要子类化.
In the example below, the comparator is implemented as a viewmodel - a proxy that amends the underlying image-providing model with results of the comparison. For the images to be displayed, they need to be provided under the Qt::DecorationRole
. The main window is simply the view (is-a), and no subclassing is necessary.
#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"
相关文章