4

In my GUI, I would like to add a QComboBox to a verticalLayout programmatically based on a a signal triggered by specific action. The following code works fine and the widget is added:

QComboBox* userOptions = new QComboBox();
ui->verticalLayout_13->addWidget(userOptions);

However, this way the widget is always added to the end of the layout.

My question is: how to position the added QComboBox to the verticalLayout in alignment to another widget in the same layout ? (i.e.: above the "Go" push button for example)

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
McLan
  • 2,552
  • 9
  • 51
  • 85
  • 1
    Do you really need to create a new QComboBox ? Because you could let it in the layout and set it visible or not whether you need it or not. – ymoreau Apr 20 '16 at 14:54
  • @YMoreau: I just used the visibility property as you suggested. Thanks – McLan Apr 20 '16 at 18:05

2 Answers2

4

There doesn't seem to be a way to explicitly insert an item in a layout where you want it to be.

You have a few choices to achieve that the "hard" way:

  • use QLayout::takeAt(int index) to take all items after the index you want to insert at, insert your item, then insert back the taken items.
  • create a placeholder widget which you can use to reserve an index in the layout, then you don't insert the item in the layout, but in a layout nested inside the placeholder widget. Without an item, the placeholder widget takes no space, and expands to accommodate whatever is put into it.
  • implement your own QLayout subclass which supports inserting at a specific index. There are several functions you will have to implement.

EDIT: An omission on my end, as Kuba Ober noted, most of the concrete layout implementations support inserting at a specific index, for example QBoxLayout derived have insert methods which pass an index as a parameter.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • It looks like a lot of unnecessary "hard" work for what I thought it will be a nice and easy skill to learn. As far as my application is concern, I will stick to the first line in your answer (for now at least), and use visibility property as "Y Moreau" suggested. Thanks – McLan Apr 20 '16 at 18:05
  • You might want to look at the documentation of concrete layouts again. Yes, there's no way of doing it using only the `QLayout` interface, but all concrete layouts definitely support it. `QGridLayout` doesn't let you insert directly, but you can easily enough shuffle existing items to make room for the new one. – Kuba hasn't forgotten Monica Apr 20 '16 at 21:14
  • @KubaOber - yes, it is true, I haven't noticed before, mainly because I haven't used layouts all that much, and most of the time - in a static scenario. I am mostly using QML, and QML layouts don't have that at all. – dtech Apr 20 '16 at 22:18
  • In QML if you don't use layouts it's IIRC possible to rebind the positions to change the visual layout on the fly, too. – Kuba hasn't forgotten Monica Apr 21 '16 at 12:49
1

First, iterate a layout to find the index of the reference item you're inserting relative to. Then use the concrete layout's specific widget insertion/addition functionality.

Since you presumably use a QBoxLayout, you'd use its insertWidget method to insert a widget.

// https://github.com/KubaO/stackoverflown/tree/master/questions/insert-widget-36746949
#include <QtWidgets>

namespace SO { enum InsertPosition { InsertBefore, InsertAfter }; }

bool insertWidget(QBoxLayout * layout, QWidget * reference, QWidget * widget,
                  SO::InsertPosition pos = SO::InsertBefore, int stretch = 0,
                  Qt::Alignment alignment = 0) {
   int index = -1;
   for (int i = 0; i < layout->count(); ++i)
      if (layout->itemAt(i)->widget() == reference) {
         index = i;
         break;
      }
   if (index < 0) return false;
   if (pos == SO::InsertAfter) index++;
   layout->insertWidget(index, widget, stretch, alignment);
   return true;
}

Similar functions can be easily devised for QFormLayout, QGridLayout and QStackedLayout.

And a test harness:

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QWidget w;
   QVBoxLayout l{&w};
   QLabel first{"First"};
   QLabel second{"Second"};
   l.addWidget(&first);
   l.addWidget(&second);
   insertWidget(&l, &first, new QLabel{"Before First"}, SO::InsertBefore);
   insertWidget(&l, &second, new QLabel{"After Second"}, SO::InsertAfter);
   w.show();
   return app.exec();
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313