PDA

View Full Version : Several GroupBoxes with Radiobutton function



KeineAhnung
1st May 2014, 10:05
Hi,

I have several group boxes (dynamical generated). The group boxes have been modified in a custom class and are now cards. I used the group box because with the border and the checkbox I only needed to add some content (image, text) to have my card.
That my program runs only one card is allowed to be active, like radio buttons. My plan was to create a signal that is sent to all boxes and tells them to turn off. The signal contains the sender index so that the sending box knows not to turn off as well. My problem is to connect the toggled(bool) signal from the QGroupBox with my custom signal. Can I somehow wire these two signals? Or asked differently how do I know where the toggled(bool) signal came from?
Here is the card class:


#ifndef SATIONCARD_H
#define SATIONCARD_H

#include <QLabel>
#include <QGroupBox>
#include <QLayout>

class StationCard : public QGroupBox{
Q_OBJECT
public:
StationCard();
QLabel *propLabel;
int index;
bool on;

public slots:
void turnOff(int actIndex);

signals:
void toggled(int index, bool on);

private:

};

#endif // SATIONCARD_H




#include "stationcard.h"

StationCard::StationCard(){
QHBoxLayout *layout = new QHBoxLayout;
propLabel = new QLabel();
layout->addWidget(propLabel, 10, Qt::AlignRight);
setLayout(layout);

on = isChecked();
if(on){
emit toggled(index, on);
emit turnOff(index);
}
}

void StationCard::turnOff(int actIndex){
if(actIndex != index){
setChecked(false);
}
}

And the connection in my main window:


int k = 0;
for(int i = 0; i < 2; ++i){
for(int j = 0; j < 4; ++j){
CardVector[k] = new SationCard;
CardVector[k]->index = k;
QObject::connect(CardVector[k], SIGNAL(toggled(int, bool)), this, SLOT(on_stationCard_toggled(int, bool)));
ui->cardLayout->addWidget(enemyVector[k], i, j);
k++;
}
}

anda_skoa
1st May 2014, 13:10
You need to connect the groupbox's toggled(bool) signal to slot in your StationCard class. In that slot you emit your signal with it two arguments.

Btw, there is no point in emitting signals from inside the constructor. At this time nobody can be connected to the object yet.

Cheers,
_

KeineAhnung
1st May 2014, 13:24
Hi ,


is that really so easy ? If I connect toggled(bool) with toggled(int, bool) all cards will sent their signal and I still don't know where the signal came from.

anda_skoa
1st May 2014, 14:39
No.

You connect to a slot and make it emit the other signal.
This indirection is needed because your second signal has more arguments than the first.

Cheers,
_

KeineAhnung
1st May 2014, 15:00
Okay I will try that. But I am still suspicious that it will work. If I connect toggle(bool) with toggle(bool , int) all cards will answer. That is okay as long as no other card is selected but if another card is already selected I will not be able to know where the signal came from. Or do I see that wrong?

anda_skoa
1st May 2014, 15:46
As I already said, you can't connect toggled(bool) to toggled(int, bool) since the latter has more arguments than the former.
Hence the need to go through a slot.

Cheers,
_

KeineAhnung
1st May 2014, 18:10
Okay here is how I made it. The code works but I do not really understand why.
The class:


#ifndef STATIONCARD_H
#define STATIONCARD_H

#include <QLabel>
#include <QGroupBox>
#include <QLayout>
#include <QDebug>

class StationCard : public QGroupBox{
Q_OBJECT
public:
StationCard();
QLabel *propLabel;
int index;
bool on;

public slots:
void turnOff(int actIndex);
void sentToggle(bool on);

signals:
void toggled(int index);

private:

};

#endif // STATIONCARD_H





#include "stationcard.h"

StationCard::StationCard(){
QHBoxLayout *layout = new QHBoxLayout;
propLabel = new QLabel();
layout->addWidget(propLabel, 10, Qt::AlignRight);
setLayout(layout);
}

void StationCard::turnOff(int actIndex){
if(actIndex != index){
setChecked(false);
}
}

void StationCard::sentToggle(bool on){
qDebug() << "Signal comes.";
if(on){
emit toggled(index);
emit turnOff(index);
}
}

And the wirering (I also added a signal to my main window: void turnOff(int index);)


QObject::connect(stationVector[k], SIGNAL(toggled(bool)), stationVector[k], SLOT(sentToggle(bool)));
QObject::connect(stationVector[k], SIGNAL(toggled(int)), this, SLOT(on_stationCard_toggled(int)));
QObject::connect(this, SIGNAL(turnOff(int)), stationVector[k], SLOT(turnOff(int)));
[...]
void MainWindow::on_stationCard_toggled(int index){
activeIndex = index;
emit turnOff(index);
qDebug() << activeIndex;
}

The first connection connects the basic toggle signal with my custom signal. I thought if I do this all cards would listen if a toggle signal is sent but since the code works I think only signal from card k is connected to the slot from card k. Why is this so? All cards are identical, how does the compiler know that only signal and slot from k should be used? The slot from k+1 is exactly the same!
The second connection connects the card to my main window and sends active card index.
The third connection connects my main window to the cards and tells them to un-select if they are not the active card.

anda_skoa
1st May 2014, 21:18
Why is this so?

Each "k" value addresses a different object.


All cards are identical, how does the compiler know that only signal and slot from k should be used? The slot from k+1 is exactly the same!

The method/slot is the same, but the object is not.
The "this" context is different for every StationCard object.

You can actually make that connection internal to StationCard, it is of no interest outside



private slots:
void sentToggle(bool on);



StationCard::StationCard(){
QHBoxLayout *layout = new QHBoxLayout;
propLabel = new QLabel();
layout->addWidget(propLabel, 10, Qt::AlignRight);
setLayout(layout);

connect(this, SIGNAL(toggled(bool)), this, SLOT(sentToggle(bool)));
}


Cheers,
_

KeineAhnung
1st May 2014, 22:37
Okay I see. Thanks for the explanation.

I just found a flaw in the code. It is possible to de-select all cards but then the activeIndex would still point to the last active card. To set it back to -1 I did these changes:
I added a private slot in my main window: void resetActiveIndex() and a corresponding signal in my card: void resetActiveIndex(). In the card .cpp I changed the toggled function to this:


void StationCard::sentToggle(bool on){
if(on){
emit toggled(index);
}else{
emit resetActiveIndex();
}
}

The slot functions in the main window I changed to this:


void MainWindow::on_stationCard_toggled(int index){
emit turnOff(index);
activeIndex = index;
qDebug() << activeIndex;
}
void MainWindow::resetActiveIndex(){
activeIndex = -1;
qDebug() << activeIndex;
}

It is important to send the signal in on_stationCard_toggled before the activeIndex is set. Otherwise a change of active cards would always result in -1. And of course I wired the new functions:

QObject::connect(stationVector[k], SIGNAL(resetActiveIndex()), this, SLOT(resetActiveIndex()));

I hope this example is still understandable. If not let me know and I try to write a nicer example. Or maybe someone of the pros likes to round it up in nicer syntax. ;-)