【问题标题】:Qt Model-View-ControllerQt 模型-视图-控制器
【发布时间】:2014-07-23 09:48:27
【问题描述】:

首先我想说的是,我已经阅读了有关 Qt 和 MVC 的所有其他问题,但是我找不到我要找的东西。因此,除非您在旧问题中找到真正回答我问题的内容,否则请不要将它们链接到我。我也在 qt.digia.com 和 qt.project.com 中进行了搜索,但同样没有运气。

所以现在我的问题。我必须实现一个简单的图像比较器,它并排显示图像,以便可以比较它们。我必须使用 MVC 来做到这一点。我的问题是我从未使用过 Qt,我对如何将它与 MVC 一起使用有点困惑。

特别是,我想知道 MainWindow 应该从哪个子类化。是视图还是模型,还是两者兼而有之?这就是我的想法。 MainWindow 是我的类图中的一个视图,但我不确定,因为它也有模型的元素,因为它实际上存储数据信息。你有什么建议? 那么如何设计其他类呢?谢谢。

【问题讨论】:

    标签: c++ qt model-view-controller


    【解决方案1】:

    Qt 没有整体实现“标准”MVC 模式——您需要从头开始重新实现这样的框架。 Qt 提供了一个模型视图框架,它为 MVVM 提供了足够的功能,但这不是 MVC-by-the-book。

    在 Qt 的实现中,视图和控制器是混合在一起的。视图是向用户显示模型的内容用户在委托的帮助下用来与模型交互的内容。

    因此,一个单独的控制器的问题基本上没有实际意义,因为没有一个。在 Qt 中,具体视图是您通常不会从中派生的独立小部件。相反,您将视图集成(具有)到一个更大的小部件中,该小部件包含其他控件。

    Qt 提供了一些标准视图(列表视图、表格视图和树视图)。还有QDataWidgetMapper 允许您将一个索引从模型映射到任何小部件的用户属性。还有多种型号可供选择。抽象模型是您自己实现的基础。然后是QStandardItemModel,它提供了灵活的数据树/表存储。最后,QSqlQueryModelQSqlRelationalTableModel 将 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】:

    MainWindow 应该作为视图控制器位于您的视图和数据模型之间,我会将数据信息存储移到数据模型中并以这种方式与之交互。

    【讨论】:

    • 所以你建议使用 MainWindow 作为控制器?我应该从 View 继承什么??
    • 这完全取决于您创建 UI 的方式。您是在使用 Designer 还是以编程方式进行?
    • 因此,您的 View 是您的 .UI 文件,您的控制器是 MainWindow,它与您的数据模型交互以更新您的 UI 文件。不要担心盲目地遵循 MVC 模式,它可以作为确保适当分离职责的指导方针。
    • 好的,我知道了。非常感谢。对不起,我可以投票赞成你的答案,没有足够的声誉。但是我很担心,因为我的教授实际上想要一个真正的 mvc 模式。我希望他也喜欢这个!!
    【解决方案3】:

    “MVC”有不同的定义。如果您的意思是 Model-View-MEDIATING Controller,我认为最好使用术语 MVA,即 Model-View-Adapter。看看这个:https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93adapter

    一个名为 MainWindow 的类绝对是该模型视图的一部分。对象是窗口的事实使它成为一种视图。例如,我有一个具有三个视图的程序:GuiView、TerminalView 和 ServiceView。这些是完全不同的接口,它们使用相同的底层模型,并且在它们之间有一个适配器。注意:我还有一个适配器使用的虚拟基类“View”,因此不关心我是否换出派生视图类型。

    除了我提供的示例之外,还有很多方法可以拆分“视图”,但基本上您的视图是用户和/或客户端与程序交互的方式。

    根据 MVC 的任何不同定义或解释,窗口类不应存储信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多