对于一个学校项目,我们必须制作一个“游戏”,在这个游戏中,我们想象一个迷宫并找到我们的方式通过它。这种可视化必须以两种不同的方式实现,在我们的例子中是基于文本的可视化和更奇特的方式。
我们必须使用Qt,我们正在使用带有QWidget的QMainWindow,这个小部件将是一个或另一个可视化。看到在游戏过程中我们应该能够在可视化之间切换,我们使用策略模式,因此创建了一个接口(ViewInterface),两个可视化实现了这一点。除了实现ViewInterface之外,两个可视化都继承了另一个类,在基于文本的情况下,这是QPlainTextEdit(具有带文本的QWidget),而在Fancy中,这是QDialog。这里的探针是在我的控制器中,我有一个指向ViewInterface的指针,用于填充QWidet但是这样做ViewInterface也必须从QWidget继承,这会导致这个错误:QObject是'TerminalView'的模糊基础。 / p>
由于视图之间的切换可以在玩游戏时完成,并且只应在当前活动视图上调用更新,因此我们无法将特定视图传递给'setWidget'。
我做错了什么或者如何解决这个问题? (我已经考虑过但不能提供解决方案)。
答案 0 :(得分:1)
这里的问题是你继承QObject两次:首先是在ViewInterface层次结构中,第二个是QDialog层次结构(diamond problem)。 尝试为FancyView和TextView类使用虚拟继承(不工作,因为虚拟继承应该在完整的层次结构中使用)
但是你的设计存在另一个问题:QDialog和QPlainTextEdit都继承了QWidget。要解决此问题,您可以执行以下操作:
在不继承QObject的情况下创建ViewInterface摘要。这个抽象类将定义你的FancyView和TextView的接口,可能会也可能不会实现一些常见的逻辑。
使用QDialog和ViewInterface以及QPlainTextEdit和ViewInterface的多重继承实现FancyView和TextView。
这样你可能不会遇到任何模糊基类的问题。
更新:
还没有看到你的编辑:确实这会解决问题,但是 另一个问题是:如果我这样做,我就不能使用ViewInterface 指针设置我的QWidget。确实可能,但在我看来 这不是很干净
嗯,这是一个真正的问题。一个明显的解决方案是不使用ViewInterface*
而不是QWidget*
。但这意味着您需要更改相当多的代码,在您的情况下实际上可能并不那么好。
关于给定的评论,我提出了另一种解决方案:
从ViewInterface
继承QWidget
(包含所有需要的界面功能):
class ViewInterface: public QWidget {
Q_OBJECT
...
}
在小部件使用的ViewInterface
构造函数集布局中进行设置:
ViewInterface::ViewInterface (QWidget* i_parent)
: QWidget (i_parent) {
auto layout {new QGridLayout (this)};
// Zeroed margins to make full fit.
layout->setContentsMargins (0, 0, 0, 0);
setLayout (layout);
}
在派生类的构造函数中,将特定小部件添加到布局:
class FancyView : public ViewInterface {
Q_OBJECT
FancyView (QWidget* i_parent)
: ViewInterface (i_parent)
, dialog_widget_ {new QDialog (this)} {
layout->addWidget (dialog_widget_);
}
...
private:
QDialog* dialog_widget_;
}
使用目标小部件实现所需的界面。如果您要处理事件,可以使用QObject::eventFilter ()。在这种情况下,您应该将上面代码中的FancyView
对象设置为dialog_widget_
的事件过滤器。
注意:在这种情况下,您无法将FancyView
用作QDialog
。此问题的一个工作原理是代理QDialog
信号,插槽和公共函数,并创建另一个包装类FancyViewDialog
,它作为FancyView
中方法,信号和插槽的代理。这不是一个很棒的解决方案,但我没有看到任何其他解决钻石问题的方法,它允许ViewInterface
和QWidget
之间存在“is-a”关系。
答案 1 :(得分:0)
接口应该是虚拟方法的抽象,并且没有具体的基类。 ViewInterface
不应继承QWidget
。这解决了你的问题。
现在至少有两种解决方案可以将ViewInterface
实例转换为QWidget
:
模板ViewInterface
的用户并让他们确保所使用的具体类型实际上来自QWidget
。如果类型没有运行时多态性,那将会有效。
如果存在运行时多态性,请在接口中添加QWidget * widget() = 0
方法,并在派生方法中实现它。这很简单:QWidget * widget() override { return this; }
。
接口可以同时具有信号和插槽 - 它们必须是虚拟方法,但它们肯定会起作用。参见例如this answer开始使用虚拟信号。
如果要在ViewInterface
的两个具体实现之间共享一些代码,则可以使用派生自ViewInterface
的其他类来提供共享代码。 TerminalView
和FancyView
都来自该类。可以在基类类型上对助手类进行参数化,以使其跳过较少的箍来访问小部件,例如:class TerminalView : ViewHelper<QPlainTextEdit> { ... };