C ++:为事件处理程序创建一个匿名类
免责声明:此描述包含许多Qt细节。 他们没有必要回答这个问题,我只想给你一个背景。
我需要对QTextEdit
的focusInEvent
做出反应。 不幸的是,这不是一个信号,这就是我需要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’是’Editor :: Editor(std :: shared_ptr):: MyTextEdit’QTextEdit * myTextEdit =无法访问的基础新的MyTextEdit();
实际问题:
如何创建与其超类的指针兼容的匿名子类?
你的子类化尝试
class MyTextEdit:protected QTextEdit{ void focusInEvent(); }; QTextEdit* myTextEdit=new MyTextEdit();
几乎可以。
仅仅因为该方法受到保护并不意味着您应该inheritanceprotected。
- 受保护的方法说:这不是我的界面的一部分。 除了我之外,没有人能称之为此。 我将自己称呼(作为事件处理的一部分)。 该方法可以在子类中重写。
- inheritance受保护的说:没有人应该知道这个inheritance,它是一个实现细节,可能对扩展我的类有用。
你想要常规的公共inheritance。
class MyTextEdit:public QTextEdit{ void focusInEvent(); }; QTextEdit* myTextEdit=new MyTextEdit();
现在你说MyTextEdit是QTextEdit的替代品。 您可能希望添加构造函数以将父窗口小部件提供给MyTextEdit。
在c ++中没有类似java的匿名内部类。
你根本不需要子类化。 您可以使用可以应用于任何对象上的任何事件类型的帮助程序类将事件转换为信号。 EventSignaler
充当一个或多个对象的事件filter。 当匹配事件到达给定对象时,它会发出eventSignal
。
class EventSignaler : public QObject { Q_OBJECT QMap> 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() << 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 struct EventWrapper { QPointer 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"