I fought this beast not too long ago, with deeply nested widgets communicating with the top-most one for user feedback.
I tried a number of solutions, documented here, but in a nutshell there where three variations
- Globals (outer-most object passing directly to inner-most)
- Signals (a compromise)
- Chain-of-responsibility (every object passing messages along)
Globals
Globals was the most successful attempt initially, as all problems I was having just magically disappeared and the world seemed at peace. However once the infatuation faded, a number of other issues surfaced; mainly about understanding how things worked after some time has passed and I not longer had intricately detailed information about what I did in the past. I think it goes without saying, globals isn’t fit for this problem.
With PubSub implements this solution, and though pubsub seems to be working for many and is a replica of typical message-passing which also has promise, I came to the conclusion that it created more problems that it solved.
Justin Israel contributed this other solution which works in a similar fashion, With Event Filter
Signals
This isn’t to say that signals is always a compromise of globals and locals. In fact, I first used signals as a means of passing signals from one child to its parent, much like chain-of-responsibility. This even worked great at first, but ultimately became quite rigid. The problem occurred whenever a new signal was introduced, at which point I had to return to objects already implemented to also forward this new signal.
A better way was to utilise MVC and build a flat hierarchy of widgets on one side (the view), a central location in which they all communicated with (model), and finally a place where they were handled (controller).
With MVC
Chain-of-responsibility
This made a lot of sense to me and I made two separate implementations of it.
Qt’s Event system works with chain-of-responsibility. All of its events are passed from widget to widget up the parent hierarchy, much more simply than you might expect. So the first example, custom cascading, implements this behaviour into a custom QWidget. The end result is custom events being passed up the chain and finally delivered to the top-most widget.
This did however pose a problem when implementing widgets that relied on subclasses of QWidget, such as QLineEdit, as you would then have to implement the method there too. So the second approach is subclassing QApplication, which seemed the recommended approach. Now, you might think, “why not just use the QApplication directly?” Well, the reason is that custom QEvents simply aren’t propagated. The source code confirms this.
With a subclassed QApplication, QEvents and custom QEvents now acted accordingly and allows for the elegant and implicit chain-of-responsibility normally used in Qt, meaning the inner-most widgets may pass an event to its nearest parent and trust it to pass it along to the grand-parent and so forth, finally catching it within an “event()” method.
This final solution was the one that stuck and I’m now treating all these scenarious this way, subclassing QEvent for every unique event that needs to touch more than one object up a chain and it works quite well.
A more full-blown example has been documented here:
With the custom QApplication here: labs/python/mvc/editable.py at master · abstractfactory/labs · GitHub
One of the custom events here: labs/python/mvc/editable.py at master · abstractfactory/labs · GitHub
And a replacement of a click-event for a custom QEvent here: labs/python/mvc/editable.py at master · abstractfactory/labs · GitHub
Hope it helps.