1

Disclaimer: This description contains a lot of Qt specifics. They are not necessary to answer the question, I just wanted to give you the background.

I need to react on the focusInEvent of a QTextEdit. Unfortunately this is not available as a signal, that's why I need to subclass QTextEdit. Since this is the only change I need, I would like to use an anonymous subclass

Like this:

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

This is the code I would write in Java, it doesn't compile in c++. All following code is within the constructor of a custom QWidget. The QTextEdit is contained in this widget and should be initialized in its constructor.

Strangely this code compiles:

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

but is useless, since I can't assign an instance of myTextEdit* to a pointer to QTextEdit. Somehow polymorphism fails. This code doesn't compile:

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

The compiler error is:

/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();

Actual question:

How do I create an anonymous subclass that is compatible to pointers of its superclass ?

lhk
  • 27,458
  • 30
  • 122
  • 201
  • 2
    Why do you use protected inheritance? If you use public inheritance, then you last example will compile. – Thomas Sparber Sep 18 '15 at 09:18
  • Maybe have a look here: http://stackoverflow.com/questions/14368619/can-i-create-anonymous-classes-in-c-and-capture-the-outside-variables-like-in – Thomas Sparber Sep 18 '15 at 09:23
  • @ThomasSparber, I need to override the event. It is marked as virtual protected. – lhk Sep 18 '15 at 09:42
  • @ThomasSparber, I looked at that question, but the problem is not the same. The other question asks how to capture outside variables. I don't need any outside variables, I just want to modify the textfield on its focus event. My problem is the type of the anonymous class – lhk Sep 18 '15 at 09:44
  • 1
    I do not understand you comment about the event being marked as virtual protected. If the event is a class, "marked as virtual proteced" doesnt make much sense to me. And btw, why is this tagged as java? – 463035818_is_not_an_ai Sep 18 '15 at 09:54
  • you mean `focusInEvent()` is protected and virtual in the base class?? If so, you are confusing some stuff and there is no problem to inherit publicly – 463035818_is_not_an_ai Sep 18 '15 at 09:56

2 Answers2

1

Your subclassing attempt

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

Is almost ok.

Just because the method is protected doesn't mean you should inherit with protected.

  • A protected method says: This is not part of my interface. Nobody but me should be able to call this. I will call this myself (as a part of the event handling). The method may be overriden in subclasses.
  • Inheriting protected says: Nobody should know about this inheritance, it is an implementation detail that might be useful to classes extending me.

You want the regular public inheritance.

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

Now you are saying that MyTextEdit is a replacement for a QTextEdit. You might want to add a constructor to supply the parent widget to MyTextEdit.

There is no such thing as a java-like anonymous inner class in c++.

Captain Giraffe
  • 14,407
  • 6
  • 39
  • 67
1

You don't need subclassing at all. You can convert events to signals using a helper class that can be applied to any event type, on any object. The EventSignaler acts as an event filter for one or more objects. It emits the eventSignal when a matching event has reached a given object.

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 &);
};

The EventWrapper is a helper class used to represent the type of an event and safely carry the pointer to an event. When the class is copied, e.g. when it is delivered via a queued connection, the original event won't exist anymore, so the wrapper zeroes out the event pointer. This is necessary, since the events generally are not copyable.

// 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)

Finally, we can demonstrate all this on a little example with a label and a line edit. The text from the line edit is copied to the label whenever you click on the label.

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"
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • very helpful. This was the way I actually did it. Had to accept the other question as an answer though, since the question was explicitly qt independent – lhk Sep 21 '15 at 14:29