1
votes

In my Qt based program, I have a hierarchy like so:

MyApplication::QMainWindow -> QTabWidget -> ProjectWidget::QWidget

In MyApplication I have a traditional menu, and one of the actions is to import, and needs to call a ProjectWidget->importStuff() as defined by QTabWidget->currentWidget.

The problem I'm having is when I use QTabWidget->currentWidget I'm limited to the subset of QWidget functions, I cannot call any of the extended functions. I also don't know how to set up a SIGNAL/SLOT that will only affect the single ProjectWidget (without triggering the rest of them)

I'm thinking of adding a property to all the ProjectWidgets called "isCurrent", then I'll set a signal that will call the import, and check the "isCurrent" state. If it's the current widget, then it will execute the import function. But this seems convoluted, is there an easier way?

1

1 Answers

1
votes

You have two straightforward options

You can use QMetaObject::invokeMethod static method to invoke any invokable method, all you need is a QObject* pointer and the method name (and parameters, if there are any). To make method invokable, either use Q_INVOKABLE macro in your subclass definition, or simply make the method into a Qt slot. Then you need just the QObject pointer, and can (try to) call any method with any arguments (and get error if no such method exists).

Another way is to use qobject_cast<>(), which tries to cast a QObject subclass pointer to another, and will return nullptr, if it can't be done (object is not of that class). And once you have the right pointer type, just call any method you like. It is much like standard C++ dynamic_cast<>, but works only for QObject subclasses, because it uses the Qt meta-object system and does not depend on C++ RTTI.

Note: these two ways are something you should use when there is no nice way to have a separate, GUI-independent way to access the widget. But in your case here, these seem to make things so much simpler, it is justified, as long as you plan to keep the QTabWidget around for the lifetime of the app.


Code example from comment of OP, on using the invokeMethod way to solve this exact problem:

QMetaObject::invokeMethod(myTabWidget->currentWidget(), "callbackFunction");

This has the benefit, that now it is possible to have two QObject subclasses which are otherwise unrelated, and just have invokable method with same name (not overriden version of a common superclass method), and the method can be called. There's no need to have the class definition available at compile time, and there is no need to add a common base class or introduce an abstract interface class and use multiple inheritance. This is basically like duck typing: if QMetaObject::invokeMethod(ptr, "quack"), then it is a duck.

Downside is then that there is no compile time type checking, simple typo in the method name string can make the code silently fail. So it's important to think (and implement and comment in code, and test) what to do when the invokeMethod fails at runtime (is it a bug, or a normal situation for some UI states?). But then this applies to (Qt4 syntax of) QObject::connect too, as many Qt programmers have learned the hard way, when slots did not get called because of a typo :-).