QPushButtons、QLabels、その他のさまざまな楽しいQWidgetsでいっぱいのウィンドウがあり、すべてがさまざまなQLayout
オブジェクトを使用して動的にレイアウトされています。 。つまり、非表示ウィジェットはウィンドウのレイアウトで通常のスペースを占有しますが、レンダリングされません。代わりに、ユーザーはウィジェットの長方形/領域にウィンドウの背景色が表示されるだけです。
hide()
および/またはsetVisible(false)
は、ウィジェットをレイアウトから完全に削除し、他のウィジェットが「新たに利用可能」なスペースを占有できるようにするため、トリックを行いません。 ;私が避けたい効果。
paintEvent()
(およびmousePressEvent()
など)をオーバーライドするすべてのQWidget
型のサブクラスをノーオペレーション(適切な場合)にできると思いますが、 3ダースの異なるQWidget
サブクラスを作成する必要のないソリューションを好むでしょう。
私が知っている唯一の適切な方法は、ウィジェットにイベントフィルターをアタッチし、再描画イベントを除外することです。ウィジェットがどれほど複雑であっても機能します-子ウィジェットを持つことができます。
以下は、完全なスタンドアロンの例です。ただし、いくつかの注意事項があり、完全にするにはさらに開発する必要があります。ペイントイベントのみがオーバーライドされるため、ウィジェットを操作できますが、エフェクトは表示されません。
マウスクリック、マウス入力/終了イベント、フォーカスイベントなどは引き続きウィジェットに到達します。おそらくそれらのイベントで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"
この問題は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
QStackedWidgetを使用できます。最初のページにボタンを配置し、2番目のページに空白のQWidgetを配置し、ページインデックスを変更して、元のスペースを保持したままボタンが消えるようにします。
私の頭の中には3つの解決策があります:
1)QWidgetをサブクラス化し、ウィジェットの描画をオン/オフにする特別な/独自のsetVisible()置換メソッドを使用します(ウィジェットを非表示にする必要がある場合、オーバーライドされたpaintEvent()メソッドで描画を無視します)。これは汚い解決策です。他の方法でできる場合は使用しないでください。
2)プレースホルダーとして QSpacerItem を使用し、非表示にするがレイアウト内の位置とサイズを保持するQWidgetの反対側に可視性を設定します。
3)子/子ウィジェットのサイズに基づいてサイズを取得/同期する特別なコンテナウィジェット(QWidgetから継承)を使用できます。
私は同様の問題を抱えていたので、コントロールの隣に、関心のある次元のサイズが0で、Expanding
sizeTypeのスペーサーを配置することになりました。次に、コントロール自体にExpanding
sizeTypeのマークを付け、ストレッチを1に設定します。そのように、表示されている場合はスペーサーよりも優先されますが、表示されない場合、スペーサーはコントロール。
1つのオプションは、QLayoutItem::isEmpty
に対して常にfalseを返すQWidgetItemの新しいサブクラスを実装することです。 QtのQLayoutサンプルサブクラス documentation が原因で機能すると思われます。
QLayoutItem :: isEmpty();は無視します。これは、レイアウトが非表示のウィジェットを可視として扱うことを意味します。
ただし、レイアウトにアイテムを追加するのは少し面倒です。特に、UIファイルでレイアウトを簡単に指定できるかどうかはわかりません。
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)
QFrame をラッパーとして使用できると思います。より良いアイデアがあるかもしれませんが。
void QWidget::erase ()
を試してください。 Qt 3で動作します。
QWidget :: setWindowOpacity(0.0)が必要な場合がありますか?しかし、この方法はどこでも機能しません。