Привет~ Я создаю набор пользовательских виджетов, которые расширяют собственные виджеты 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