C++:为事件处理程序创建匿名 class
C++: Create an anonymous class for event handlers
免责声明: 此描述包含大量 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:
error: 'QTextEdit' is an inaccessible base of
'Editor::Editor(std::shared_ptr)::MyTextEdit'
QTextEdit* myTextEdit=new MyTextEdit();
真题:
如何创建与其超类的指针兼容的匿名子类?
你的class尝试
class MyTextEdit:protected QTextEdit{
void focusInEvent();
};
QTextEdit* myTextEdit=new MyTextEdit();
差不多好了。
仅仅因为该方法是受保护的并不意味着您应该继承受保护的。
- 一个受保护的方法说:这不是我的界面的一部分。除了我,没有人可以这样称呼它。我将自己调用它(作为事件处理的一部分)。该方法可以在 subclasses.
中被覆盖
- Inheriting protected 说:没有人应该知道这个继承,它是一个实现细节,可能对 classes 扩展我有用。
您需要常规的 public 继承。
class MyTextEdit:public QTextEdit{
void focusInEvent();
};
QTextEdit* myTextEdit=new MyTextEdit();
现在你说 MyTextEdit 是 QTextEdit 的替代品。
您可能想要添加一个构造函数来为 MyTextEdit 提供父窗口小部件。
C++ 中没有类似 java 的匿名内部 class 这样的东西。
你根本不需要 subclassing。您可以使用可应用于任何对象上的任何事件类型的助手 class 将事件转换为信号。 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
是一个助手class,用于表示事件的类型并安全地携带指向事件的指针。当 class 被复制时,例如当它通过 queued 连接传递时,原始事件将不再存在,因此包装器将事件指针归零。这是必要的,因为事件通常是不可复制的。
// https://github.com/KubaO/Whosebugn/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"
免责声明: 此描述包含大量 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: error: 'QTextEdit' is an inaccessible base of 'Editor::Editor(std::shared_ptr)::MyTextEdit' QTextEdit* myTextEdit=new MyTextEdit();
真题:
如何创建与其超类的指针兼容的匿名子类?
你的class尝试
class MyTextEdit:protected QTextEdit{
void focusInEvent();
};
QTextEdit* myTextEdit=new MyTextEdit();
差不多好了。
仅仅因为该方法是受保护的并不意味着您应该继承受保护的。
- 一个受保护的方法说:这不是我的界面的一部分。除了我,没有人可以这样称呼它。我将自己调用它(作为事件处理的一部分)。该方法可以在 subclasses. 中被覆盖
- Inheriting protected 说:没有人应该知道这个继承,它是一个实现细节,可能对 classes 扩展我有用。
您需要常规的 public 继承。
class MyTextEdit:public QTextEdit{
void focusInEvent();
};
QTextEdit* myTextEdit=new MyTextEdit();
现在你说 MyTextEdit 是 QTextEdit 的替代品。 您可能想要添加一个构造函数来为 MyTextEdit 提供父窗口小部件。
C++ 中没有类似 java 的匿名内部 class 这样的东西。
你根本不需要 subclassing。您可以使用可应用于任何对象上的任何事件类型的助手 class 将事件转换为信号。 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
是一个助手class,用于表示事件的类型并安全地携带指向事件的指针。当 class 被复制时,例如当它通过 queued 连接传递时,原始事件将不再存在,因此包装器将事件指针归零。这是必要的,因为事件通常是不可复制的。
// https://github.com/KubaO/Whosebugn/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"