Подтвердить что ты не робот

Как сделать виджет Qt невидимым без изменения позиции других виджетов Qt?

У меня есть окно, полное QPushButtons и QLabels, а также различные другие интересные QWidgets, все из которых динамически создаются с использованием различных объектов QLayout... и то, что я хотел бы сделать, иногда делает некоторые из этих виджетов невидимыми, То есть невидимые виджеты по-прежнему занимают свое нормальное пространство в макете окна, но они не будут отображаться: вместо этого пользователь просто увидит цвет фона окна в прямоугольнике/области виджета.

hide() и/или setVisible(false) не будет делать трюк, потому что они полностью исключают виджет из макета, позволяя другим виджетам расширяться, чтобы заняться "новым доступным" пространством; эффект, который я хочу избежать.

Предположим, что я мог бы создать подкласс каждого типа QWidget, который переопределяет paintEvent()mousePressEvent() и т.д.) как необязательный (если необходимо), но я бы предпочел решение, t потребовать от меня создать три десятка различных подклассов QWidget.

4b9b3361

Ответ 1

Единственный достойный способ, которым я знаю, - присоединить фильтр событий к виджету и отфильтровать события перерисовки. Он будет работать независимо от того, насколько сложна виджет - он может иметь дочерние виджеты.

Ниже приведен полный автономный пример. Однако это связано с некоторыми предостережениями, и для его завершения потребуется дальнейшее развитие. Только событие рисования отменено, поэтому вы все еще можете взаимодействовать с виджетами, вы просто не увидите никаких эффектов.

Клики мыши, события ввода/выключения мыши, события фокуса и т.д. все равно попадут в виджет. Если виджет зависит от определенных вещей, выполняемых при перерисовке, возможно, из-за срабатывания 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"

Ответ 3

Вы можете использовать QStackedWidget. Поместите свою кнопку на первой странице, пустой QWidget на второй и измените индекс страницы, чтобы ваша кнопка исчезла, сохраняя исходное пространство.

Ответ 4

У меня есть 3 решения:

1) Подкласс QWidget и использование специального/собственного метода замены setVisible() witch включает/выключает рисование виджета (если виджет должен быть невидимым, просто игнорируйте картину с помощью метода overridden paintEvent()). Это грязное решение, не используйте его, если вы можете сделать это другими способами.

2) Используйте QSpacerItem в качестве заполнителя и установите видимость на противоположность QWidget, который вы хотите скрыть, но сохраните его позицию + размер в макете.

3) Вы можете использовать специальный виджет контейнера (наследовать от QWidget), который получает/синхронизирует его размер на основе размера дочерних элементов/детей.

Ответ 5

У меня была аналогичная проблема, и я закончил размещение проставки рядом с моим элементом управления размером 0 в размерности, о которой я заботился, и Expanding sizeType. Затем я сам пометил элемент управления с типом Expanding sizeType и установил его растяжку на 1. Таким образом, когда он виден, он имеет приоритет над разделителем, но когда он невидим, проставка расширяется, чтобы заполнить пространство, обычно занимаемое элементом управления.

Ответ 6

Один из вариантов - реализовать новый подкласс QWidgetItem, который всегда возвращает false для QLayoutItem::isEmpty. Я подозреваю, что это сработает из-за подкласса Qt QLayout подкласса документация:

Мы игнорируем QLayoutItem:: isEmpty(); это означает, что макет будет рассматривать скрытые виджеты как видимые.

Однако вы можете обнаружить, что добавление элементов в макет немного раздражает. В частности, я не уверен, что вы можете легко указать макеты в файлах пользовательского интерфейса, если вы должны сделать это таким образом.

Ответ 7

Я считаю, что вы можете использовать QFrame в качестве обертки. Хотя может быть и лучшая идея.

Ответ 8

Здесь приведена версия PyQt класса С++ Hider из Куба Обер.

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)

Ответ 9

Может быть QWidget:: setWindowOpacity (0.0) - это то, что вы хотите? Но этот метод не работает повсюду.

Ответ 10

Попробуйте void QWidget::erase (). Он работает на Qt 3.