Call method on subclass given pointer to superclass - c++

I have a set of buttons in a group:
buttons = new QButtonGroup();
button_0 = new MyButton("A", this);
button_1 = new MyButton("B", this);
button_2 = new MyButton("C", this);
MyButton is a QPushButton, which inherits QAbstractButton. One MyButton in the group can be selected at a time.
MyButton::MyButton(const QString &text, QWidget *parent)
: QPushButton(text, parent)
{
this->setCheckable(true);
this->setChecked(false);
}
MyButton has an increment method.
void MyButton::increment()
{
this->j++;
}
I want to call the increment method on whichever MyButton instance is currently selected. There's a handy checkedButton() method that I can call on buttons, but that returns a pointer to a QAbstractButton.
I can't call increment on the pointer or the QAbstractButton. buttons->checkedButton()->increment(); yields error: no member named 'increment' in 'QAbstractButton'. How can I increment the selected MyButton?

You just need to let the compiler know that you are using a MyButton* rather than a QAbstractButton*, and that means a cast.
If you know that the pointer is a MyButton* then you can just do a static_cast<MyButton*>(buttons->checkedButton())->increment(); If you don't know if its a MyButton* then you'll need to check, and that'll mean something like this:
MyButton* temp = dynamic_cast<MyButton*>(buttons->checkedButton());
if(temp != nullptr) temp->increment();
EDIT:
SaZ has pointed out that qobject_cast is preferable to dynamic_cast because:
It doesn't require RTTI support and it works across dynamic library boundaries
MyButton already inherits from QObject, but there is one more qualification, that it is declared with Q_OBJECT. If both of these qualifications are met then you can simply replace the dynamic_cast with a qobject_cast:
MyButton* temp = qobject_cast<MyButton*>(buttons->checkedButton());
if(temp != nullptr) temp->increment();

Note that in the case of QObject derived classes, you can and should use qobject_cast() instead of dynamic cast. It is more efficient by employing the Qt meta system.
Also, unlike the dynamic cast, it will work when compiling without RTTI, and with dynamic libraries as well.

Dynamic cast QAbstractButton to MyButton and call increment().
QAbstractButton* abstractButton = buttons->checkedButton();
MyButton* myButton = dynamic_cast<MyButton*>(abstractButton);
if (myButton)
{
myButton->increment();
}

For the sake of completeness, in the case of Qt program you may also make use of signal-slot system: if increment was declared to be a slot then this would work:
QMetaObject::invokeMethod(abstractButton, "increment");

Related

QT - Detecting which button was pressed

I have a problem with the usage of Application::sender. I have a few QPushButtons and in one function, I want to detect which button was pressed.
I got to know that using Application::sender might be the solution, however I have troubles with it. Namely I get two errors:
call to non-static member function without an object argument
'sender' is a protected member of 'QObject'
And here is my code:
void MainWindow::on_button_click()
{
unsigned long i=0;
for(; i<buttons.size(); ++i)
{
if(buttons[i] == QApplication::sender())
break;
}
if(checks[i]->checkState() == false)
buttons[i]->setText("Undone");
else
buttons[i]->setText("Done!");
}
Where variable buttons is a vector of QPushButton *
Call the method sender() of the object where your slot is, not the static member of QApplication.
In other words, remove QApplication:: and your code should work as expected.
sender() returns QObject. You need QPushButton so your have to use casting. This code will work:
QPushButton *button = qobject_cast<QPushButton*>(sender());
button->setText("New Text");

How do I get ui from a widget in order to connect it in another class?

I need to connect a QPushButton (startListeningPushButton) from my StartWindow to a slot in my MainController. I still have a few questions:
Should I make a pointer of Ui::startWidget ui, because by default Qt created it as a normal variable?
Is getStartWindow() the right way to get the StartWindow from ViewController?
What would be the right way to get startListeningPushButton from StartWindow (is my getter right)?
This is my code:
MainController.cpp:
MainController::MainController()
{
connect(m_viewController.getStartWindow()->getStartListeningPushButton, &QPushButton::clicked, this, &MainController::bla)
}
ViewController.cpp:
StartWindow* ViewController::getStartWindow()
{
return &startWindow;
}
StartWindow.cpp:
QPushButton* StartWindow::getStartListeningPushButton()
{
return ui->fStartListeningPushButton;
}
StartWindow.h:
#ifndef STARTWINDOW_H
#define STARTWINDOW_H
#include "ui_startwindow.h"
class StartWindow : public QWidget
{
Q_OBJECT
public:
StartWindow(QWidget *parent = 0);
~StartWindow();
QPushButton* getStartListeningPushButton();
private:
Ui::startWidget *ui;
};
#endif // STARTWINDOW_H
If you are using Qt Designer and Qt IDE generated such code that it's object not a pointer I don't think that you should make it pointer.
Yeah, returning a pointer to QWidget (StartWindow in your case) is pretty OK.
Your getter is OK.
Seems like you have mistype in your connect, it should look like this:
QObject::connect(m_viewController.getStartWindow()->getStartListeningPushButton(), SIGNAL(clicked()),
this, SLOT(bla()));
It's unclear if you have and then what is your problem.
The only thing I doubt would work is the first parameter of your call to connect:
m_viewController.getStartWindow()->getStartListeningPushButton should actually be m_viewController.getStartWindow()->getStartListeningPushButton() (to have the function be called so that you get the pointer to expected QPushButton and pass it to the connect function).
In the connect function:
First and third parameter must be of type QObject*. So this can either be this pointer (if current class is derived from QObject), or any class attribute of type QObject* (ui->fStartListeningPushButton) or a function call returning QObject* (m_viewController.getStartWindow()->getStartListeningPushButton()). But m_viewController.getStartWindow()->getStartListeningPushButton (with no ()) does not make sense here).
Second parameter must be a signal (declared in header file class using signals: keyword. You don't need implement any code here, you just declare the signal and Qt MOC mechanism implements it silently). Valid syntax for this parameter is &QPushButton::clicked or SIGNAL(clicked()) (Qt4 syntax, still valid in Qt5).
Fourth parameter must be a slot (declared in header file class using slots: keyword, and implemented by you). Valid syntax for this parameter is &MainController::bla or SLOT(bla()) (Qt4 syntax, still valid in Qt5).
There's actually a fifth optional parameter to use when you'll start dealing with threads.

Passing different classes in connect() in Qt

I'm working on a image editing software which includes a few classes. But I need my code to be more generic. But I've got a big problem with my classes when it comes to connections.
QObject::connect(actionSmartContrast, SIGNAL(triggered(bool)), effectsWindow, SLOT(addSmartContrast()));
QObject::connect(actionSaturation, SIGNAL(triggered(bool)), effectsWindow, SLOT(addSaturation()));
I've got a Menu called "Effects", and when the user clicks the QAction actionSmartContrast, then the effect Smart Contrast is added to my effects window. The thing is, given that each effect has its own class, I have to create a function for each class as you can see in the code above. And this is very repetitive. I would like to avoid this problem by doing something like this:
QObject::connect(actionSmartContrast, SIGNAL(triggered(bool)), effectsWindow, SLOT(addEffect(new SmartContrast())));
QObject::connect(actionSaturation, SIGNAL(triggered(bool)), effectsWindow, SLOT(addEffect(new Saturation())));
Everything would be fine for the function addEffect() because it expects a pointer to an Effect object and both SmartContrast and Saturation inherit from Effect. The only problem is that it is impossible to pass variables in connect() like this. So I thought of subclassing QAction and creating a signal which would return the class I like everytime but again, how to tell my new Action class what class it should return? If I have a thousand effects, I won't subclass QAction a thousand times! I need to create a function which would take for example a pointer to a SmartContrast object and it will guess that it has to return a SmartContrast pointer everytime the Action is clicked. And that would still be possible to do it because of the inheritance from the class Effect. But I really can't figure out how to do that. Any help would be much appreciated. Thanks in advance!
Looks like QSignalMapper is exactly what you're looking for.
UPDATED:
Another way is to use lambda (if Qt version and c++ compiler allows):
QObject::connect(actionSmartContrast, &QAction::triggered, [effectsWindow](){ effectsWindow->addEffect(new SmartContrast()) });
There are several options.
If it is enough to have the base class pointer of the effects because you use e.g. virtual methods following solution should do:
You can create an intermedite class:
class Intermediate : public QObject
{
Q_OBJECT
public:
Intermediate(QObject* parent = 0) : QObject(parent){}
signals:
void triggerEffect(Effect*);
public slots:
void effectTriggered()
{
QAction* action = qobject_cast<QAction*>(QObject::sender());
if ( action ) {
std::map<QAction*,Effect*>::iterator it = m_mapping.find(action);
if ( it != m_mapping.end() )
{ emit triggerEffect( it->second ); }
}
}
public:
void registerActionEffectPair(QAction* action,Effect* effect)
{ m_mapping[action]=effect; }
private:
std::map<QAction*,Effect*> m_mapping;
};
To use your Effect base class as type for signals and slots, you have to register it as a metatype:
qRegisterMetaType<Effect*>();
Connect it:
QObject::connect(intermediateInstancePtr, SIGNAL(triggerEffect(Effect*),
effectsWindow, SLOT(addEffect(Effect*)));
And the connections of each action would look like:
intermediateInstancePtr->registerActionEffectPair( yourEffectAction, theEffectPtr );
QObject::connect(yourEffectAction, SIGNAL(triggered(bool)),
intermediateInstancePtr, SLOT(effectTriggered()));
Another one could be to use QObjects properties:
setProperty( "property", "value" )
Call this for each effect QAction and read the property in the slot "addEffect".
The property can be read by calling
QAction* action = qobject_cast<QAction*>(QObject::sender());
if ( action ){
QVariant val = action->property("property");
if ( val.isValid() )
{
//TODO
}
}
since Object::sender returns the sender which is responsible for the slot call.
Afterwards you can do a switch case or stuff like this to distinguish between the different effects.
I finally solved my problem! I subclassed QAction and added a signal to my new class which creates a new effect from the class I want depending on the property text(). Simple if blocks are enough. Thank you all for your answers!

Qt object ownership when using lambda as slot

I recently joined a new project, which is full with idiom like: ,
void foo()
{
Widget* temp = new Widget;
connect(temp, &Widget::signalTriggerred,[this, temp ]()
{
do cool staff...
}
}
As you can see no delete nothing, I am afraid even user class "Widget" is inherited QObject, this is still a leak. Does QT do something fancy to prevent leek in case above?
What I am planning to do:
void foo
{
std::shared_ptr<Widget > temp( new Widget () );
connect(temp.get(), &Widget::signalTriggerred,[this, temp] ()
{
do even cooler things...
}
}
Is there a problem with my apporach? (For example I didn't want to use .get() but compiler errors forced me to use it).
Edit : Since there is no parent in my case it is different. Duplicated question seek answer for parent-child cases. I am already aware in that case there will be no leek. In my question I am asking about creating a local QObject based object. And connecting it.
Not quite sure what you're wanting from the question, but if you need to delete the widget and it is derived from QObject, you can delete it in the lambda expression, assuming it's not going to be used after this scope:
void foo()
{
Widget* temp = new Widget;
connect(temp, &Widget::signalTriggerred,[this, temp ]()
{
temp->deleteLater();
}
}
This very much depends on the context.
If the temp widget is actually a visible top-level widget (no parent QWidget), then the widget needs to be kept alive until the user closes it. You can achieve it getting deleted automatically when being closed using:
widget->setAttribute(Qt::WA_DeleteOnClose);
If the temp widget however is inserted into the layout of some parent widget, Qt will automatically insert it into the QObject ownership tree and temp will have the same lifetime as its parent, see QObject::setParent().
The shared_ptr by itself saves nothing because it does not answer the question of intended lifetime of widget.

How to make a function to be a slot temporally in Qt?

To make a function of a class to be a slot, the class has to inherit from QObject. However, QObject takes up a quite large amount of memory. I am not sure how much it is and if the memory is for each class or each object. My code has many small data whose functions can be a slot sometime. I am wonder if there is a way to make a function of class to be a slot temporally when using it. After using it, the memory for the slot cost will be deleted. The following code illustrates the requirement.
class SmallData // size of 2 or 3 integers.
{
public:
virtual void F(); // use it as a slot.
virtual QMenu* createMenu(); // use this to create the context menu with
// an action connected to F()
...
};
// use the small data
vector<SmallData> vec(1000000); // the vector is put at a tree view. When an
// item in the tree view is selected, a context
// menu pop up with an action to run F().
SmallData* data = treeView.selectedItem();
connect(action, SIGNAL(triggered()), data, SLOT(F())); // How to make F() to be
// a slot just here.
// The action is from
// data->createMenu().
If you can use Qt5, you can connect signals to plain functions and static methods (which essentially are funnily named plain functions):
connect(action, &QAction::triggered,
&SmallData::statF);
Where action is a QAction instance, and SmallData::statF is a static method of SmallData.
Edit per Christian Rau's comment, to call a particular instance, you can also connect to lambda:
connect(action, &QAction::triggered,
[data]() { data->F(); });
Already with Qt4, you can use QSignalMapper to achieve much the same effect, with a few more objects. It allows you to add add a parameter (in this case, probably an integer index to your vec) to signal, based on which object emitted it. But in Qt4, receiver must still always be a QObject.
For using the signal slot mechanism, you won't get around QObject, but what you can do is create a temporary object that has a slot calling your function. You just have to care for properly releasing the object. Something like:
class Callback : public QObject
{
Q_OBJECT
public:
typedef std::function<void()> FunctionType;
Callback(FunctionType fn, bool oneShot = true, QObject *parent = nullptr)
: QObject(parent), fn_(std::move(fn)), oneShot_(oneShot) {}
public slots:
void call()
{
fn_(); //delegate to callback
if(oneShot_)
deleteLater(); //not needed anymore
}
private:
FunctionType fn_;
bool oneShot_;
};
Callback* makeCallback(FunctionType fn, bool oneShot = true, QObject *parent = nullptr)
{
return new Callback(std::move(fn), oneShot, parent);
}
You then just create a (more or less temporary) Callback object each time needed:
SmallData* data = treeView.selectedItem();
connect(action, SIGNAL(triggered()),
makeCallback(std::bind(&SmallData::F, data)), SLOT(call()));
With the oneShot parameter you can control if the slot should dissolve automatically once triggered.
The only problem is, if this slot is never called, you have a leaking Callback hanging around. To accomodate this, you can pass something meaningful into the parent argument, so that Qt cares for proper deletion at least at some later point in time:
SmallData* data = treeView.selectedItem();
connect(action, SIGNAL(triggered()),
makeCallback(std::bind(&SmallData::F, data), true, this), SLOT(call()));
This way you can also bind the lifetime of the callback object (and thus the signal-slot connection) to some other object (e.g. the action itself and deleting the action when no item is selected, or something the like).
Alternatively, you can also remember the Callback object for the currently selected item and care for proper deletion yourself, once it's delesected.
disclaimer: Beware that the above example contains plenty of C++11, but I'm not in the mood to rewrite this for C++03. Likewise can this solution be imporved further, maybe using a templated functor instead of a std::function (but if I remember correctly the Qt meta object system doesn't like templates that much).
EDIT: In the end the solution proposed by Frank Osterfeld in his comment might be a much simpler approach for your situation than my overly generic object lifetime madness above: Just connect the action to a single slot of a higher level object (your main widget or maybe the item model containing the data vector) and call F on the currently selected item:
connect(action, SIGNAL(triggered()), this, SLOT(callF()));
...
void MyController::callF()
{
treeView.selectedItem()->F();
}
I don't think that what you try to do is possible in Qt.
If you really don't want to inherit QObject, then I suggest you have a look at the boost signals and slots mechanism.