0

In C++ Qt there is a class QWidget

  • class A inherits QWidget and adds some features
  • class B inherits A and adds some features
  • class C inherits B and adds some features, C must be of type QWidget and should do all things done by B

For some reason C is not behaving as expected, I have to rewrite class C. I can not modify code up to class B creation.

How can I solve this problem? Any design pattern, or anything else?

If I try to inherit QWidget and B, a multiple inheritance, there will be two QWidget which leads to problem (QObject - moc does not allow it).

If I inherit from QWidget and pass object of B to C's constructor, whatever operation B performs applies to another QWidget coming through B, which is not desired, all features provided by B should apply to my C's QWidget.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sumit
  • 1,485
  • 11
  • 24
  • First thing that comes to mind is to have a look at the [Decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern). Additional reference here: [Decorator Design Pattern](https://sourcemaking.com/design_patterns/decorator). – Phil Brubaker Apr 26 '17 at 18:57
  • ***For some reason C is not behaving as expected*** Maybe you should ask a question about that (showing some details so we can possibly help). Perhaps we can fix it if we know what is the problem. – drescherjm Apr 26 '17 at 18:57
  • 1
    ***class C inherits B and adds some feature, C must be of type QWidget and should do all things done by B*** There really is should be no problem with this. `C` will be a `QWidget` by its parent. – drescherjm Apr 26 '17 at 18:59
  • @drescherjm The problem is B is doing something with the layout of the Qwidget that it has which make user interface incorrect, and I don't have source of B. – Sumit Apr 26 '17 at 19:03
  • @PhilBrubaker , I have seen exactly the Decorator Design Pattern, but it does not look like solving my problem. It says to pass object of one class to another, if I pass object of B to C's constructor, manipulation done by B internally on it's QWidget will persist. – Sumit Apr 26 '17 at 19:07
  • @Sumit Is it possible to instantiate B as a member of C? – Phil Brubaker Apr 26 '17 at 19:09
  • @PhilBrubaker , Yes – Sumit Apr 26 '17 at 19:10
  • @Sumit So regardless of whether B is passed in to the constructor, or is instantiated as a member, it seems to me that you could (Decorator pattern-style) forward method calls to B untouched that you want to leave alone, and override them in C as necessary. Does that make sense, or am I missing something? – Phil Brubaker Apr 26 '17 at 19:15
  • @PhilBrubaker , I explain, suppose there is a temperature sensor, when that gets disconnected from machine, class B draws a content blocker image on the whole area of Qwidget that it owns, I can monitor sensor connect /disconnect and suppose C's fun. OnDisconnect() gets called, I will call B::OnDisconnect(), B will draw blocker image on its own Qwidget, not on the one which is owned by C – Sumit Apr 26 '17 at 19:20
  • @Sumit I'm sorry I don't follow. Is the idea that a B and a C must coexist and perform similar but different functions at different times, or are you trying to code your own C as an extension of B, and you will use C in place of all B? – Phil Brubaker Apr 26 '17 at 19:34
  • I think the OP wants to use `C` to provide a visualization for `B` – drescherjm Apr 26 '17 at 19:48
  • "I don't have source of B" I doubt that. It's next to impossible to use Qt-based binary libraries. – Kuba hasn't forgotten Monica Apr 26 '17 at 21:05

1 Answers1

1

Suppose there is a temperature sensor, when that gets disconnected from machine, class B draws a content blocker image on the whole area of QWidget that it owns, I can monitor sensor connect /disconnect and suppose C's fun. OnDisconnect() gets called, I will call B::OnDisconnect(), B will draw blocker image on its own QWidget, not on the one which is owned by C.

This has everything to do with C++'s rather inflexible method implementation inheritance when compared e.g. to the Common LISP Object System.

Since B's obscuration is always meant to be on top of B's contents, what you effectively need is for B to provide an overlay that draws on top of its contents, even if paintEvent is overriden in derived classes.

See this answer for a simple example, or another answer for an overlay with blur graphical effect.

This is fairly easy to accomplish by having B add an optional overlay widget to itself.

Example:

class OverlayWidget; // from https://stackoverflow.com/a/19367454/1329652    

class ObscureOverlay : public OverlayWidget
{
public:
  ObscureOverlay(QWidget * parent = {}) : OverlayWidget{parent} {}
protected:
  void paintEvent(QPaintEvent *) override {
    QPainter p{this};
    p.fillRect(rect(), {Qt::black});
  }
};

class A : public QWidget {
  ...
protected:
  void paintEvent(QPaintEvent *) override { ... }
};

class B : public A {
  ...
  ObscureOverlay m_obscure{this};
public:
  B() {
    m_obscure.hide();
  }
  Q_SLOT void OnDisconnect() {
    m_obscure.show();
    ...
  }
};

class C : public B {
  ...
protected:
  void paintEvent(QPaintEvent * event) override {
    B::paintEvent(event);
    ...
  }
};

If you don't have the source code to B, you can add the overlay to C, replicating original B's functionality when obscured. All of Qt's slots are effectively virtual, so if you pass a C to someone expecting B and connecting to its OnDisconnect slot, it will be C's slot that will get invoked.

class C : public B {
  Q_OBJECT
  ...
  ObscureOverlay m_obscure{this};
public:
  explicit C(QWidget * parent = {}) : B{parent} {
    m_obscure.hide();
  }
  Q_SLOT void OnDisconnect() { // slots are effectively virtual
    m_obscure.show();
    B::OnDisconnect();
  }
protected:
  void paintEvent(QPaintEvent * event) override {
    B::paintEvent(event);
    QPainter p{this};
    ...
  }
};
Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313