Why is slot passed as string? Qt - c++

private:
Button *createButton(const QString &text, const char *member);
void abortOperation();
bool calculate(double rightOperand, const QString &pendingOperator);
Button *pointButton = createButton(tr("."), SLOT(pointClicked()));
In qt's calculator example: http://qt-project.org/doc/qt-4.8/widgets-calculator.html
The createButton member function takes two constant string. Then why we are passing slots to them as a second argument?

Simplest summary: The create button function allocates a new button, sets the text, and then connects that button's clicked signal to the slot represented with the string you sent in.
Button *Calculator::createButton(const QString &text, const char *member)
{
Button *button = new Button(text);
//NOTE right here it uses the string you passed in - BEGIN
connect(button, SIGNAL(clicked()), this, member);
//NOTE right here it uses the string you passed in - END
return button;
}
A little bit more detail as to why the signal and slot macros are compatable with strings like this (per this previous stack overflow post):
As Neil said, the SLOT and SIGNAL macros are defined as
> #define SLOT(a) "1"#a
> #define SIGNAL(a) "2"#a
The #a (with # a stringizing operator) will simply
turn whatever is put within the parentheses into a string
literal, to create names from the signatures provided to the macros.
The "1" and "2" are merely there to distinguish between slots and
signals.
This earlier post should provide you some more insight.
If you wonder about the "why?" of all this macro stuff and
preprocessing, I would suggest you read up on the
"Meta-Object-Compiler" or MOC. And just for fun you could have a look
at what MOC does to the code you provide it with. Look through its
output and see what it contains. That should be quite informative.
In short, this preprocessing through MOC allows Qt to implement some
features (like the signals and slots) which C++ does not provide as
standard. (Although there are arguably some implementations of this
concept, not related to Qt, which don't require a Meta Object
Compiler)
Hope that helps.
Please note the post I linked has other links of value, that didn't come through with the copy and paste.

Related

C++ Qt: is it possible to create a sort of template slot?

I'm new to Qt and not a C++ expert so please bear with me with this one.
I'm working on an application that has 12 different QPushButtons, but all of them perform very similar actions: they grab some value from a map and then use it to set the style for that button:
void MainWindow::on_btn01_clicked(){
QString img=images["btn01"];
ui->btn01->setStyleSheet("#btn01{ background-image: url(://" + img + ") }");
}
For each one of the 12 buttons I have to create a slot that only differs in the button being used. So it looks a bit weird to create 12 functions that are almost identical.
Is there a better way to do this?
Generally, there are several approaches I've seen used:
The preferred method: lambda expressions.
If you're using modern C++ (C++11 or newer), you can use a lambda function to get the exact effect you described.
I'd expect the resulting code to look something like this:
foreach( QPushButton * button : buttons ) {
connect( button, &QPushButton::clicked, [button, &images]() {
button->setStyleSheet( QString( "{ background-image: url(://%1) }" )
.arg( images[button->objectName()] ) );
});
}
See "Qt Slots and C++11 lambda" for more guidance on how to write lambda functions with Qt.
A dirtier way: QObject::sender().
Qt lets you use QObject::sender() within a slot to get a pointer to the object that invoked it. This has been a widely used approach for many years, but there are limitations, as described in the Qt documentation:
Warning: As mentioned above, the return value of this function is not valid when the slot is called via a Qt::DirectConnection from a thread different from this object's thread. Do not use this function in this type of scenario.
See "How to get sender widget with a signal/slot mechanism?" for more information.
Obsolete code: QSignalMapper.
There's a QSignalMapper class that will let you associate a signal with a bit of data, like a string, to then connect to a slot that uses this parameter. This used to exist as a convenience class in Qt but is being phased out because the lambda methodology makes it pointless.
If you need to update code that uses QSignalMapper to remove it, this tutorial is a reasonable starting point.
You can try one more simple method, that is set the QPushButton's object name respectively, and check the Object name in your slot and use that string. This saves you a lot of code.
Ex:
QPushButton 1 object name is set as button->setObjectName("btn01"); and respectively you can set the other names of the buttons and in your slot you could do some thing like this
void MainWindow::on_button_clicked(){
QPushButton* btn=qobject_cast<QPushButton*>(sender());
QString name=btn->objectName();
QString img=images[name]; btn->setStyleSheet("#" + name + "{ background-image: url(://" + img + ");
}
and then connect all your QPushButtons to this slot

Determine which Shortcut is pressed in Qt

I have a QAction which I've assigned multiple shortcuts to it
test = new QAction();
this->addAction(test);
QList<QKeySequence> shortcuts;
shortcuts << QKeySequence(Qt::Key_N) << QKeySequence(Qt::Key_T);
test->setShortcuts(shortcuts);
connect(test,SIGNAL(triggered()),this,SLOT(SomeFucntion()))
In SomeFucntion I need to know which shortcut was pressed....Is there anyway of knowing that ?
You could try a more elaborate pattern with QSignalMapper that avoids the need to define as many actions as many shortcut you need, but requires c++11 (at least this implementation).
In the constructor of your window use the following code to declare your QShortcut objects and a QSignalMapper:
QSignalMapper* signalMapper = new QSignalMapper(this);
QShortcut* sc1 = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_N), this);
QShortcut* sc2 = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_T), this);
connect(sc1, &QShortcut::activated, signalMapper, static_cast<void (QSignalMapper::*)(void)>(&QSignalMapper::map));
connect(sc2, &QShortcut::activated, signalMapper, static_cast<void (QSignalMapper::*)(void)>(&QSignalMapper::map));
signalMapper->setMapping(sc1, sc1);
signalMapper->setMapping(sc2, sc2);
QAction* action = new QAction();
connect(signalMapper, static_cast<void (QSignalMapper::*)(QObject*)>(&QSignalMapper::mapped),
[action](QObject *object){
QShortcut* sc = qobject_cast<QShortcut*>(object);
if (sc)
{
action->setData(sc->key().toString());
action->trigger();
}
});
connect(action, &QAction::triggered, this, &MainWindow::doStuff);
The 3rd connection is required because of the way QSignalMapper works: when a shortcut is activated, it will be notified to the QSignalMapper thanks to the 1st and 2nd connections, that will trigger the map() slot.
The QSignalMapper::map() slot will scan its mappings, made with the setMapping() API, whose first argument is the mapped object, and the second one is the parameter that will be used to emit the mapped() slot of the QSignalMapper, once the emitting object is identified. To do so, it uses the sender() method and simply compares the pointer returned to the mapped QObject pointers you provided as mappings.
Once the QObject is identified, QSignalMapper will emit the QSignalMapper::mapped(QObject*) signal, whose argument is the second argument given to setMapping, and in this case it the same as the first one, that is again a pointer to the QShortcut that was activated.
I used a lambda to catch this signal, and inside this lambda I simply check that the parameter given is a QShortcut pointer, and store its key sequence inside the data member of the QAction before triggering the action itself. The QAction::trigger() slot will then emit the QAction::triggered() signal that will in turn invoke your custom slot, in this case doStuff(). There you can retrieve the key sequence and do what you want with it.
So your slot implementation should look similar to this one:
void MainWindow::doStuff()
{
// use sender() to fetch data from action
QAction* act = qobject_cast<QAction*>(sender());
if (act)
{
QString sequence = act->data().toString();
// debug output will show you the triggering key sequence
qDebug() << sequence;
// use sequence string to determine which shortcut was used
// On Mike hint: better to reset data after use :)
act.setData(QVariant());
}
}
Note that I'm using a mapping based on QObject pointers. In this way you can reuse the signalMapper instance to connect events from other kind of QObjects (e.g. QPushButtons) and identify them in your custom slot as well setting a proper value for the QAction data member, that can store a generic QVariant istance.
Also when using QShortcut, be aware of their contex, that is when they are active, as it could be at widget or a window scope.
Unfortunately this pattern violates pure oop principles, but could be better than managing many actions (icon, text, tooltip etc...) for the same purpose.
EDIT: to answer comments
First of all, let me clarify that you can of course skip the use of QSignalMapper at all. This is just a possible solution (not the better, maybe an overkill... but not really worse in terms of performance).
A simpler way, as pointed by Mike in the comments consists in using lambdas for each QShotcut::activated signal, but this will result in copy/paste code, that I always try to avoid.
You can instead define a custom slot inside the MainWindow and use sender() to catch the QShortcut and prepare the action before triggering it.
Anyway, QSignalMapper IMHO, better explains what you are doing (from a semantic point of view) and is more flexible in case you need to expand the connection to other QObjects, supporting also other type of mappings.
Furthermore, but this is related to my personal taste, I like the idea to have fragments of code that are logically tied condensed into small snippets, instead of have it sparse among several slot/functions because it makes it easier to read and to trace back when I need to change it, of course only if this does not hurt the quality of code itself.
You should create a separate QAction for each shortcut and group them using a QSignalMapper.

What is the type of SLOT in Qt?

digitButtons[i] = createButton (QString::number(i), SLOT(digitClicked()));
and
Button *Calculator::createButton(const QString &text, const char *member)
{
Button *button = new Button(text);
connect(button, SIGNAL(clicked()), this, member);
return button;
}
That code is from Calculator example of Qt docs.
In this doc http://doc.qt.io/qt-4.8/signalsandslots.html, I couldn't find the type of SLOT.
Where is it mentioned that SLOt is QString?
Yes as stated by Starl1ght SLOT and SIGNAL are macro of the Meta-Object Compiler, that's why they have no data type.
In the example you have mentioned, it's used const char * because "SLOT is passed by name" to connection function. Have a look here http://doc.qt.io/qt-4.8/qobject.html#connect
Hope this briefly clarify a little bit what are SIGNALS and SLOTS.
SLOT and SIGNAL are both macro for qt MOC.
They are defined simply as:
#define SLOT(a) "1"#a
#define SIGNAL(a) "2"#a
Before compilation stage, MOC will find such lines and generate valid .moc file with c++ code, include them in your project and thus, signal\slots shall work.

Saving the content of a QLineEdit object into a string variable (C++)

I've looked around the Qt Documentation, but within my project, I'd like to having most of the non-graphical 'more thinking' part of my program be on a seperate .cpp file.
Given that, I was wanting to take the text typed into a QLineEdit object and save it as a string after the user triggers the 'returnPressed' action, but when I type:
void MainWindow::on_lineEdit_returnPressed()
{
QMessageBox msgBox;
msgBox.setText("The entry has been modified.");
msgBox.exec();
//The line which should save the contents of the QLineEdit box:
string input = QLineEdit::text();
}
...Into the template provided by the Qt Creator IDE (with all necessary slots hopefully created) The compiler returns
In member function 'void MainWindow::on_lineEdit_returnPressed()'
cannot call member function 'QString...'
... and so on.
How should I rewrite my code to do this correctly?
You must choose how to store the string. Your main options are: array of chars, std::string from the standard library, and QString from Qt. If you need to use the string in a third party library then you might need to store it in an std::string or an array of chars, but if that's not the case then I suggest that you simply use QString as it is widely used throughout Qt, although you can convert a QString to std::string or array of chars.
You must actually retrieve the text. To do this you must call the text() function on the QLineEdit instance, not on the QLineEdit class itself. All widgets can be accessed through the ui pointer. Open the designer and check the name of the line edit, the default name is lineEdit, so try replacing the line
string input = QLineEdit::text();
with the line
QString input = ui->lineEdit->text();
How about that:
lineEdit->text().toStdString()
For Qt6 this is the best solution that I found
string input = ui->lineEdit->text().toStdString();
A more developed answer from 'alagner'

Qt SLOT macro used as function argument

I'm going through the calculator example that was installed with Qt 5.1.1 and there is a private function used to create button widgets (Button inherits QToolButton):
Button *Calculator::createButton(const QString &text, const char *member)
{
Button *button = new Button(text);
connect(button, SIGNAL(clicked()), this, member);
return button;
}
The example calls the above function to create several different buttons e.g.:
createButton(tr("Clear"), SLOT(clear()));
Where void clear() was declared as a private slot. I understand what the code is trying to do but I want to know why does passing SLOT(clear()) as the const char *member work. I can't seem to find much online that would explain using SLOT like that.
As you can see in the documentation of the connect method, the function signature expects the const char* type. These are the corresponding defines from QtCore:
Q_CORE_EXPORT const char *qFlagLocation(const char *method);
...
# define SLOT(a) qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
It is a bit more complex and you can see the details in here, but I simplified it for the sake of the explanation and understanding.
This "old" signal-slot syntax is basically "string" based, and that is also the fundamental flaw with it. This was fixed in Qt 5, however. It is now closer what you seem to imply with your question so that you would rather expect it to be function or method pointers since you eventually pass such an element to the SLOT and SIGNAL moc tokens.
For completeness, the corresponding SIGNAL and SLOT tokens (i.e. Q_SLOTS, Q_SIGNALS, etc) are processed by the meta object compiler, aka. moc, the way that it puts those into the ".moc" files. You can see it yourself if you open those files up. For further details, look into the moc source code which can be found in here.