Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a Qt widget invisible without changing the position of the other Qt widgets?

Tags:

layout

qt

I've got a window full of QPushButtons and QLabels and various other fun QWidgets, all layed out dynamically using various QLayout objects... and what I'd like to do is occasionally make some of those widgets become invisible. That is, the invisible widgets would still take up their normal space in the window's layout, but they wouldn't be rendered: instead, the user would just see the window's background color in the widget's rectangle/area.

hide() and/or setVisible(false) won't do the trick because they cause the widget to be removed from the layout entirely, allowing other widgets to expand to take up the "newly available" space; an effect that I want to avoid.

I suppose I could make a subclass of every QWidget type that override paintEvent() (and mousePressEvent() and etc) to be a no-op (when appropriate), but I'd prefer a solution that doesn't require me to create three dozen different QWidget subclasses.

like image 799
Jeremy Friesner Avatar asked May 29 '12 07:05

Jeremy Friesner


People also ask

How do you hide the spacer in Qt?

Try putting the button you want to hide and unhide in another layout. In that layout along with the button put a spacer. Call Button hide and spacer will take over. Spacer takes over hidden button's space.

How do you hide QLabel in Qt?

You can't control the visible property, But if you want to make QLabel looks invisible as default, just set the width/height property to 0 .

How do you hide QLayout?

Transform QLayout to QWidget first, then you can use QWidget->hide().

How do you overlap widgets in Qt?

Right click on the root widget (easiest in the Object Inspector), and from the bottom of context menu, select Lay out > - Lay out in Grid. Right click on the label, and from Layout alignment > set it aligned to the corner you want.


2 Answers

This problem was solved in Qt 5.2. The cute solution is:

QSizePolicy sp_retain = widget->sizePolicy(); sp_retain.setRetainSizeWhenHidden(true); widget->setSizePolicy(sp_retain); 

http://doc.qt.io/qt-5/qsizepolicy.html#setRetainSizeWhenHidden

like image 191
Thorbjørn Martsum Avatar answered Sep 23 '22 18:09

Thorbjørn Martsum


The only decent way I know of is to attach an event filter to the widget, and filter out repaint events. It will work no matter how complex the widget is - it can have child widgets.

Below is a complete stand-alone example. It comes with some caveats, though, and would need further development to make it complete. Only the paint event is overridden, thus you can still interact with the widget, you just won't see any effects.

Mouse clicks, mouse enter/leave events, focus events, etc. will still get to the widget. If the widget depends on certain things being done upon an a repaint, perhaps due to an update() triggered upon those events, there may be trouble.

At a minimum you'd need a case statement to block more events -- say mouse move and click events. Handling focus is a concern: you'd need to move focus over to the next widget in the chain should the widget be hidden while it's focused, and whenever it'd reacquire focus.

The mouse tracking poses some concerns too, you'd want to pretend that the widget lost mouse tracking if it was tracking before. Properly emulating this would require some research, I don't know off the top of my head what is the exact mouse tracking event protocol that Qt presents to the widgets.

//main.cpp #include <QEvent> #include <QPaintEvent> #include <QWidget> #include <QLabel> #include <QPushButton> #include <QGridLayout> #include <QDialogButtonBox> #include <QApplication>  class Hider : public QObject {     Q_OBJECT public:     Hider(QObject * parent = 0) : QObject(parent) {}     bool eventFilter(QObject *, QEvent * ev) {         return ev->type() == QEvent::Paint;     }     void hide(QWidget * w) {         w->installEventFilter(this);         w->update();     }     void unhide(QWidget * w) {         w->removeEventFilter(this);         w->update();     }     Q_SLOT void hideWidget()     {         QObject * s = sender();         if (s->isWidgetType()) { hide(qobject_cast<QWidget*>(s)); }     } };  class Window : public QWidget {     Q_OBJECT     Hider m_hider;     QDialogButtonBox m_buttons;     QWidget * m_widget;     Q_SLOT void on_hide_clicked() { m_hider.hide(m_widget); }     Q_SLOT void on_show_clicked() { m_hider.unhide(m_widget); } public:     Window() {         QGridLayout * lt = new QGridLayout(this);         lt->addWidget(new QLabel("label1"), 0, 0);         lt->addWidget(m_widget = new QLabel("hiding label2"), 0, 1);         lt->addWidget(new QLabel("label3"), 0, 2);         lt->addWidget(&m_buttons, 1, 0, 1, 3);         QWidget * b;         b = m_buttons.addButton("&Hide", QDialogButtonBox::ActionRole);         b->setObjectName("hide");         b = m_buttons.addButton("&Show", QDialogButtonBox::ActionRole);         b->setObjectName("show");         b = m_buttons.addButton("Hide &Self", QDialogButtonBox::ActionRole);         connect(b, SIGNAL(clicked()), &m_hider, SLOT(hideWidget()));         QMetaObject::connectSlotsByName(this);     } };  int main(int argc, char *argv[]) {     QApplication a(argc, argv);     Window w;     w.show();     return a.exec(); }  #include "main.moc" 
like image 44
5 revs Avatar answered Sep 22 '22 18:09

5 revs