Rearranging/shifting widgets in grid layout



  • First of all I'm pretty much a noob at QT at this point so I went the easy route and created my whole UI using designer.

    I have a grid layout with 2 columns and multiple rows. Each cell contains a group box with some forms. I want to add an option of toggling the visibility of every group box widget but I ran into a problem with rearranging them into a grid after some of them are removed. Hiding and deleting widgets works and scales the grid properly if I remove the entire row, but if I remove only one of the widgets in a row I'm left with a "hole" in my layout.

    Figuring I could just write a loop that will shift widgets as needed I tried this:

    @QLayoutItem *child = ui->grid>takeAt(0);

    delete child->widget();
    delete child;

    ui->grid->addWidget(ui->grid->itemAt(0)->widget(), 0, 0, 0, 0);@

    This however breaks the layout completely. Second row moves up and overlaps the widget I moved. I tried a few different things and it seems like addWidget creates a new grid every time it's called.

    Am I missing something or should I just give up and scrap my UI in favor of dynamically creating widgets every time settings are changed?



  • If it's something that happens only every once in a while (startup, personal settings change) I'd go for dynamic recreation. If it is something that happens a lot I would reconsider the setup of the UI. From your description I don't really understand why you would want to do exactly this. Maybe if you post a more extended explanation or possibly a small example (movie/program) of what you want, we can help finding the best approach for you.



  • If recreation, mentioned by Franzk, is not what you want, then i have another idea(s).

    Maybe right place to implement this, would be to subclass Q(Grid)Layout?
    Because i think, your question is general enought to implement it as container layout manager. But i don't know what needs to be done to achieve it, and maybe it could be not so trivial.

    Otherwise i would do it creating some kind of 'rearrange' method. Which will go through all of child widgets, remove them from grid layout, walk through them again and based on condition place them in gridlayout at correct position. Something like (dirty):
    @
    class AutoArrangedWidget : public QWidget
    {
    public: // maybe slots
    void addWidget(QWidget*);
    void removeWidget(QWidget*);
    void toggleWidget(QWidget*);

    private:
    QList<QWidget*> childs;
    int columns = 2;

    void rearrange() {
    for(int i = 0; i < layout->count(); ++i) {
    this->layout->removeItem( layout->itemAt(i) );
    }

    int visible = 0;
    foreach(QWidget* w, childs) {
    if(w->isVisible()) {
    this->layout->addWidget(
    w,
    visible/columns,
    visible%columns);
    ++visible;
    }
    }
    }
    }
    @

    In your example, you can manualy prefill childs with your widgets from designer.
    @
    // ... construct of your class with designer ui
    childs << ui->w1 << ui->w2 << ui->w3;
    rearrange();
    // and if this is in your mainwindow
    // (or you don't have place in code where you can call rearrange after your gui was displayed (isVisible))
    // then at end of mainwin constructor can help you
    QTimer::singleShot(300, this, SLOT(rearrange()));
    @

    Of course this will not behave right, when you will "setVisible" child widgets from outside of this class (there is no signal QWidget->visibilityChanged etc...). If you want to make it based on "exists/destroyed" instead of visibility, then you can probably use destroyed signal to remove widget from childs (in my example) and initiate reordering.

    This, of course, would best work with own subclass of QWidget ... like AutoArrangableItem (which emit signals like about visibility/existence/whatever). Or at least it would be better to use is/setEnabled instead of visibility (disabled widgets would be hidden/removed by rearrange).

    And of course based on your conditions to show/hide widgets, it could/should be written better, eg. to not remove/insert widgets already in correct position (part above changed widget?).



  • If the whole thing doesn't need to look like a table, you could also consider using layouts in layouts. The whole thing would then end up in a QVBoxLayout, with QHBoxLayouts embedded therein. Your widgets would then be residing in the QHBoxLayouts.



  • Thanks for your input guys. After weighing my options I decided to go for dynamic recreation, it will occur rarely enough (startup and settings change, although it's possible someone will change settings multiple times during use it's still a relatively uncommon event) to not have a performance hit. I like the idea of subclassing QGrid but it's a fairly complex task and bit of an overkill in this specific case I think. Also, HBox/VBox idea is great but I would like to keep the ordering of cells in this case.

    As I said, I'm still learning and I was unaware of this specific problem when I was starting so rearranging it out of a grid would mean a major UI overhaul, but I'll be careful with grids in the future. At least I'll learn how to rely on UI designer less now so it's all good :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.