你好~我正在创建一组自定义小部件,用于扩展 Qt 中的本机小部件。我的自定义小部件应该是从数据源构建的,它们都提供自定义功能
Foobar
。例如:
class CheckBox: public QCheckBox, public Control
{
Q_OBJECT
public:
CheckBox(QWidget* parent = 0);
CheckBox(DataSource* data, QWidget* parent = 0);
virtual ~CheckBox();
virtual void Foobar();
};
class ComboBox: public QComboBox, public Control
{
Q_OBJECT
public:
ComboBox(QWidget* parent = 0);
ComboBox(DataSource* data, QWidget* parent = 0);
virtual ~ComboBox();
virtual void Foobar();
};
每个小部件类都有一个相应的数据源类,负责创建小部件。例如:
class CheckBoxDataSource: public DataSource
{
public:
CheckBoxDataSource();
virtual ~CheckBoxDataSource();
virtual QWidget* createControl(QWidget* parent);
};
class ComboBoxDataSource: public DataSource
{
public:
ComboBoxDataSource();
virtual ~ComboBoxDataSource();
virtual QWidget* createControl(QWidget* parent);
};
这些类暴露给Python之后,拥有各种数据源列表的Python客户端预计会像这样使用它:
for data in dataSources:
widget = data.createControl(parent) # returns a QCheckBox/QComboBox/etc
# at a later time ...
widget.Foobar()
但是,这是行不通的,因为
data.createControl()
将返回 PyQt 中 QCheckBox
或 QCombox
类型的对象,因此客户端将无法调用 widget.Foobar()
,因为 Foobar()
函数不在这些课程中可用。我该如何解决这个问题?
到目前为止,我已经尝试过以下这些,但没有一个起作用......
在 Python 中,我尝试将小部件转换为正确的 C++ 类,以便我可以调用
Foobar()
。
widget.__class__ = CheckBox
widget.__class__ = ComboBox
在某些情况下,这工作正常,在其他情况下,即使它是正确的类型,我也会收到错误
AttributeError: 'CheckBox' object has no attribute 'Foobar'
。有趣的是,如果我在该行上放置一个断点并在调试器中单步执行它,它总是可以工作,没有断点它会失败,所以似乎存在一个我无法弄清楚的计时问题。不管怎样,变异__class__
是危险的,所以我可能应该避免它。
如果我将
virtual void Foobar()
添加到基类 Control
并声明 createControl()
以返回 Control*
而不是 QWidget*
。这应该允许客户端在 Python 中调用 Foobar()
,但他们也失去了将其视为 QWidget
的能力。
widget = CheckBox(data.createControl(parent))
通过将返回的对象包装在另一个构造函数调用中,Python 中的小部件现在应该具有正确的类型。但这会创建 2 个
CheckBox
小部件,而不是 1 个,所以我必须“删除”其中一个。看起来在这种情况下正在调用复制构造函数。我不认为我可以用 Python move
实现它。
抱歉我的大脑现在一片混乱,我想应该有更好的工厂设计?
因此您希望拥有
QWidget*
以及附加接口 Foobar
。恐怕这可能是不可能的,不是以简单的 C++ 方式,没有包装类,例如类似于 Qt User Interface Compiler使用的
setupUi(parentPtr);
模式。 Qt 不允许从 QObject
进行多重继承,因此您不能将 class Control
设计为 QWidget
,因为那样您就无法同时继承 Control
和 QCheckBox
。
但是,您可以使用元对象功能(反射模式)。如果您将附加接口定义为
QINVOKALBES
:
class MyCheckBox : public QCheckBox {
Q_OBJECT;
public:
Q_INVOKABLE void Foobar() { qDebug() << "MyCheckBox::Foobar"; }
};
然后您可以用
QMetaObject::invokeMethod(myCheckBoxPtr, "Foobar", Qt::DirectConnection);
来称呼它们,其中 myCheckBoxPtr
是 QWidget*
(如果您想进一步抽象,甚至可以使用 QObject*
)。
QWidget* createControl(QWidget* parent);
返回一个没有 Foobar() 的 QWidget 指针。
你不能在cpp或python中调用
widget->Foobar()
,因为widget是一个QWidget。
解决这个问题:
制作一个扩展 QWidget 和 Control 的基本小部件(例如 BaseWidget)。 BaseWidget 有成员函数 Foobar()。然后更改
virtual QWidget* createControl(QWidget* parent);
返回 BaseWidget* 。然后你可以在Python中调用w.Foobar()。