如何在不改变其他Qt小部件的位置的情况下使Qt小部件不可见?

时间:2012-05-29 07:04:25

标签: qt layout

我有一个充满QPushButtons和QLabels以及其他各种有趣的QWidgets的窗口,所有这些都是使用各种QLayout对象动态布局的......而我想要做的是偶尔制作一些小部件变得无形。也就是说,隐形窗口小部件仍会占用窗口布局中的正常空间,但它们不会被渲染:相反,用户只会在窗口小部件的矩形/区域中看到窗口的背景颜色。

hide()和/或setVisible(false)不会这样做,因为它们会导致小部件完全从布局中删除,允许其他小部件扩展以占用“新可用”空间;我希望避免的效果。

我想我可以创建覆盖QWidget(和paintEvent()等)的每个mousePressEvent()类型的子类为无操作(适当时),但我会更喜欢不需要我创建三十个不同QWidget子类的解决方案。

10 个答案:

答案 0 :(得分:52)

这个问题在Qt 5.2中得到了解决。可爱的解决方案是:

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

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

答案 1 :(得分:14)

我所知道的唯一不错的方法是将事件过滤器附加到窗口小部件,并过滤掉重绘事件。无论窗口小部件有多复杂,它都可以工作 - 它可以有子窗口小部件。

以下是一个完整的独立示例。但是,它有一些警告,需要进一步开发才能完成。只覆盖了绘制事件,因此您仍然可以与窗口小部件进行交互,您将看不到任何效果。

鼠标点击,鼠标进入/离开事件,焦点事件等仍将进入窗口小部件。如果小部件依赖于在重新绘制时完成的某些事情,可能是由于在这些事件上触发的update(),可能会有麻烦。

至少你需要一个case语句来阻止更多事件 - 比如鼠标移动和点击事件。处理焦点是一个值得关注的问题:如果小部件在焦点处于隐藏状态时以及重新获得焦点时,您需要将焦点移至链中的下一个小部件。

鼠标跟踪也引起了一些担忧,你想假装小部件在以前跟踪时丢失鼠标跟踪。正确地模拟这个需要一些研究,我不知道Qt向小部件提供的确切鼠标跟踪事件协议。

//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"

答案 2 :(得分:7)

您可以使用QStackedWidget。将您的按钮放在第一页上,在第二页上放置一个空白的QWidget,然后更改页面索引以使您的按钮消失,同时保留其原始空间。

答案 3 :(得分:4)

我脑子里有3个解决方案:

1)对QWidget进行子类化并使用特殊/自己的setVisible()替换方法来打开/关闭窗口小部件的绘制(如果窗口小部件应该是隐藏的,只需忽略使用重写的paintEvent()方法的绘制)。这是一个肮脏的解决方案,如果你能以其他方式做到这一点,请不要使用它。

2)使用QSpacerItem作为占位符,并将其可见性设置为您要隐藏的QWidget的反面,但在布局中保留其位置+大小。

3)你可以使用一个特殊的容器小部件(继承自QWidget),它根据它的子/子小部件的大小来获取/同步它的大小。

答案 4 :(得分:3)

我遇到了类似的问题,最后我在我的控件旁边放了一个间隔符,在我关心的维度和Expanding sizeType中的大小为0。然后我用Expanding sizeType标记了控件本身并将其拉伸设置为1.这样,当它可见时它优先于间隔符,但当它不可见时,间隔扩展为填充通常由控件占据的空间。

答案 5 :(得分:2)

一个选项是实现QWidgetItem的新子类,它始终为QLayoutItem::isEmpty返回false。我怀疑这会因为Qt的QLayout示例子类documentation而起作用:

  

我们忽略QLayoutItem :: isEmpty();这意味着布局会将隐藏的小部件视为可见。

但是,您可能会发现在布局中添加项目有点烦人。特别是,如果您这样做,我不确定您是否可以轻松地在UI文件中指定布局。

答案 6 :(得分:1)

我相信您可以使用QFrame作为包装器。虽然可能有更好的主意。

答案 7 :(得分:1)

这是来自Kuba Ober答案的C ++ Hider类的PyQt版本。

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)

答案 8 :(得分:0)

可能是QWidget :: setWindowOpacity(0.0)是你想要的吗?但是这种方法无处不在。

答案 9 :(得分:0)

试试void QWidget::erase ()。它适用于Qt 3。