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。因此没有可遵循的模式,也不需要将编辑操作抽象到界面中。

有几种方法可以解决这个问题:

  1. 利用元对象系统已知所讨论的方法这一事实。请注意,invokeMethod 采用方法 name,而不是签名。

    bool cut(QWidget * w) {
       return QMetaObject::invokeMethod(w, "cut");
    }
    bool copy(QWidget * w) {
       return QMetaObject::invokeMethod(w, "copy");
    }
    //...
    

    您可以在任何支持编辑操作的小部件上使用上述独立功能。

  2. 同上,但缓存方法查找不重复支付其成本。请注意,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);
    }
    //...
    
  3. 定义一个接口并提供专门用于小部件类型的实现。这种方法的唯一好处是它比 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);
    }