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

前端 未结 10 2342
醉梦人生
醉梦人生 2020-12-01 10:09

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 lik

相关标签:
10条回答
  • 2020-12-01 10:48

    One option is to implement a new subclass of QWidgetItem that always returns false for QLayoutItem::isEmpty. I suspect that will work due to Qt's QLayout example subclass documentation:

    We ignore QLayoutItem::isEmpty(); this means that the layout will treat hidden widgets as visible.

    However, you may find that adding items to your layout is a little annoying that way. In particular, I'm not sure you can easily specify layouts in UI files if you were to do it that way.

    0 讨论(0)
  • 2020-12-01 10:52

    Here's a PyQt version of the C++ Hider class from Kuba Ober's answer.

    class Hider(QObject):
        """
        Hides a widget by blocking its paint event. This is useful if a
        widget is in a layout that you do not want to change when the
        widget is hidden.
        """
        def __init__(self, parent=None):
            super(Hider, self).__init__(parent)
    
        def eventFilter(self, obj, ev):
            return ev.type() == QEvent.Paint
    
        def hide(self, widget):
            widget.installEventFilter(self)
            widget.update()
    
        def unhide(self, widget):
            widget.removeEventFilter(self)
            widget.update()
    
        def hideWidget(self, sender):
            if sender.isWidgetType():
                self.hide(sender)
    
    0 讨论(0)
  • 2020-12-01 10:56

    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"
    
    0 讨论(0)
  • 2020-12-01 10:56

    I believe you could use a QFrame as a wrapper. Although there might be a better idea.

    0 讨论(0)
提交回复
热议问题