web-dev-qa-db-ja.com

他のQtウィジェットの位置を変更せずにQtウィジェットを非表示にする方法は?

QPushButtons、QLabels、その他のさまざまな楽しいQWidgetsでいっぱいのウィンドウがあり、すべてがさまざまなQLayoutオブジェクトを使用して動的にレイアウトされています。 。つまり、非表示ウィジェットはウィンドウのレイアウトで通常のスペースを占有しますが、レンダリングされません。代わりに、ユーザーはウィジェットの長方形/領域にウィンドウの背景色が表示されるだけです。

hide()および/またはsetVisible(false)は、ウィジェットをレイアウトから完全に削除し、他のウィジェットが「新たに利用可能」なスペースを占有できるようにするため、トリックを行いません。 ;私が避けたい効果。

paintEvent()(およびmousePressEvent()など)をオーバーライドするすべてのQWidget型のサブクラスをノーオペレーション(適切な場合)にできると思いますが、 3ダースの異なるQWidgetサブクラスを作成する必要のないソリューションを好むでしょう。

37
Jeremy Friesner

私が知っている唯一の適切な方法は、ウィジェットにイベントフィルターをアタッチし、再描画イベントを除外することです。ウィジェットがどれほど複雑であっても機能します-子ウィジェットを持つことができます。

以下は、完全なスタンドアロンの例です。ただし、いくつかの注意事項があり、完全にするにはさらに開発する必要があります。ペイントイベントのみがオーバーライドされるため、ウィジェットを操作できますが、エフェクトは表示されません。

マウスクリック、マウス入力/終了イベント、フォーカスイベントなどは引き続きウィジェットに到達します。おそらくそれらのイベントで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"
14
Kuba Ober

この問題は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

53

QStackedWidgetを使用できます。最初のページにボタンを配置し、2番目のページに空白のQWidgetを配置し、ページインデックスを変更して、元のスペースを保持したままボタンが消えるようにします。

7
goertzenator

私の頭の中には3つの解決策があります:

1)QWidgetをサブクラス化し、ウィジェットの描画をオン/オフにする特別な/独自のsetVisible()置換メソッドを使用します(ウィジェットを非表示にする必要がある場合、オーバーライドされたpaintEvent()メソッドで描画を無視します)。これは汚い解決策です。他の方法でできる場合は使用しないでください。

2)プレースホルダーとして QSpacerItem を使用し、非表示にするがレイアウト内の位置とサイズを保持するQWidgetの反対側に可視性を設定します。

3)子/子ウィジェットのサイズに基づいてサイズを取得/同期する特別なコンテナウィジェット(QWidgetから継承)を使用できます。

4
Viktor Benei

私は同様の問題を抱えていたので、コントロールの隣に、関心のある次元のサイズが0で、Expanding sizeTypeのスペーサーを配置することになりました。次に、コントロール自体にExpanding sizeTypeのマークを付け、ストレッチを1に設定します。そのように、表示されている場合はスペーサーよりも優先されますが、表示されない場合、スペーサーはコントロール。

3
BKewl

1つのオプションは、QLayoutItem::isEmptyに対して常にfalseを返すQWidgetItemの新しいサブクラスを実装することです。 QtのQLayoutサンプルサブクラス documentation が原因で機能すると思われます。

QLayoutItem :: isEmpty();は無視します。これは、レイアウトが非表示のウィジェットを可視として扱うことを意味します。

ただし、レイアウトにアイテムを追加するのは少し面倒です。特に、UIファイルでレイアウトを簡単に指定できるかどうかはわかりません。

2
cgmb

Kuba Ober 's answerの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)
1
glennr

QFrame をラッパーとして使用できると思います。より良いアイデアがあるかもしれませんが。

1
Yassir Ennazk

void QWidget::erase ()を試してください。 Qt 3で動作します。

0
shan

QWidget :: setWindowOpacity(0.0)が必要な場合がありますか?しかし、この方法はどこでも機能しません。

0
dmr