Привет~ Я создаю набор пользовательских виджетов, которые расширяют собственные виджеты 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()
вернет объект типа QCheckBox
или QCombox
в PyQt, поэтому клиенты не смогут вызвать 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*
. Это должно позволить клиентам вызывать Foobar()
в Python, но они также теряют возможность обрабатывать его как QWidget
.
widget = CheckBox(data.createControl(parent))
Обернув возвращаемый объект в другой вызов конструктора, виджет в Python теперь должен иметь правильный тип. Но при этом будет создано 2 CheckBox
виджета, а не 1, поэтому мне придется «удалить» один из них. Похоже, в этом случае вызывается конструктор копирования. Я не думаю, что смогу move
это сделать на Python.
Извините, в моем мозгу сейчас полный беспорядок, я думаю, что должен быть лучший заводской дизайн?
Итак, вы хотите иметь QWidget*
с дополнительным интерфейсом Foobar
. Боюсь, это может быть невозможно, по крайней мере, простым способом C++, без классов-оболочек, например. похож на шаблон setupUi(parentPtr);
, используемый Компилятором пользовательского интерфейса Qt . Qt не допускает множественного наследования от QObject
, поэтому вы не можете спроектировать свой class Control
как QWidget
, потому что тогда вы не сможете наследовать как Control
, так и QCheckBox
.
Однако вы можете использовать возможности MetaObjects (шаблон отражения). Если вы определите свои дополнительные интерфейсы как 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);
возвращает указатель QWidget, у которого нет Foobar().
Вы не можете вызвать widget->Foobar()
в cpp или python, поскольку виджет — это QWidget.
Чтобы это исправить:
создайте базовый виджет (скажем, BaseWidget), который расширяет QWidget и Control. BaseWidget имеет функцию-член Foobar(). Затем измените virtual QWidget* createControl(QWidget* parent);
, чтобы вернуть BaseWidget* . тогда вы можете вызвать w.Foobar() в Python.
OP не может иметь BaseWidget
, который наследует QWidget
, потому что тогда будет невозможно настроить QCheckBox
, QComboBox
или любые другие виджеты, используя этот базовый класс, что я объяснил в своем ответе.
@pptaszni понял суть. как насчет использования d_ptr/q_ptr, например, qt? q_ptr — это исходный класс, который должен расширять собственные виджеты Qt, а d_ptr — внутренний класс, расширяющий Control.
Кстати, вы можете изменить источник данных, используя шаблон в cpp. Замените QWidget на T расширяет QWidget, что поможет вам удалить повторяющиеся коды из источников данных с одинаковым поведением. чаевые только за cpp