Java — C ++: создать анонимный класс для обработчиков событий

Отказ от ответственности: Это описание содержит много специфики Qt. Им не обязательно отвечать на вопрос, я просто хотел дать вам предысторию.

Мне нужно реагировать на focusInEvent из QTextEdit,
К сожалению, это не доступно как сигнал, поэтому мне нужно подкласс QTextEdit, Поскольку это единственное изменение, которое мне нужно, я хотел бы использовать анонимный подкласс

Как это:

myTextEdit =new QTextEdit(){
void focusInEvent(){
//code here
}
};

Это код, который я написал бы на Java, он не компилируется в C ++.
Весь следующий код находится внутри конструктора QWidget, QTextEdit содержится в этом виджете и должен быть инициализирован в его конструкторе.

Странно этот код компилируется:

class MyTextEdit:protected QTextEdit{
void focusInEvent();
};
auto myTextEdit=new MyTextEdit();

но бесполезно, так как я не могу назначить экземпляр myTextEdit* на указатель на QTextEdit, Каким-то образом полиморфизм терпит неудачу. Этот код не компилируется:

class MyTextEdit:protected QTextEdit{
void focusInEvent();
};
QTextEdit* myTextEdit=new MyTextEdit();

Ошибка компилятора:

/home/lars/ProgrammierPraktikum/moleculator/implementation/Moleculator/gui_elements/editor.cpp:40:
ошибка: «QTextEdit» является недоступной базой
‘Редактор :: Редактор (станд :: shared_ptr) :: MyTextEdit’
QTextEdit * myTextEdit = new MyTextEdit ();

Актуальный вопрос:

Как создать анонимный подкласс, совместимый с указателями его суперкласса?

1

Решение

Ваша попытка создания подкласса

class MyTextEdit:protected QTextEdit{
void focusInEvent();
};
QTextEdit* myTextEdit=new MyTextEdit();

Это почти нормально.

То, что метод защищен, не означает, что вы должны наследовать с защищенным.

  • Защищенный метод говорит: это не часть моего интерфейса. Никто, кроме меня, не сможет это назвать. Я назову это сам (как часть обработки событий). Метод может быть переопределен в подклассах.
  • Наследование защищенного говорит: никто не должен знать об этом наследовании, это деталь реализации, которая может быть полезна для классов, расширяющих меня.

Вы хотите обычное публичное наследство.

class MyTextEdit:public QTextEdit{
void focusInEvent();
};
QTextEdit* myTextEdit=new MyTextEdit();

Теперь вы говорите, что MyTextEdit является заменой для QTextEdit.
Возможно, вы захотите добавить конструктор для предоставления родительского виджета в MyTextEdit.

В c ++ нет такого понятия, как java-подобный анонимный внутренний класс.

1

Другие решения

Вам не нужно создавать подклассы вообще. Вы можете преобразовывать события в сигналы, используя вспомогательный класс, который можно применять к любому типу события, к любому объекту. EventSignaler действует как фильтр событий для одного или нескольких объектов. Испускает eventSignal когда соответствующее событие достигло данного объекта.

class EventSignaler : public QObject {
Q_OBJECT
QMap<QObject*, QSet<int>> m_watch;
bool eventFilter(QObject * obj, QEvent * ev) Q_DECL_OVERRIDE {
auto it = m_watch.find(obj);
if (it != m_watch.end() && it.value().contains(ev->type()))
emit eventSignal(EventWrapper(obj, ev));
return false;
}
public:
EventSignaler(QObject * parent = 0) : QObject(parent) {}
void watch(QObject * object, QEvent::Type type) {
auto it = m_watch.find(object);
if (it == m_watch.end()) {
it = m_watch.insert(object, QSet<int>() << type);
object->installEventFilter(this);
connect(object, &QObject::destroyed, this, [this, object]{
m_watch.remove(object);
});
} else
it.value().insert(type);
}
void unWatch(QObject * object, QEvent::Type type) {
auto it = m_watch.find(object);
if (it == m_watch.end()) return;
it.value().remove(type);
if (it.value().isEmpty()) m_watch.erase(it);
}
Q_SIGNAL void eventSignal(const EventWrapper &);
};

EventWrapper это вспомогательный класс, используемый для представления типа события и безопасного переноса указателя на событие. Когда класс копируется, например, когда он доставлен через очередь В связи с этим исходное событие больше не будет существовать, поэтому оболочка обнуляет указатель события. Это необходимо, поскольку события обычно не подлежат копированию.

// https://github.com/KubaO/stackoverflown/tree/master/questions/event-signaler-32648234
#include <QtWidgets>

struct EventWrapper {
QPointer<QObject> target;
QEvent::Type type { QEvent::None };
QEvent * event { nullptr };
public:
EventWrapper() {}
EventWrapper(QObject * target, QEvent * event) :
target(target), type(event->type()), event(event) {}
EventWrapper(const EventWrapper & o) : target(o.target), type(o.type) {}
EventWrapper(EventWrapper && o) :
target(o.target), type(o.type), event(o.event) { o.event = nullptr; }
EventWrapper & operator=(const EventWrapper & o) {
target = o.target;
type = o.type;
event = nullptr;
return *this;
}
};
Q_DECLARE_METATYPE(EventWrapper)

Наконец, мы можем продемонстрировать все это на небольшом примере с меткой и редактированием строки. Текст из строки редактирования копируется в метку всякий раз, когда вы нажимаете на метку.

int main(int argc, char ** argv) {
QApplication app(argc, argv);
EventSignaler signaler;
QWidget w;
QVBoxLayout layout(&w);
QLabel label("text");
QLineEdit edit;
layout.addWidget(&label);
layout.addWidget(&edit);
signaler.watch(&label, QEvent::MouseButtonPress);
QObject::connect(&signaler, &EventSignaler::eventSignal,
[&label, &edit]{ label.setText(edit.text()); });
w.show();
return app.exec();
}

#include "main.moc"
1