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.
Related
On Qt doc website in QHeaderView class i found two signals with similar descriptions:
void QHeaderView::sectionDoubleClicked(int logicalIndex)
and
void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
what's the difference between the two of these? When should I use the first, and when the other?
Although the documentation strings are exactly the same,
void QHeaderView::sectionDoubleClicked(int logicalIndex)
This signal is emitted when a section is double-clicked. The section's logical index is specified by logicalIndex.
[signal]void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
This signal is emitted when a section is double-clicked. The section's logical index is specified by logicalIndex.
The signals are emitted in different cases. From KDE's copy of Qt5,
void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
{
...
int handle = d->sectionHandleAt(pos);
if (handle > -1 && sectionResizeMode(handle) == Interactive) {
emit sectionHandleDoubleClicked(handle);
...
} else {
emit sectionDoubleClicked(logicalIndexAt(e->position().toPoint()));
}
}
The documentation doesn't make it particularly clear, though, when "handles" might be present and when they aren't. At a guess, if your sections are resizable you may get a handle -- for resizing -- and then you can (double) click on either the handle or the section-body.
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();
}
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.
How would I connect a signal and slot of 2 objects when the object names, signals, and slots are all specified in a text file?
Getting the right objects names isn't an issue, since I can easily loop through an array and compare the names to the spot in the file, but there has to be some sort of way where I can return the signal and slot from the file and use it in the connect function, like:
connect(rtnObj1(line),SIGNAL(rtnSignal(line)),rtnObj2(line),SLOT(rtnSlot(line)));
where rtn functions return the object name/signal/slot, and "line" is the current QString line from the file.
The only way I know of is by literally coding in every single combination and comparing QStrings with if statements, but that would be incredibly tedious, as the amount of combinations would be incredibly high.
Note:
Here's a simplified example demonstrating essentially how this issue exists.
Frame 1:
4 QComboBoxes. The first and third hold object names, the second holds signals, the fourth holds slots. Every item is of course a QString within these lists. Hitting a button appends a new line to a file, writing the text selected from each box.
Frame 2: Has already has the required objects. Reading the file, it would match the objects defined in the list against the ones already created, and connect them as the file describes.
It's easy enough to create an object based on the data a file holds, but how would one create/pull a signal and a slot from a file?
Edit:
Unless, is one able to connect like this?
connect(objectA, "", objectB, "");
Because I just found out that my code will compile like that, however whenever I try putting in the slot or signal names I just get an error like:
QObject::connect: Use the SIGNAL macro to bind Tile::clicked
Your problem is easily solvable with one of the following static QObject::connect() method:
QMetaObject::Connection QObject::connect(
const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method,
Qt::ConnectionType type = Qt::AutoConnection)
First of all, you need pointers to sender and receiver objects. There are several ways how you can store an object pool. I would suggest to keep all objects in QHash:
QHash<QString, QObject *> m_objects; // I use QObject as a generic example
Now, it's possible to find a pointer to any object for connecting in an efficient way.
The next step would be obtaining QMetaMethod objects for sender's signal and receiver's slot from corresponding QMetaObject objects. Use QObject::metaObject() QMetaObject instances.
Here's the complete code of a function which connects two object using only string parameters:
void dynamicConnect(const QString &senderName, const QString &signalName,
const QString &receiverName, const QString &slotName)
{
QObject *emitter = m_objects.value(senderName);
int index = emitter->metaObject()
->indexOfSignal(QMetaObject::normalizedSignature(qPrintable(signalName)));
if (index == -1) {
qWarning("Wrong signal name!");
return;
}
QMetaMethod signal = emitter->metaObject()->method(index);
QObject *receiver = m_objects.value(receiverName);
index = receiver->metaObject()
->indexOfSlot(QMetaObject::normalizedSignature(qPrintable(slotName)));
if (index == -1) {
qWarning("Wrong slot name!");
return;
}
QMetaMethod slot = receiver->metaObject()->method(index);
QObject::connect(emitter, signal, receiver, slot);
}
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.