Qt issue passing arguments to slot - c++

I can't seem to pass an argument to a slot. If I don't pass an argument, the function rolls through fine. If I pass an argument (integer), I get the errors "No such name type" and "No such slot" when I compile.
In my header, I declare:
private slots:
void addButton(int);
signals:
void clicked(int)
in my Main.cpp, I do:
int count;
int count = 0;
QPushButton* button = new QPushButton("Button");
_layout->addWidget(button);
connect(button, SIGNAL(clicked(count), this, SLOT(addButton(count)));
....
void Main::addButton(int count) {
//do stuff with count
}

Sebastian is correct that you cannot do this in the way you're trying, however Qt does provide a class that gives you the functionality you want.
Check out the QSignalMapper. It allows you to associate an integer with an object/signal pair. You then connect to its signals instead of directly to the button.

The signal and the slot must have the same number and type(s) of argument(s), and you can only pass the argument(s) of the signal to the slot, not any variable or value that you want.

I can see three problems with this.
Firstly, the clicked() signal is emitted by QPushButton (with no parameters), but you're trying to redefine it in your own class (with an int parameter). If you want to do this:
SignalClass* objectWithSignals = new SignalClass;
SlotClass* objectWithSlots = new SlotClass;
connect(objectWithSignals, SIGNAL(a()), objectWithSlots, SLOT(b()));
then you can only connect to the signals already defined in SignalClass. In other words, the signal a() must belong to SignalClass, not SlotClass.
(In fact, clicked() is defined in QPushButton's base class QAbstractButton.)
Secondly, inside the connect() function, you need to specify the signal and slot signatures with their parameter types. Where you have count inside the connect() function, it should be int.
And thirdly, there's a bracket missing in your call to connect: SIGNAL(clicked(count)).
Hope that helps.

Related

How QT signals with parameters works

can someone tell me how exactly signals with parameters work? I mean... if i have declared signal f.e.:
void sign1(int)
how should i specify what integer i want to send with that signal? Also, can i declare signal with multiple arguments? Like:
void sign2(int, int)
And again... i want to send with sign2 two out of four variables that i have. Is that possible, and how it should be done? To specify my question below is a little more detailed example:
class Board
{
signals:
void clicked(int, int);
private:
int x1{4}; int x2{4}; int x3{5}; int x4{8};
}
and there is board.ui file with pushbutton. After pushbutton is clicked i want to send to the slot for example x1 and x3. Example:
connect(ui->button, SIGNAL(clicked(int, int)), obj2, slot2);
I hope that it's somehow clear. I will really appreciate your help.
QObject::connect() works like this (in the general case, not using lambda):
connect(obj1, obj1_signal, obj2, ob2_slot)
Since there is no signal clicked(int, int) in the class QPushButton (which I assume you are using), it cannot be use for the connection.
If you want to have the signal clicked(int, int) in a button, you can subclass QPushButton, add the signal, and using emit to send the signal where the click event is handled.
However, that is not a good design, since you will have to store a Board object (or at least a reference to it) in the button class, which is irrelevant to the class.
Instead, you can have a slot Board::buttonClicked(), connected to QPushButton::clicked(bool). Then in that slot, you can do emit Board::clicked(int, int).
The rule for signal/slot connection may be formulated as the following:
You can ignore signal arguments, and you cannot create slot arguments from nothing
What does it mean?
If your signal has n arguments, your slot shall have at most n arguments as well (beware of the types). See tabular below.
On first line, you have a signal with two arguments, thus, your slot can have two arguments (using all the signal arguments), or one argument (ignoring one argument of the signal) or no argument (ignoring both signal arguments)
On the second line, you have a signal valueChanged(int) with one argument. Your slot may have one or no argument (ignoring the signal argument) but may not have two
or more arguments as you cannot create values.
On the third line the signal textChanged(QString) cannot be connected with setValue(int) because we cannot create an int value from from QString.
The fourth line follow these rules. If the signal has no argument, the connected signal cannot create new arguments, thus update() is correct, setValue(int) isn't.
Another point that shall be looked at is overloading of signal/slots. It is the case where many signals/slots with the same name but with different numers or different types of arguments.
You may have the class QLCDNumber, where the slot display has many overload. In this cas, you have to explicitly defined which pair of signals slots, you would like to use as explained here
You can try out the following example:
Example :
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget *window = new QWidget();
window->setAttribute(Qt::WA_DeleteOnClose);
QVBoxLayout *topLayout = new QVBoxLayout(window);
//Set up of GUI
QSlider *slider = new QSlider(Qt::Horizontal);
slider->setRange(0, 100);
QSpinBox *spin = new QSpinBox;
spin->setReadOnly( true );
QHBoxLayout *horizontalLayout = new QHBoxLayout;
horizontalLayout->addWidget(slider);
horizontalLayout->addWidget(spin);
topLayout->addLayout(horizontalLayout);
// using pointer to member function
QObject::connect(slider, &QSlider::valueChanged,
spin, &QSpinBox::setValue);
// set the slider position and hence the QSpinBox value too
slider->setValue(40);
// Uncommenting the following connect will result in a compile time error.
// The signal passes no arguments whereas the slot is expecting a single
// argument.
// By using function pointers we get compile time parameter list checking.
// Using the old-style SIGNAL/SLOT macros this would have been detected
// as a run time warning only.
//QObject::connect(slider, &QSlider::sliderPressed,
// spin, &QSpinBox::setValue);
QTextEdit *textEdit = new QTextEdit();
textEdit->setAttribute(Qt::WA_DeleteOnClose);
// Uncommenting the following connect will result in a compile time error.
// The signal is passing an incompatible parameter to the slot.
// By using function pointers we get compile time parameter type conversion.
// Using the old-style SIGNAL/SLOT macros this would have been detected
// as a run time warning only.
//QObject::connect(slider, &QSlider::sliderMoved,
// textEdit, &QTextEdit::setFontFamily);
window->show();
return app.exec();
}

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.

Unable to add slot and connect it to a button

I gathered a code of an application called calendar from the base of examples of the Qt Framework. I am trying to learn from it and add there some functionality. The problem right now that I've got is that I want to implement two function to the two button that I created ( one for increase counting of the days and the second for decrease ).
The code that I added to the function for increasing the days is:
void MainWindow::forward(int *click_forward)
{
click_forward++;
}
and the code added to the function for decreasing the days:
void MainWindow::backwards(int *click_backwards)
{
click_backwards--;
}
In the constructor I defined a variable named click which of the int
type, and I sent this variable to the both function by reference:
forward(&click);
backward(&click);
In the header file, in the public slosts area these both functions are
defined as:
void forward(int *click_forward);
void backwards(int *click_backwards);
I also implemented two SIGNAL-SLOT connections:
QObject::connect(nextbtn, SIGNAL(clicked()), this, SLOT(forward(int
&click)));
QObject::connect(beforebtn, SIGNAL(clicked()), this,
SLOT(backwards(int &clickt)));
But for some reasons when I compile the project I receive an information that:
QObject::connect: No such slot MainWindow::forward(int &click)
QObject::connect: No such slot MainWindow::backwards(int &clickt)
I wanted to use pointers in these two functions, just to work on the original variable itself not on the copy.
Could I please ask you to point me out what I am doing wrong.
Thank you,
The problem is that your signal and slot(s) have different signatures: signal has no arguments, but slot has an argument of pointer type. Besides, even if your signals connections would work, the execution of such code wouldn't do anything useful (at least) as you modify the temporary defined variables click_backwards etc.
I would solve this in the following way:
Define the class member variables and slots:
class MainWindow
{
[..]
private slots:
void forward();
void backwards();
private:
int click_forward;
int click_backwards;
}
Define slots:
void MainWindow::forward()
{
click_forward++;
}
void MainWindow::backwards()
{
click_backwards--;
}
And finally establish connections:
QObject::connect(nextbtn, SIGNAL(clicked()), this, SLOT(forward()));
QObject::connect(beforebtn, SIGNAL(clicked()), this, SLOT(backwards()));
if you do your signals and slots like this, then you get a compiler error instead of a run time error, which i personally find very helpful since it will just tell you that they wont connect because of incompatible signals/slots
QObject::connect(nextbtn, &QPushButton::clicked, this, &MainWindow::forward);
By the way, you're not increasing the value of the integer, you're increasing the pointer.
That's a bug waiting to happen.

QMenu cannot read function Slot

My intent is create a context menu to copy the cell content to the clipboard. With the help of sender() I’m able to connect the same function to two different QTableWidget. Everything works, except for this error message:
"QObject::connect: Incompatible sender/receiver arguments
QAction::triggered(bool) --> MainWindow::copyToClipboard(QTableWidget*,int,int)"
This is the part of code that generates the error
void MainWindow::ProvideContextMenu(const QPoint& pos) // this is a slot
{
QTableWidget *tw = (QTableWidget *)sender();
int row = tw->currentRow();
int col = tw->currentColumn();
QMenu menu;
menu.addAction(QString("Test Item"), this,
SLOT(copyToClipboard(QTableWidget *, int,int)));
menu.exec(tw->mapToGlobal(pos));
}
void MainWindow::copyToClipboard(QTableWidget *tw, int row, int col) {
clipboard = QApplication::clipboard();
clipboard->setText(tw->item(row, col)->text());
}
I've been looking in the official documentation for hours, but found nothing about this. There is a solution?
From the documentation:
The signals and slots mechanism is type safe: The signature of a signal must match the signature of the receiving slot. (In fact a slot may have a shorter signature than the signal it receives because it can ignore extra arguments.) Since the signatures are compatible, the compiler can help us detect type mismatches when using the function pointer-based syntax. The string-based SIGNAL and SLOT syntax will detect type mismatches at runtime.
This is the culsprit:
menu.addAction(QString("Test Item"), this,
SLOT(copyToClipboard(QTableWidget *, int,int)));
You cannot have non-matching signal-slot parameters like that. You can only connect slots that have no parameters or one boolean to the triggered(bool) signal. You have to reconsider your design.

Pointer to a slot function

I have some slot function defined in my class which do some actions. I wanted to create a possibility to allow the user of my class to define his own slot function (replacing the function from my class for his own). I tried to achieve it by pointer to a slot function this way:
class asd {
Q_OBJECT
private:
void ( asd::*m_funcTrigger )( QAction* );
public:
asd();
// and some method to pass the pointer
private slots:
void actionTrigger( QAction* );
};
the constructor:
asd::asd() {
// set the slot function from class as default
m_funcTrigger = &asd::actionTrigger;
// m is a QMenu object
connect(m, SIGNAL(triggered(QAction*)), this, SLOT(m_funcTrigger(QAction*)));
}
actionTrigger's implementation is not important I think.
So, when I put actionTrigger into the SLOT() it works ok. When I put there the m_funcTrigger it doesn't - nothing happens (the slot is not found by the Qt). I was sure that it is beacuse the pointer is not in the slots section in the class, so I just put it there:
private slots:
void ( asd::*m_funcTrigger )( QAction* );
void actionTrigger( QAction* );
but I got strange error:
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(151,5): error MSB6006: "cmd.exe" exited with code 1.
I completely don't know how to deal with this.
EDIT:
I think the reason why it's not found by the Qt:
From what I have read over the Internet, the SLOT() just returns a simple const char* which includes identifier name of the method passed to the SLOT, Therefore the Qt completely doesn't know what the pointer is pointing at. It just looks after the m_funcTrigger( QAction* ) function.
I created another solution (which works I will put it here later I'm currently at work) that requires the user of the class to pass a SLOT(hisOwnFunction()) into the function which sets the slot function. Because the class uses signal-slots idea, so it's Qt dependent and I think because of that it's ok to pass SLOT there instead of a pointer. What do you think?
You can make your slot virtual, so derived class can override it.
You can call m_funcTrigger in your slot by yourself:
private slots:
void actionTrigger_slot( QAction* a)
{
m_funcTrigger(a);
}