Qt新手:QLineEdit和QTextEdit的基础class
Qt novice: base class for QLineEdit and QTextEdit
除了 QWidget
之外,是否还有另一个 class 拥有两者的所有通用功能?像 QEdit 这样的东西...
作为示例,我想引用 cut()、copy() 和 paste(),但看起来我必须动态转换 QWidget
。还有其他办法吗?
除了QWidget
没有别的办法了。原因是QLineEdit
直接继承自QWidget
。你可以看到Qt的完整层次类 here
您不必动态转换任何内容:这通常是设计不当的标志。 Qt 通常只有很少的接口 类 - 它们通常在名称的某处有 Abstract
这个词,并且不是真正的纯接口,因为它们具有非抽象基础 类,例如QObject
。因此没有可遵循的模式,也不需要将编辑操作抽象到界面中。
有几种方法可以解决这个问题:
利用元对象系统已知所讨论的方法这一事实。请注意,invokeMethod
采用方法 name,而不是签名。
bool cut(QWidget * w) {
return QMetaObject::invokeMethod(w, "cut");
}
bool copy(QWidget * w) {
return QMetaObject::invokeMethod(w, "copy");
}
//...
您可以在任何支持编辑操作的小部件上使用上述独立功能。
同上,但缓存方法查找不重复支付其成本。请注意,indexOfMethod
采用方法 signature,而不仅仅是其名称。
static QMetaMethod lookup(QMetaObject * o, const char * signature) {
return o->method(o->indexOfMethod(signature));
}
struct Methods {
QMetaMethod cut, copy;
Methods() {}
explicit Methods(QMetaObject * o) :
cut(lookup(o, "cut()")),
copy(lookup(o, "copy()")) {}
Methods(const Methods &) = default;
};
// Meta class names have unique addresses - they are effectively memoized.
// Dynamic metaobjects are an exception we can safely ignore here.
static QMap<const char *, Methods> map;
static const Methods & lookup(QWidget * w) {
auto o = w->metaObject();
auto it = map.find(o->className());
if (it == map.end())
it = map.insert(o->className(), Methods(o));
return *it;
}
bool cut(QWidget * w) {
lookup(w).cut.invoke(w);
}
bool copy(QWidget * w) {
lookup(w).copy.invoke(w);
}
//...
定义一个接口并提供专门用于小部件类型的实现。这种方法的唯一好处是它比 QMetaMethod::invoke
快一点。将此代码用于剪贴板方法没有什么意义,但它可能有助于最大限度地减少经常调用的小方法的开销。我建议不要过度设计它,除非基准测试表明它确实有帮助。以前的方法(上面的#2)应该足够了。
// Interface
class IClipboard {
public:
virtual cut(QWidget *) = 0;
virtual copy(QWidget *) = 0;
virtual paste(QWidget *) = 0;
};
class Registry {
// all meta class names have unique addresses - they are effectively memoized
static QMap<const char *, IClipboard*> registry;
public:
static void register(const QMetaObject * o, IClipboard * clipboard) {
auto name = o->className();
auto it = registry.find(name);
if (it == registry.end())
registry.insert(name, clipboard);
else
Q_ASSERT(it->value() == clipboard);
}
static IClipboard * for(QWidget * w) {
auto it = registry.find(w->metaObject()->className());
Q_ASSERT(registry.end() != it);
return it->value();
}
static void unregister(const QMetaObject * o) {
registry.remove(o->className());
}
};
template <class W> class ClipboardWidget : public IClipboard {
Q_DISABLE_COPY(ClipboardWidget)
public:
cut(QWidget * w) override { static_cast<W*>(w)->cut(); }
copy(QWidget * w) override { static_cast<W*>(w)->copy(); }
paste(QWidget * w) override { static_cast<W*>(w)->paste(); }
ClipboardWidget() {
Registry::register(&W::staticMetaObject(), this);
}
~ClipboardWidget() {
Registry::unregister(&W::staticMetaObject());
}
};
// Implementation
QMap<const char *, IClipboard*> Registry::registry;
static ClipboardWidget<QTextEdit> w1;
static ClipboardWidget<QLineEdit> w2;
void yourCode() {
//...
Registry::for(widget)->cut(widget);
}
除了 QWidget
之外,是否还有另一个 class 拥有两者的所有通用功能?像 QEdit 这样的东西...
作为示例,我想引用 cut()、copy() 和 paste(),但看起来我必须动态转换 QWidget
。还有其他办法吗?
除了QWidget
没有别的办法了。原因是QLineEdit
直接继承自QWidget
。你可以看到Qt的完整层次类 here
您不必动态转换任何内容:这通常是设计不当的标志。 Qt 通常只有很少的接口 类 - 它们通常在名称的某处有 Abstract
这个词,并且不是真正的纯接口,因为它们具有非抽象基础 类,例如QObject
。因此没有可遵循的模式,也不需要将编辑操作抽象到界面中。
有几种方法可以解决这个问题:
利用元对象系统已知所讨论的方法这一事实。请注意,
invokeMethod
采用方法 name,而不是签名。bool cut(QWidget * w) { return QMetaObject::invokeMethod(w, "cut"); } bool copy(QWidget * w) { return QMetaObject::invokeMethod(w, "copy"); } //...
您可以在任何支持编辑操作的小部件上使用上述独立功能。
同上,但缓存方法查找不重复支付其成本。请注意,
indexOfMethod
采用方法 signature,而不仅仅是其名称。static QMetaMethod lookup(QMetaObject * o, const char * signature) { return o->method(o->indexOfMethod(signature)); } struct Methods { QMetaMethod cut, copy; Methods() {} explicit Methods(QMetaObject * o) : cut(lookup(o, "cut()")), copy(lookup(o, "copy()")) {} Methods(const Methods &) = default; }; // Meta class names have unique addresses - they are effectively memoized. // Dynamic metaobjects are an exception we can safely ignore here. static QMap<const char *, Methods> map; static const Methods & lookup(QWidget * w) { auto o = w->metaObject(); auto it = map.find(o->className()); if (it == map.end()) it = map.insert(o->className(), Methods(o)); return *it; } bool cut(QWidget * w) { lookup(w).cut.invoke(w); } bool copy(QWidget * w) { lookup(w).copy.invoke(w); } //...
定义一个接口并提供专门用于小部件类型的实现。这种方法的唯一好处是它比
QMetaMethod::invoke
快一点。将此代码用于剪贴板方法没有什么意义,但它可能有助于最大限度地减少经常调用的小方法的开销。我建议不要过度设计它,除非基准测试表明它确实有帮助。以前的方法(上面的#2)应该足够了。// Interface class IClipboard { public: virtual cut(QWidget *) = 0; virtual copy(QWidget *) = 0; virtual paste(QWidget *) = 0; }; class Registry { // all meta class names have unique addresses - they are effectively memoized static QMap<const char *, IClipboard*> registry; public: static void register(const QMetaObject * o, IClipboard * clipboard) { auto name = o->className(); auto it = registry.find(name); if (it == registry.end()) registry.insert(name, clipboard); else Q_ASSERT(it->value() == clipboard); } static IClipboard * for(QWidget * w) { auto it = registry.find(w->metaObject()->className()); Q_ASSERT(registry.end() != it); return it->value(); } static void unregister(const QMetaObject * o) { registry.remove(o->className()); } }; template <class W> class ClipboardWidget : public IClipboard { Q_DISABLE_COPY(ClipboardWidget) public: cut(QWidget * w) override { static_cast<W*>(w)->cut(); } copy(QWidget * w) override { static_cast<W*>(w)->copy(); } paste(QWidget * w) override { static_cast<W*>(w)->paste(); } ClipboardWidget() { Registry::register(&W::staticMetaObject(), this); } ~ClipboardWidget() { Registry::unregister(&W::staticMetaObject()); } }; // Implementation QMap<const char *, IClipboard*> Registry::registry; static ClipboardWidget<QTextEdit> w1; static ClipboardWidget<QLineEdit> w2; void yourCode() { //... Registry::for(widget)->cut(widget); }