Qt and C++: Signal & Slot on multiple PushButtons - c++

i create multiple QPushButtons in the following way:
QList<QByteArray> pBList;
pBList= rec_data.split(',');
for (int i = 1; i < pBList.size() -1; i++){
QPushButton *newpB = new QPushButton(ui->verticalLayoutWidget);
newpB->setText(pBList[i]);
ui->verticalLayoutWidget->layout()->addWidget(newpB);
}
This works fine and the QPushButtons are shown on the GUI.
But how do i connect them to a clicked()-Signal and to a Slot?
I tried it this way, but this dosen't work...
QObject::connect(ui->verticalLayoutWidget->layout()->itemAt(1)->widget(), SIGNAL(clicked()),this, SLOT(_on_send_name()));
Thanks for the help

QList<QByteArray> pBList;
pBList= rec_data.split(',');
for (int i = 1; i < pBList.size() -1; i++){
QPushButton *newpB = new QPushButton(ui->verticalLayoutWidget);
newpB->setText(pBList[i]);
ui->verticalLayoutWidget->layout()->addWidget(newpB);
//This will CONNECT all buttons to a single slot
connect (newpB,&QPushButton::clicked,this,&YOUR_CLASS_NAME::_on_send_name);
}
You can use sender() inside _on_send_name to get a pointer to the clicked button. But sender() is not recommended. https://doc.qt.io/qt-5/qobject.html#sender

I would go with the QSignalMapper for your scenario.

Related

connect dynamically created buttons in qt5

I have a scenario where I am asking the user for a number between 1 and 10 and creating that number of buttons of the type QPushButton. I then want to create a function such that when I click the button the number on the button gets printed.
Just use a lambda function like this:
for (int i = 1; i < numButtons; i++)
{
QPushButton *btn = new QPushButton(...);
connect(btn, &QPushButton::clicked, [=]() {
// Do something with 'i'
}
}

qt how to connect 2D array of QPushButton and return the location

I'm new to QT Creator GUI and I'm having a hard time with Signals and Slots. I'm using QT 4.2.1 to make a word search puzzle for practicing
Below is a portion of my code, creating a 2D puzzle using vectors. I tried to use array but the size of the puzzle will be decided by the user. I had a lot of compile errors using array. So I decided to use Vector
I tried some options from this post: Get index of QPushButton on 2D array QPushButton but they all don't work.
Can you help me to see why the signals and slots are not connected? And is there a way to check if the signal is connected?
I would appreciate any helps and feedback. Thank you!
void MainWindow::displayPuzzle(QVector<QVector<QPushButton*>>& button, QVector<QVector<QChar>> puzzle2D){
widget1 = new QWidget;
QGridLayout* l = new QGridLayout;
QSignalMapper mapper;
for (int r = 0; r < puzzle2D.size(); r++) {
QVector<QPushButton*> vect;
for (int c = 0; c < puzzle2D[r].size(); c++) {
//format the button
vect.push_back(new QPushButton);
vect.last()->setText(puzzle2D[r][c]);
vect.last()->setFixedSize(60,60);
vect.last()->show();
auto pos = QString{"%1 %2"}.arg(r).arg(c);
//connect with signals and slots
mapper.connect(vect.last(), SIGNAL(clicked()), SLOT(map()));
mapper.setMapping(vect.last(), pos);
//add button to layout
l->addWidget(vect.last(), r, c);
l->setSpacing(0);
l->setMargin(0);
l->setContentsMargins(-1,-1,-1,-1);
}
button.push_back(vect);
}
connect(&mapper, SIGNAL(mapped(QString)), SLOT(puzzleClick(QString)));
widget1->setLayout(l);
}
void MainWindow::puzzleClick(QString text){
int r = text.split(" ").at(0).toInt();
int c = text.split(" ").at(1).toInt();
QMessageBox::information(this, "click:", r + " " + c );
}
The answer you are linking is very complete and useful, I think you should stick with that one. Any suggestion about those methods would be just rewriting the original answer. Maybe you can ask something more specific about what you can't understand of those options.
Another solution, not cited in that answer, would be implementing a class extending QPushButton, intercept the clicked() signal and re-emit it with the data you want. The same thing can be obtained with lambda functions in Qt5 and C++11: have a look at this: qt-custom-qpushbutton-clicked-signal the two answers to this questions explain how to do this.

Qt slots and signals: Input QPushButton as a parameter?

I currently have this functionality:
void MainWindow::onJeopardySquareClicked(){
QPushButton *temp;
for(int i =0; i < 6;i++){
for (int j = 0; j < 6; ++j) {
if(button[i][j] == sender()){
temp = button[i][j];
break;
}
}
}
temp->setStyleSheet("background-color: gray;");
}
Which is connected to a grid of QPushButtons:
QObject::connect(button[arrI][j],SIGNAL(pressed()),this, SLOT(onJeopardySquareClicked()));
Is there any ways to send the actual button so I don't need to loop through each button in the array? I don't want to subclass QPushButton if I don't have to, it seems overkill. I also want to avoid using static variables.
This is kind of what I want:
void MainWindow::onJeopardySquareClicked(QPushButton clickedButton){
clickedButton.setStyleSheet("background-color: gray;");
}
In onJeopardySquareClicked you can use QObject::sender() to find out from which QObject the signal originated. Otherwise you can also use a QSignalMapper to map the object/widget.
sender() is ugly and non-OOP. Use QSignalMapper.

No such slot button QT

for(i=0; i<height; i++)
{
for(j=0; j<width; j++)
{
button[i][j] = new QPushButton("Empty", this);
button[i][j]->resize(40, 40);
button[i][j]->move(40*j, 40*i);
connect(button[i][j], SIGNAL(clicked()), this, SLOT(changeText(button[i][j])));
}
}
If i changed function changeText with function (fullScreen for example) it works
but when i use a slot defined by me (changeText) this Error Appears and i don't know how to solve it
QObject::connect: No such slot buttons::changeText(&button[i][j])
and this is the function changeText:
void buttons::changeText(QPushButton* button)
{
button->setText("Fish");
}
NOTE: in the header file i defined the slot like this :
class buttons : public QWidget
Q_OBJECT
public slots:
void changeText(QPushButton* button);
slot can have less arguments then signal but type of arguments it has must match exactly with types of arguments in connected signal.
you can't have dynamic slot like that.
probably what you need is a QSignalMapper.
here is sample:
QSignalMapper *map = new QSignalMapper(this);
connect (map, SIGNAL(mapped(QString)), this, SLOT(changeText(QString)));
for(i=0; i<height; i++)
{
for(j=0; j<width; j++)
{
button[i][j] = new QPushButton("Empty", this);
button[i][j]->resize(40, 40);
button[i][j]->move(40*j, 40*i);
connect(button[i][j], SIGNAL(clicked()), map, SLOT(map()));
map->setMapping(button[i][j], QString("Something%1%2").arg(i).arg(j));
}
}
Probably you can remove a table.
If the SIGNAL doesn't provide certain parameter, the SLOT can't recieve it.
The signal clicked() doesn't provide any parameter. SLOTs receiving it shouldn't have any, either. In any case, you can have a SLOT receiving less parameters than the SIGNAL provides (ignoring some others), but not otherwise. You can, however, get to know the sender of the signal, cast it to QPushButton* and work on it:
void buttons::changeText()
{
QPushButton *pb = qobject_cast<QPushButton *>(sender());
if (pb){
pb->setText("fish");
} else {
qDebug() << "Couldn't make the conversion properly";
}
}
QButtonGroup is a class that has been designed as a handy collection for buttons. It give you direct access to the button which triggered the slot. It also provide you the possibility to register button with a given id. This can be useful if you want to retrieve easily some meta information from the button id.
QButtonGroup* buttongrp = new QButtonGroup();
for(i=0; i<height; i++)
{
for(j=0; j<width; j++)
{
button[i][j] = new QPushButton("Empty", this);
button[i][j]->resize(40, 40);
button[i][j]->move(40*j, 40*i);
buttongrp->addButton(button[i][j], i << 16 + j);
}
}
QObject::connect(buttongrp, SIGNAL(buttonClicked(int)),
this, SLOT(getCoordinates(int)));
QObject::connect(buttongrp, SIGNAL(buttonClicked(QAbstractButton *)),
this, SLOT(changeText(QAbstractButton * button)));
...
void MyObject::changeText(QAbstractButton * button)
{
button->setText("Fish");
}
void MyObject::getCoordinates(int id){
int i = id >> 16;
int j = ~(i << 16) & id;
//use i and j. really handy if your buttons are inside a table widget
}
Usually you don't need to connect to both slots. For the id I assumed that height and width are less that 2^16.
Retrospectively, It seems to me you are reimplementing some of the functions of the button group.

How to pass a QString to a Qt slot from a QMenu via QSignalMapper or otherwise

I have a QMenu with many submenus. These are dynamically created i.e. the names menus come from a db and created in a loop. Now i wanted to fire the same slot triggered() or similar when a menu is clicked, but i needed the QString menu name to be passed to slot so i could perform menu specific actions. I have tried this i.e. passing a QAction * to the triggered event and used setData but i am getting the run time error.
object::connect: No such signal QAction::triggered(QAction *)
for(int j=0; j<channelTypes[i].getNumChannels() ; j++){
QAction *subMenuAct = subMenu->addAction(tr(c_name)); // c_name the menu name
subMenuAct->setData(ch_name);
connect(subMenuAct, SIGNAL(triggered(QAction *)), this, SLOT(playChannel(QAction *))); // playChannel is the slot
}
void <ClassName>::playChannel(QAction *channelAction)
{
QString str = channelAction->data().toString();
qDebug() << "Selected - " << str;
}
Alternately, i have also tried QSignalMapper where signalMapper is a data member initialized in the constructor
signalMapper = new QSignalMapper(this);
and
for(int j=0; j<channelTypes[i].getNumChannels() ; j++){
QAction *subMenuAct = subMenu->addAction(tr(c_name));
connect(subMenuAct, SIGNAL(triggered()), signalMapper, SLOT(map()));
signalMapper->setMapping(subMenu, ch_name);
connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(playChannel(QString)));
}
In the second case, i don't get any error, however the slot function playChannel is not being called. Would really appreciate if some one could help resolving it.
Update 1: The only difference that i see from other examples i have seen is that usually people are connecting signals from several widgets to a single slot (say different buttons). In my case i am connecting several sub menus (with different names) to a single slot. Should this make any difference?
Update 2: It worked after the correction suggested in the solution below for QSignalMapper. Also the fact that i was using SubMenu as argument to setMapping , where as MenuAction item should have been used instead. But now i am getting event fired multiple times i.e. as many times as there are entries in the main menu for the selected sub menu category. If channel type is English (main menu) with four entries), HBO, star movies etc. (sub menu), and i choose HBO, then event is fired four times with string HBO. It works fine if i create a separate signal mapper for each submenu. But i was hoping a single mapper should be used and i am doing something incorrectly here. Some more details in the comments to the answer.
After adding the QAction to the menu, you only have to connect QMenu to the slot. You don't connect each action individually to the slot:
for(int j=0; j<channelTypes[i].getNumChannels() ; j++){
ch_name = <name from the database for the channel j>;
QAction *subMenuAct = subMenu->addAction(tr(ch_name));
subMenuAct->setData(ch_name);
}
connect(subMenu, SIGNAL(triggered(QAction *)),
this, SLOT(playChannel(QAction *)), Qt::UniqueConnection);
As I don't know how you if you delete subMenu each time the dynamic menu is filled, the Qt::UniqueConnection ensure that the slot won't be reconnected multiple times.
For the signal mapper version, you should only connect the actions to the mapper in the loop. The connection from the mapper to the slot should only be done once.
for(int j=0; j<channelTypes[i].getNumChannels() ; j++){
ch_name = <name from the database for the channel j>;
QAction *subMenuAct = subMenu->addAction(tr(ch_name));
connect(subMenuAct, SIGNAL(triggered()), signalMapper, SLOT(map()));
signalMapper->setMapping(subMenuAct, ch_name);
}
connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(playChannel(QString)));
And for that case, the slot playChannel should accept a QString instead of a QAction*.