首先我想说的是我'我已经阅读了关于Qt和MVC的所有其他问题,但我无法找到我正在寻找的东西。所以,除非您在旧问题中找到实际回答我问题的内容,否则请不要将它们与我联系起来。我也在qt.digia.com和qt.project.com上搜索过,但又没有运气。
所以现在我的问题。我必须实现一个简单的图像比较器,它可以并排显示图像,以便对它们进行比较。我必须使用MVC来做到这一点。我的问题是我从未使用过Qt,而且我对如何将它与MVC一起使用感到有些困惑。
特别是,我想知道什么是MainWindow应该被分类。它是视图还是模型,还是两者兼而有之?这就是我的想法。 MainWindow是我的类图中的一个视图,但我不确定,因为它还包含模型的元素,因为它实际上存储了数据信息。你有什么建议? 那么如何设计其他类呢?谢谢。
答案 0 :(得分:2)
MainWindow应该作为视图控制器位于您的视图和数据模型之间,我会将数据信息存储移动到数据模型中并以这种方式与其进行交互。
答案 1 :(得分:2)
Qt没有实施"标准" MVC模式作为一个整体 - 您需要从头开始重新实现这样的框架。 Qt提供了一个模型视图框架,为MVVM提供了足够的功能,但这不是MVC-by-the-book。
在Qt的实现中,视图和控制器混合在一起。视图是在用户的帮助下向用户显示模型和用户与模型交互的内容。
因此,单独控制器的问题基本上没有实际意义,因为没有。在Qt中,具体视图是您通常不会从中派生出来的独立窗口小部件。相反,您将视图集成(has-a)一个包含其他控件的更大的小部件。
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"
答案 2 :(得分:0)
“MVC”有不同的定义。如果您的意思是模型 - 视图 - MEDIATING控制器,我认为最好使用术语MVA,即模型 - 视图 - 适配器。看看这个:https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93adapter
一个名为MainWindow的类绝对是每个模型的视图的一部分。对象是窗口的事实使它成为一种视图。我有一个程序,例如有三个视图:GuiView,TerminalView和ServiceView。这些是完全不同的接口,它们使用相同的底层模型,并且介于两者之间。注意:我也是适配器使用的虚拟基类“View”,因此我不关心是否换出派生视图类型。
除了我提供的示例之外,还有很多方法可以拆分“视图”,但基本上你的观点是用户和/或客户端如何与程序交互。
窗口类不应该通过MVC的任何不同定义或解释来存储信息。