Connect slots QAction dynamically to a function - qt

I want to add some QAction dynamically from settings file :
_settings.beginGroup("openRecent");
QStringList recentList = _settings.childKeys();
foreach(QString recentFile, recentList)
{
QAction * action = new QAction(_settings.value(recentFile, "empty").toString(), this);
action->setObjectName(_settings.value(recentFile, "empty").toString());
connect(action, SIGNAL(triggered()), this, openFile(action->objectName()));
_recentFileButtons.append(action);
}
_settings.endGroup();
which fails to compile due to this line connect(action, SIGNAL(triggered()), this, openFile(action->objectName()));
Question :
How do I connect a QAction to a given function (with parameters)?

you can't, not directly
there are 2 options available:
use sender() to get the sending QObject and use that
use a QSignalMapper which will add a single parameter to the signal
signalMapper->setMapping(action, action->objectName());
connect(action, SIGNAL(triggered()), signalMapper, SLOT(map()));
and connect signalMapper to this:
connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(openFile(QString)));

You cannot pass parameters in that way. I would suggest to do the following:
connect(action, SIGNAL(triggered()), this, SLOT(openFile()));
And in your openFile() slot just do:
void MyClass::openFile()
{
QObject *obj = sender();
QString objName = obj->objectName();
[..]
}

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).

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

QSignalMapper with signal argument and extra argument

I knew that QSignalMapper work well for the condition like this:
QSignalMapper *signalMapper = new QSignalMapper(this);
connect(signalMapper, SIGNAL(mapped(int)), this, SIGNAL(SetSlice(int)));
connect(this->ui->button_1, SIGNAL(slicked()), signalMapper, SLOT(map()));
connect(this->ui->button_2, SIGNAL(clicked()), signalMapper, SLOT(map()));
connect(this->ui->button_3, SIGNAL(clicked()), signalMapper, SLOT(map()));
Now I want to implement 3 sliders all have one SLOT like buttons:
QSignalMapper *signalMapper = new QSignalMapper(this);
connect(signalMapper, SIGNAL(mapped(int)), this, SIGNAL(SetSlice(int)));
connect(this->ui->verticalSlider_1, SIGNAL(valueChanged(int)), signalMapper, SLOT(map()));
connect(this->ui->verticalSlider_2, SIGNAL(valueChanged(int)), signalMapper, SLOT(map()));
connect(this->ui->verticalSlider_3, SIGNAL(valueChanged(int)), signalMapper, SLOT(map()));
As you can see, this is contradictory with the consistent rule between SIGNAL and SLOT.
Is there a workaround here? I am using Qt4.
QSignalMapper is not about sending arguments from signals to slots but to let signal receiver know "who" was that or what data use. If you need to know both value and sender you either can use some internal class mapping, or use QObject * mapper and then cast QObject * to slider.
QSignalMapper * mapper = new QSignalMapper(this);
connect(mapper, SIGNAL(map(QWidget *)), this, SLOT(SetSlice(QWidget *)));
mapper->setMapping(this->ui->verticalSlider_1, this->ui->verticalSlider_1);
mapper->setMapping(this->ui->verticalSlider_2, this->ui->verticalSlider_2);
mapper->setMapping(this->ui->verticalSlider_3, this->ui->verticalSlider_3);
And here's slot body:
void YourClass::SetSlice(QWidget *wgt)
{
QSlider * slider = qobject_cast<QSlider *>(wgt);
if(slider) {
SetSlice(slider->value());
}
}

calling quit() method of QApplication

if i try to use quit() method directly, it is compiling perfectly,
however during runtime there comes an error saying "Object::connect: No such slot myClass::quit()."
so to avoid this, is there any way?
by using a method quitPicture()(defined as slot) the application is working fine.
is this the only solution?
myClass::myClass(QWidget *parent)
: QWidget(parent)
{
QWidget *window = new QWidget;
window->setWindowTitle(QObject::tr("Class"));
QPushButton *quitButton = new QPushButton("&Quit");
// QObject::connect(quitButton, SIGNAL(clicked()), this, SLOT(quit())); //showing run time error
QObject::connect(quitButton, SIGNAL(clicked()), this, SLOT(quitPicture())); //working perfectly
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(this);
layout->addWidget(quitButton);
window->setLayout(layout);
window->show();
}
void myClass::quitPicture()
{
std::cout << "calling quitPicture" << std::endl;
QApplication::quit();
}
The button's clicked signal can be connected directly to the application's quit slot:
QObject::connect(quitButton, SIGNAL(clicked()),
QApplication::instance(), SLOT(quit()));
This answer covers new signal/slot syntax in Qt and also additionally covers how to handle it when using a signal that uses overloads.
For signals with no overloads using QObject as an example object:
QObject obj(nullptr);
QObject::connect(&obj, &QObject::destroyed, QCoreApplication::instance(), \
&QCoreApplication::quit);
For signals with overloads using QProcess as an example object:
QProcess * process = new QProcess(QCoreApplication::instance());
QObject::connect(process, static_cast<void (QProcess::*)(int)>(&QProcess::finished), \
QCoreApplication::instance(), &QCoreApplication::quit);
That crazy-looking syntax is basically this, as placeholder syntax:
static_cast< _signalReturnType_( _ObjectName::*_ )( _overloadType1_, _overloadType2_, \
…etc )>( _&ObjectName::signalName_ )
You can check out this link if you want the details on why.
Since QProcess has two overloads, this is the other overload for it:
QProcess * process = new QProcess(QCoreApplication::instance());
QObject::connect(process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>( \
&QProcess::finished ), QCoreApplication::instance(), &QCoreApplication::quit);
If this crazy-looking stuff is spinning your head, don't sweat it. You can comment questions here, as I usually check SO daily, or at least nowadays.
void my_exit_func()
{
// in mainwindow.cpp
delete MainWindow;
}

Work around for signal and slot argument limitation

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");
}

Resources