Work around for signal and slot argument limitation - qt

I want to make a PushButton when it is clicked, its text change into 'clicked'. I tried it by
connect(button1, SIGNAL(clicked()), this, SLOT(markClicked(button1)));
where this refer to the MainWindow and
void MainWindow::markClicked(QPushButton *button) { button->setText("Clicked"); }
It does not seem to work because I think SLOT cannot take more arguments than SIGNAL. If there any approach to work around this limitation?
Thanks.

Qt signals/slots mechanism can only transfer signal to slot function with similar parameters. As a workaround, you should use QSignalMapper:
QSignalMapper mapper;
...
connect(button1, SIGNAL(clicked()), &mapper, SLOT(map()));
mapper.setMapping(button1, button1); // not sure whether this is mandatory or not
...
connect(&mapper, SIGNAL(mapped(QWidget*)), this, SLOT(markClicked(QWidget*)));
and function markClicked is
void MainWindow::markClicked(QWidget *widget) {
QPushButton *button = qobject_cast<QPushButton*>(widget);
button->setText("Clicked");
}

The other way you could do this is to use a default value for the argument and then use the sender() method:
In MainWindow:
void markClicked(QPushButton *button = NULL);
then:
connect(button1, SIGNAL(clicked()), this, SLOT(markClicked()));
and:
void MainWindow::markClicked(QPushButton *button) {
if (button==NULL) { button = qobject_cast<QPushButton*>(sender()); }
button->setText("Clicked");
}

Related

How to add a QAction to a QListWidget

I have the following code:
roslaserscandoialog.h
public:
explicit ROSLaserScanDialog(QWidget *parent = nullptr);
~ROSLaserScanDialog();
QListWidgetItem *createItemFromAction(const QAction* action);
private slots:
void on_listWidget_itemClicked(QListWidgetItem *item);
private:
Ui::ROSLaserScanDialog *ui;
QAction *mAddMsgs;
QAction *mDeleteMsgs;
roslaserscandoialog.cpp
ROSLaserScanDialog::ROSLaserScanDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ROSLaserScanDialog)
{
ui->setupUi(this);
connect(ui->listWidget,SIGNAL(on_listWidget_itemClicked(QListWidgetItem*)),this,SLOT(createItemFromAction(QListWidgetItem*)));
}
QListWidgetItem *ROSLaserScanDialog::createItemFromAction(const QAction *action)
{
Q_ASSERT( action );
QListWidgetItem *mAddMsgs = new QListWidgetItem();
mAddMsgs->setText( action->text() );
mAddMsgs->setToolTip( action->toolTip() );
mAddMsgs->setIcon( action->icon() );
// ...
return mAddMsgs;
}
void ROSLaserScanDialog::on_listWidget_itemClicked(QListWidgetItem *item)
{
mAddMsgs = new QAction(QIcon(":ros.png"), tr("Add New Message"), this);
mDeleteMsgs = new QAction(QIcon(":remove_item.png"), tr("Remove Message"), this);
}
What I have done so far:
I came across this post and also this one. Very useful as I set up my project in a very similar way, but nothing happens when I try to click on the QListWidget.
I know that in order to trigger the action I have to go to the slot called itemClicked as I did on the above code provided.
On the official documentation I was trying to apply what is advised but I don't know why nothing happens.
Please point to the right direction for solving this problem.
Look at console output, there should a warning about connect failing. If you look at your code, the reason should be pretty obvious. Consider
SLOT(createItemFromAction(QListWidgetItem*))
versus your method which isn't even a slot
QListWidgetItem *createItemFromAction(const QAction* action);
See the difference?
And then you have this slot:
void on_listWidget_itemClicked(QListWidgetItem *item);
which you are trying to use as a signal
SIGNAL(on_listWidget_itemClicked(QListWidgetItem*))
That obviously won't work.
It's a bit unclear what you want to happen when an item is clicked, but maybe you just should call createItemFromAction directly from the on_listWidget_itemClicked.
Additionally, add debug print or use breakpoint to verify that the on_listWidget_itemClicked is actually called when you click an item. If not, then you are missing connecting the relevant signal from your list view, ie. ui->setupUi(this); does not have that connect (in other words you did not do the connection in the GUI Designer).

Qt connect mainwindow and dialog using signal and slot

I am trying to connect mainwindow and dialog using signal and slot. I am very new to qt. I have a lineEdit and a pushButton in mainwindow.ui, a lineEdit in dialog.ui. And I have those very basic code:
mainwindow.h:
signals:
void sendString(QString);
mainwindow.cpp:
void MainWindow::on_pushButton_clicked()
{
Dialog *mDialog = new Dialog(this);
emit sendString(ui->lineEdit->text());
connect(this, SIGNAL(sendString(QString)), mDialog, SLOT(showString(QString)));
mDialog->show();
}
dialog.h:
private slots:
void showString(QString);
dialog.cpp:
void Dialog::showString(QString str)
{
ui->lineEdit->setText(str);
}
But after I clicked the pushButton, the dialog showed, but nothing changed in the lineEdit.I hope I explain this clearly enough?
Can someone explain to me why and how to solve this? Thanks.
emit signal after connect
void MainWindow::on_pushButton_clicked()
{
Dialog *mDialog = new Dialog(this);
connect(this, SIGNAL(sendString(QString)), mDialog, SLOT(showString(QString)));
mDialog->show();
emit sendString(ui->lineEdit->text());
}
You have to create the connection before the emit.
But in your case you dont need the signal of the of the mainwindow at all. You invoke the showString method directly.

how to use connect with member function

I want to connect the tabBarDoubleClicked signal with a member function but the compiler keeps barking at me with:
/home/ron/src/kterminal/sessionstack.cpp:79:56: error: invalid use of non-static member function
this, SessionStack::editTabLabel(session->id()));
^
and I'm not sure how to fix it, my code looks like:
int SessionStack::addSession(Session::SessionType type)
{
Session* session = new Session(type, this);
connect(session, SIGNAL(titleChanged(int,QString)), this, SIGNAL(titleChanged(int,QString)));
connect(session, SIGNAL(terminalManuallyActivated(Terminal*)), this, SLOT(handleManualTerminalActivation(Terminal*)));
connect(session, SIGNAL(activityDetected(Terminal*)), m_window, SLOT(handleTerminalActivity(Terminal*)));
connect(session, SIGNAL(silenceDetected(Terminal*)), m_window, SLOT(handleTerminalSilence(Terminal*)));
connect(session, SIGNAL(destroyed(int)), this, SLOT(cleanup(int)));
m_sessions.insert(session->id(), session);
QString tab_label = QString("Shell (") + QString::number(session->id(), 16) + ")";
addTab(session->widget(), tr(qPrintable(tab_label)));
emit sessionAdded(session->id());
raiseSession(session->id());
connect(this, tabBarDoubleClicked,
this, SessionStack::editTabLabel(session->id()));
return session->id();
}
void SessionStack::editTabLabel(int tabIndex)
{
How can I get editTabLabel() invoked when the tab is double clicked?
EDIT1
In my header I have the following declared:
class SessionStack : public QTabWidget
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kterminal")
public:
explicit SessionStack(QWidget* parent, QWidget* window);
~SessionStack();
private slots:
void tabBarDoubleClicked(int index);
void editTabLabel(int tabIndex);
};
the error occurs because you just forgot to add SIGNAL and SLOT macros at
connect(this, tabBarDoubleClicked, this, SessionStack::editTabLabel(session->id()));
You can't pass session->id() while connecting, you can only pass the argument when you emit the signal.
Please note that your code is not going to connect anyway in runtime since the signal misses an argument (int), so that it matches the editTabLabel slot. So you need to fix that as well.. should be
connect(this, SINGAL(tabBarDoubleClicked(int), this, SLOT(editTabLabel(int)));
if the signal "tabBarDoubleClicked" is builtin and you can't change it to add an argument then you might consider QSignalMapper http://doc.qt.io/qt-5/qsignalmapper.html

connect QPushButton and QComboBox

How can I connect a QPushButton and a QComboBox?
I created a SLOT that accepts 2 parameters, a pointer to the QComboBox and the index of the selected item:
void modificaExp::eliminaExp(QComboBox *combo,int value)
{
......
combo->removeItem(value);
....
}
the widgest are there:
QComboBox* combo=new QComboBox();
combo->addItem("ciao1");
combo->addItem("ciao44");
combo->addItem("ciao222");
combo->addItem("ciao555");
QPushButton* delButton=new QPushButton();
delButton->setText("delete");
connect(delButton, SIGNAL(clicked()), this, SLOT( eliminaExp(combo,combo->currentIndex() )));
so, when I click on delButton the element stays there. I think there is a problem in the connect command, specifically I think than the slot is not called.
Are you sure you need this slot with two parameter?
Another simple way:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setupUi(this);
connect(deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteSlot()));
}
void MainWindow::deleteSlot()
{
comboBox->removeItem(comboBox->currentIndex());
}
The slot should have the same type and equal or less number of arguments than the signal
Declare the QComboBox and the QPushButton objects in the header
modificaexp.h
private:
QComboBox* combo;
QPushButton* delButton;
modificaexp.cpp
combo=new QComboBox();
combo->addItem("ciao1");
combo->addItem("ciao44");
combo->addItem("ciao222");
combo->addItem("ciao555");
delButton=new QPushButton();
delButton->setText("delete");
connect(delButton, SIGNAL(clicked()), this, SLOT( eliminaExp()));
Modify the slot
void modificaExp::eliminaExp()
{
combo->removeItem(combo->currentIndex());
}
Refer the Qt signal slot documentation

multiple signals for one slot

For my GUI i would like to have two pairs of buttons that scroll up and down a scrollarea. The first set of buttons should work on say scrollarea1 and the second set of buttons should work on a scrollarea2. The widgets that I put in the scrollarea are called viewport1 and viewport2.
Since both both set of buttons should do the same (scrolling up and down) I thought I would make two slots called scrollUp and scrollDown that would handle the scrolling for both sets of buttons. Unfortunately I cannot make this work and need some help. I have tried the following:
QPushButton up;
QPushButton down;
QPushButton up2;
QPushButton down2;
connect(&up,SIGNAL(clicked()),&up,SLOT(scrollUp()));
connect(&up2,SIGNAL(clicked()),&up,SLOT(scrollUp()));
connect(&down,SIGNAL(clicked()),&down,SLOT(scrollDown()));
connect(&down2,SIGNAL(clicked()),&down,SLOT(scrollDown()));
void MainWindow::scrollDown()
{
QScrollArea area;
QWidget view;
if((QPushButton) &sender = down)
{
area=scrollArea;
view=viewport;
}
if((QPushButton) &sender = down2)
{
area=scrollArea;
view=viewport;
}
int curpos = area.verticalScrollBar()->value();
area.verticalScrollBar()->setValue(curpos+15);
int newpos = area.verticalScrollBar()->value();
QPoint topLeft = area.viewport()->rect().topLeft();
view.move(topLeft.x(),topLeft.y()-(newpos));
}
void MainWindow::scrollUp()
{
QScrollArea area;
QWidget view;
if((QPushButton) &sender = up)
{
area=scrollArea;
view=viewport;
}
if((QPushButton) &sender = up2)
{
area=scrollArea2;
view=viewport2;
}
int curpos = area.verticalScrollBar()->value();
area.verticalScrollBar()->setValue(curpos-15);
int newpos = area.verticalScrollBar()->value();
QPoint topLeft = area.viewport()->rect().topLeft();
view.move(topLeft.x(),topLeft.y()-(newpos));
}
But this doesn´t work for several reasons. I also tried giving the slot some arguments, something like:
connect(&up,SIGNAL(clicked()),&up,SLOT(scrollUp(scrollarea1,viewport1)));
connect(&up2,SIGNAL(clicked()),&up,SLOT(scrollUp(scrollarea2,viewport2)));
But again, no succes. Can anybody help me?
First of all, "It doesn't work" does not mean anything, and it is hard to help you if you do not say what errors you get. Then, there are few problems.
All QObject's derived classes are not copiable, it means you can not do
QWidget a;
QWidget b;
b = a; // Wrong
You should use pointers (or perhaps references).
QWidget a;
QWidget * b = new QWidget(...);
QWidget * c;
c = & a; // Ok
c = b; // Ok
Then your connect calls are wrong:
connect(&up, SIGNAL(clicked()), &up, SLOT(scrollUp()));
The third argument is the object who has the slot. up is a QPushButton, it does not have a scrollUp() slot, it is your MainWindow who does:
connect(&up, SIGNAL(clicked()), this, SLOT(scrollUp()));
(since connect is called in MainWindow's constructor this points to the current MainWindow object).
Also in C++ the single = sign means assignment, for equality comparison use =='. Andsender` is a function.
Your approach should work if implemented in the right way:
class MainWindow: public QWidget
{
QScrollArea * scroll1;
QScrollArea * scroll2;
QWidget * view1;
QWidget * view2;
QPushButton * up1;
QPushButton * up2;
QPushButton * down1;
QPushButton * down2;
public:
MainWindow()
{
// Here initialize member variables.
...
connect(up1, SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(up2, SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(down1, SIGNAL(clicked()), this, SLOT(scrollDown()));
connect(down2, SIGNAL(clicked()), this, SLOT(scrollDown()));
}
public slots:
void scrollDown()
{
QScrollArea * area;
QWidget * view;
if(qobject_cast<QPushButton>(sender()) == down1) {
area = & scroll1;
view = & view1;
} else if(qobject_cast<QPushButton>(sender()) == down2) {
area = & scroll2;
view = & view2;
} else {
// Error.
}
// Now `area` and `view` point to the right widgets.
...
}
void scrollUp()
{
// The same as before.
}
};
Another approach would be to extract the actual scrolling instructions to a separate function:
class MainWindow: public QWidget
{
// Same variables as before
...
public:
MainWindow()
{
// Here initialize member variables.
...
connect(up1, SIGNAL(clicked()), this, SLOT(scrollUp1()));
connect(up2, SIGNAL(clicked()), this, SLOT(scrollUp2()));
connect(down1, SIGNAL(clicked()), this, SLOT(scrollDown1()));
connect(down2, SIGNAL(clicked()), this, SLOT(scrollDown2()));
}
public slots:
void scrollDown(QScrollArea * area, QWidget * view)
{
// Here you scroll over `area` and `view`.
}
void scrollDown1()
{
scrollDown(scroll1, area1);
}
void scrollDown2()
{
scrollDown(scroll2, area2);
}
// Again, the same for `scrollUp`.
};
There are several mistakes in your code :
About the sender of the signal : There is not a QObject called "sender" but a method QObject * QObject::sender() const; which returns a pointer on the sender of the signal.
In the if conditions : you are casting a QPushButton** into a QPushButton ((QPushButton) &sender) and you dont compare that thing with your buttons up(2) and down(2).
In your connections between slots and signals : the scrollUp and scrollDown slots do not belong to the QPushButton class but to your MainWindow class.
Finally, you should write something like this :
connect(&up, SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(&up2, SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(&down, SIGNAL(clicked()), this, SLOT(scrollDown()));
connect(&down2, SIGNAL(clicked()), this, SLOT(scrollDown()));
void MainVindow::scrollDown() {
// [...]
QPushButton * senderButton = qobject_cast<QPushButton *>(this->sender());
// QPushButton * senderButton = (QPushButton *) this->sender(); works too
if (senderButton == &down) {
// [...]
}
if (senderButton == &down2) {
// [...]
}
// [...]
}
void MainVindow::scrollUp() {
// [...]
QPushButton * senderButton = qobject_cast<QPushButton *>(this->sender());
// QPushButton * senderButton = (QPushButton *) this->sender(); works too
if (senderButton == &up) {
// [...]
}
if (senderButton == &up2) {
// [...]
}
// [...]
}
First of all the slot can have no other arguments than the signal hands to it. Clicked has no arguments and there fore the slot can have no arguments.
I would think that the easiest way to check whether scrollArea 1 or 2 has focus and decide from that which one should move.
I also think that there is an error in your code. Shouldn't this:
if((QPushButton) &sender = down2)
{
area=scrollArea;
view=viewport;
}
Be this:
if((QPushButton) &sender = down2)
{
area=scrollArea2;
view=viewport2;
}
First of all, this is pseudo code. It won't compile, but it should contain the necessary information.
I believe this problem can be most elegantly solved using the QSignalMapper class. It allows parameterless signals from multiple senders to connect to one slot.
In the header, write something like this:
class QSignalMapper;
class MainWindow : public QMainWindow
{
public:
void init();
public slots:
void handleScrollButtons(int id);
private:
enum { ScrollUp1, ScrollDown1, ScrollUp2, ScrollDown2 } // just makes it more convenient to use
QSignalMapper *m_scrollbuttonhandler;
}
In the source file, write something like this
#include <QSignalMapper>
void MainWindow::init()
{
m_scrollbuttonhandler = new QSignalMapper(this);
m_scrollbuttonhandler->setMapping(scrollup1button, ScrollUp1);
m_scrollbuttonhandler->setMapping(scrolldown1button, ScrollDown1);
m_scrollbuttonhandler->setMapping(scrollup2button, ScrollUp2);
m_scrollbuttonhandler->setMapping(scrolldown2button, ScrollDown2);
connect(scrollup1button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
connect(scrolldown1button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
connect(scrollup2button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
connect(scrolldown2button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
connect(m_scrollbuttonhandler, SIGNAL(mapped(int)), this, SLOT(handleScrollButtons(int)));
}
void MainWindow::handleScrollButtons(int id)
{
switch (id)
{
case ScrollUp1:
// stuff to do for scrollup1button
case ScrollDown1:
// stuff to do for scrolldown1button
case ScrollUp2:
// stuff to do for scrollup2button
case ScrollDown2:
// stuff to do for scrolldown2button
}
}

Resources