PDA

View Full Version : Grid of QComboBox



Rakma74
11th August 2012, 19:13
Dear Sirs,

I'm a newcomer in the QT4 world, and I decided to use this wonderful tool for a software GUI.

I would like to design a grid of ComboBox, to get input data. I succeded in designing it.
However, all the boxes have the same name : ComboBox, and so I don't know how to get the data of a single comboBox (see code bellow).

Could any one have an idea me to properly reach my goal ??

Thanks in advance, and Best Regards,

Stéphane (Alias Rakma74)




// Data assigmnent window filling with input sub-widgets
for (unsigned int row = 0; row < columns_number; row++)
{
Label = new QLabel(tr("Data ")+QString::number(row+1)+" :");

ComboBox = new QComboBox;
ComboBox->addItem(tr("To be set..."));
ComboBox->addItem(tr("A"));
ComboBox->addItem(tr("B"));
ComboBox->addItem(tr("C"));
ComboBox->addItem(tr("D"));

grid->addWidget(Label, row, 0);
grid->addWidget(ComboBox, row, 1);
}

//
data_assigment_input->setLayout(grid);



// Quit push button creation
QPushButton *quit = new QPushButton(tr("OK"));
// quit->setFont(QFont("Times", 18, QFont::Bold));

// Connection between widgets
connect(quit, SIGNAL(clicked()), window_data_assignment, SLOT(close()));

// Size/Position auto-managment by QVBoxLayout
QVBoxLayout *wlayout = new QVBoxLayout;
wlayout->addWidget(data_assigment_input);
wlayout->addWidget(quit);
window_data_assignment->setLayout(wlayout);

ZikO
12th August 2012, 06:16
Hi,

You could just define an array of pointers to QComboBox objects. If the column_number is a constant during compilation, you can use simple array definition. If the column_number is a variable, then you need to use new [] operator. Make sure though you are going to release memory by using delete []comboBox at the ned of application. Otherwise your memory will be slowly leaking.



// Data assigmnent window filling with input sub-widgets
QComboBox* comboBox[column_number]; // if column_number if a constant
// QComboBox** comboBox = new QComboBox*[column_number]; // this will have to be release by using delete []comboBox;
for (unsigned int row = 0; row < columns_number; row++)
{
Label = new QLabel(tr("Data ")+QString::number(row+1)+" :");

comboBox[row]->addItem(tr("To be set..."));
comboBox[row]->addItem(tr("A"));
comboBox[row]->addItem(tr("B"));
comboBox[row]->addItem(tr("C"));
comboBox[row]->addItem(tr("D"));

grid->addWidget(Label, row, 0);
grid->addWidget(comboBox[row], row, 1);
}

//
data_assigment_input->setLayout(grid);



// Quit push button creation
QPushButton *quit = new QPushButton(tr("OK"));
// quit->setFont(QFont("Times", 18, QFont::Bold));

// Connection between widgets
connect(quit, SIGNAL(clicked()), window_data_assignment, SLOT(close()));

// Size/Position auto-managment by QVBoxLayout
QVBoxLayout *wlayout = new QVBoxLayout;
wlayout->addWidget(data_assigment_input);
wlayout->addWidget(quit);
window_data_assignment->setLayout(wlayout);


A side note, I usually use size_t, which is probably an equivalent of uint, as a counter. Also, when you define a variable, try to get used to start variable names with lower case letters, like thisFormOfName. Names beginning with a capital letter should be reserved for new classes, e.g. myNewObject of MyNewClass MyWindow, MyLabel etc. Also, the variable counter_number has different form of name: my_new_varialbe. It is also a good habit to stick to one form, either new_name or newName. It is easier to analyse a code. I also add suffix describing what variable represents QLabel, QPushButton etc. For instance, quitButton, vegComboBox, firstLabel, secondLabel, firstEdit, secondEdit (QEditLine). This helps to write code when Qt suggest names.

Rakma74
12th August 2012, 09:31
Thank you so much for your answer ZikO !! It works perfectly... (OMG, I lost half a day with this trouble...)

Also, thank you very much for your advice... In fact, 'columns_number' could change during the execution, because it is the column number of the main Qtable of the application, and the ComboBox is used to setup each column, so I'm going to use your second initialisation !!

Thanks for all !!

Best Regards,

Stéphane

ZikO
12th August 2012, 12:10
Np. I am glad it's working for you. You need to remember that the second choice requires to release memory and use delete []comboBox somewhere. If you don't do it, the memory will be leaking.

What I helped with is a fundamental of C++. There are two great books that may help you to catch up. One of them is only about ISO/ANSI C++ not Qt but still useful as we use C++ in Qt. The link to a free online version is here:
http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html/
It has been released in two volumes. First is about C++ basics whereas the second about Standard Template Library, exceptions etc. still useful things but they are substituted or not really required when using Qt as Qt provide its own versions (I think wow) and solutions.

The second book was suggested in another forum about Qt (http://qt-project.org/forums/viewthread/19243/). It looks more like reference than real book but it can still be useful.
http://www.ics.com/designpatterns/

Rakma74
12th August 2012, 13:38
Thanks so much for all the links you provided me !!

That's sure that I really need to go deeper inside several C++ principles and concepts, to avoid to be limitated in my development !
I'll read them...

Thanks again !!

Stéphane

Rakma74
12th August 2012, 17:19
Dear ZikO,

I unfortunately have another problem...

I successfully performed a connection between the comboBoxes, and the slot function, I'm only able to get the argument of the selected data, but not of the current ComboBox which has been clicked...

for (unsigned int row = 0; row < columns_number; row++)
connect(ComboBox[row], SIGNAL(currentIndexChanged(int)), this, SLOT(data_assign(int)));

Would you have any tip ??

Thanks in advance, and Best Regards !!

Stéphane

Rakma74
12th August 2012, 20:34
I finally succeded in getting the needed data in declaring --- QComboBox** comboBox --- in the MainWindow class, so that I can have access to its data in the slots...

Thanks again...

Stéphane

ZikO
13th August 2012, 03:29
I successfully performed a connection between the comboBoxes, and the slot function, I'm only able to get the argument of the selected data, but not of the current ComboBox which has been clicked...

for (unsigned int row = 0; row < columns_number; row++)
connect(ComboBox[row], SIGNAL(currentIndexChanged(int)), this, SLOT(data_assign(int)));

Would you have any tip ??

I don't know how to identify an object other way than using a trick. There is one way to find out which object sent the signal. In your program, you have connected a couple of object signals into one slot. As the signal sends only index, you cannot really know which combo box sent the signal from signal/slot mechanism. This is tricky and I am not sure if it's proper technique but you may want to use QObject::sender() method that returns an address of a sender. Something like this:


void YourClassName::data_assign(int item) {
QComboBox* currentSender = static_cast<QComboBox*>(QObject::sender());
std::size_t i = 0;
while((i<rows) && (currentSender != comboBox[i])) {
i++;
}
// "i" the index of your array and points out the sender which is one of the combo box
}


The trick is that QObject::sender() provides an address but it is of a QObject type. Without casting it into QComboBox, compiler wouldn't accept it. Casting this address to QComboBox solves this problem here. However, I personally don't like it. It is very easy to mess up. I wouldn't try to do anything else then just comparing the addresses. In general, it's better to avoid things like this or connect object signals only of the same type to the same slot. Then, you can be certain of the type of a sender.

Hope it helps.

PS> I am still C++ programmer not Qt C++ >.< . Instead of using static_cast<QComboBox*> Qt provides qobject_cast<> which I believe is saver:

QComboBox* currentSender = qobject_cast<QComboBox*>(QObject::sender());

yeye_olive
13th August 2012, 09:33
There are two alternatives to using QObject::sender() (the use of which is discouraged):

1 (recommended). Use QSignalMapper. It can identify senders by an integer, a pointer, or a QString. Your slot will then be able to identify the QComboBox whose index changed, but will lose the argument index of currentIndexChanged(int index). This is not a problem since you can always get that value with QComboBox::currentIndex().

2. Subclass QComboBox and add a custom signal with an additional parameter allowing to identify the combobox, e.g. MyComboBox::currentIndexChanged(int index, int id). In MyComboBox, connect QComboBox::currentIndexChanged(int index) to a private slot which emits your custom signal with the appropriate identifier.

ZikO
13th August 2012, 12:36
2. Subclass QComboBox and add a custom signal with an additional parameter allowing to identify the combobox, e.g. MyComboBox::currentIndexChanged(int index, int id). In MyComboBox, connect QComboBox::currentIndexChanged(int index) to a private slot which emits your custom signal with the appropriate identifier.
Hi yeye_olive,

I am interested in this approach. Could you provide a simple code? I thought about that and would probably suggested it if I knew how to code custom signals / slots. Are signals also simple methods like slots?

yeye_olive
13th August 2012, 12:59
Here we go for the second approach (subclass QComboBox). I recommend the first approach (QSignalMapper), though.

Disclaimer: I did not even try to compile this code, there may be errors.



class MyComboBox : public QComboBox {
Q_OBJECT
public:
MyComboBox(int id, QWidget *parent = 0);
signals:
void currentIndexChanged(int index, int id);
protected slots:
void onCurrentIndexChanged(int index);
protected:
int m_id;
};

MyComboBox::MyComboBox(int id, QWidget *parent) : QComboBox(parent), m_id(id) {
connect(this, SIGNAL(currentIndexChanged(int)), SLOT(onCurrentIndexChanged(int)));
}

void MyComboBox::onCurrentIndexChanged(int index) {
emit currentIndexChanged(index, m_id);
}


Signals are not simple methods (although they are implemented by moc as methods). They are emitted by a component and you need to connect them to a slot to react to their emission.

ZikO
14th August 2012, 20:09
Thanks for this. I can see that your connect function has only three arguments whereas it should have 4: Widget, SIGNAL, Widget, SLOT ? Should there be this in the middle, as the third parameter?

yeye_olive
15th August 2012, 09:16
That is because I used the QObject::connect() non-static method (http://qt-project.org/doc/qt-4.8/qobject.html#connect-3), which is indeed equivalent to using the static one with "this" between the signal and the slot parameters.