Создайте фабрику виджетов в Qt

Привет~ Я создаю набор пользовательских виджетов, которые расширяют собственные виджеты 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() недоступна в этих классах. Как я могу обойти это? До сих пор я пробовал это ниже, но ни один из них не работает...

Попытка 1

В Python я попробовал привести виджет к нужному классу C++, чтобы можно было вызвать Foobar().

widget.__class__ = CheckBox
widget.__class__ = ComboBox

В некоторых случаях это работает нормально, в других случаях я получаю сообщение об ошибке AttributeError: 'CheckBox' object has no attribute 'Foobar', хотя это правильный тип. Интересно, что если я ставлю точку останова на строку и перехожу через нее в отладчике, это всегда работает, без точки останова происходит сбой, поэтому, похоже, возникла проблема с синхронизацией, которую я не смог выяснить. В любом случае, мутация __class__ опасна, поэтому мне, вероятно, следует ее избегать.

Попытка 2

Если я добавлю virtual void Foobar() в базовый класс Control и объявлю createControl(), чтобы вернуть Control* вместо QWidget*. Это должно позволить клиентам вызывать Foobar() в Python, но они также теряют возможность обрабатывать его как QWidget.

Попытка 3
widget = CheckBox(data.createControl(parent))

Обернув возвращаемый объект в другой вызов конструктора, виджет в Python теперь должен иметь правильный тип. Но при этом будет создано 2 CheckBox виджета, а не 1, поэтому мне придется «удалить» один из них. Похоже, в этом случае вызывается конструктор копирования. Я не думаю, что смогу move это сделать на Python.

Извините, в моем мозгу сейчас полный беспорядок, я думаю, что должен быть лучший заводской дизайн?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Итак, вы хотите иметь 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*, если вы хотите абстрагироваться дальше).

Документация C++, документация по Python

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.

Кстати, вы можете изменить источник данных, используя шаблон в cpp. Замените QWidget на T расширяет QWidget, что поможет вам удалить повторяющиеся коды из источников данных с одинаковым поведением. чаевые только за cpp

Devin Aatrox 31.07.2024 12:15

OP не может иметь BaseWidget, который наследует QWidget, потому что тогда будет невозможно настроить QCheckBox, QComboBox или любые другие виджеты, используя этот базовый класс, что я объяснил в своем ответе.

pptaszni 31.07.2024 13:32

@pptaszni понял суть. как насчет использования d_ptr/q_ptr, например, qt? q_ptr — это исходный класс, который должен расширять собственные виджеты Qt, а d_ptr — внутренний класс, расширяющий Control.

Devin Aatrox 01.08.2024 08:59

Другие вопросы по теме