J'ai une fenêtre pleine de QPushButtons et QLabels et de divers autres QWidgets amusants, tous disposés dynamiquement en utilisant divers objets QLayout
... et ce que j'aimerais faire, c'est que de temps en temps certains de ces widgets deviennent invisibles . Autrement dit, les widgets invisibles occuperaient toujours leur espace normal dans la disposition de la fenêtre, mais ils ne seraient pas rendus: à la place, l'utilisateur verrait simplement la couleur d'arrière-plan de la fenêtre dans le rectangle/la zone du widget.
hide()
et/ou setVisible(false)
ne fera pas l'affaire car ils entraînent la suppression complète du widget de la mise en page, permettant à d'autres widgets de s'étendre pour occuper l'espace "nouvellement disponible" ; un effet que je veux éviter.
Je suppose que je pourrais faire une sous-classe de chaque QWidget
type qui remplace paintEvent()
(et mousePressEvent()
et etc) pour être un no-op (le cas échéant), mais je Je préférerais une solution qui ne nécessite pas que je crée trois douzaines de sous-classes QWidget
différentes.
La seule manière décente que je connaisse est d'attacher un filtre d'événements au widget et de filtrer les événements de repeinture. Cela fonctionnera quelle que soit la complexité du widget - il peut avoir des widgets enfants.
Vous trouverez ci-dessous un exemple autonome complet. Il est livré avec quelques mises en garde, cependant, et aurait besoin d'être développé pour être complet. Seul l'événement Paint est remplacé, vous pouvez donc toujours interagir avec le widget, vous ne verrez aucun effet.
Les clics de souris, les événements d'entrée/sortie de souris, les événements de focus, etc. continueront à accéder au widget. Si le widget dépend de certaines choses qui sont effectuées lors d'un repeint, peut-être en raison d'une mise à jour () déclenchée sur ces événements, il peut y avoir des problèmes.
Au minimum, vous auriez besoin d'une déclaration de cas pour bloquer plus d'événements - par exemple, déplacer la souris et cliquer sur des événements. La gestion du focus est une préoccupation: vous devez déplacer le focus vers le widget suivant de la chaîne si le widget est masqué pendant qu'il est focalisé et chaque fois qu'il réacquiert le focus.
Le suivi de la souris pose également quelques problèmes, vous voudrez peut-être prétendre que le widget a perdu le suivi de la souris s'il le faisait auparavant. Émuler correctement cela nécessiterait des recherches, je ne sais pas du haut de ma tête quel est le protocole d'événement de suivi de souris exact que Qt présente aux 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"
Ce problème a été résolu dans Qt 5.2. La solution mignonne est:
QSizePolicy sp_retain = widget->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
widget->setSizePolicy(sp_retain);
http://doc.qt.io/qt-5/qsizepolicy.html#setRetainSizeWhenHidden
Vous pouvez utiliser un QStackedWidget. Placez votre bouton sur la première page, un QWidget vierge sur la seconde et modifiez l'index de page pour faire disparaître votre bouton tout en conservant son espace d'origine.
J'ai 3 solutions en tête:
1) Sous-classe votre QWidget et utilisez une méthode de remplacement spéciale/propre setVisible () qui active/désactive la peinture du widget (si le widget doit être invisible, ignorez simplement la peinture avec une méthode paintEvent () remplacée). C'est une sale solution, ne l'utilisez pas si vous pouvez le faire autrement.
2) Utilisez un QSpacerItem comme espace réservé et définissez sa visibilité à l'opposé du QWidget que vous souhaitez masquer mais conservez sa position + sa taille dans la mise en page.
3) Vous pouvez utiliser un widget conteneur spécial (hérité de QWidget) qui obtient/synchronise sa taille en fonction de la taille de ses widgets enfant/enfants.
J'ai eu un problème similaire et j'ai fini par mettre un espaceur à côté de mon contrôle avec une taille de 0 dans la dimension qui m'intéressait et un Expanding
sizeType. Ensuite, j'ai marqué le contrôle lui-même avec un Expanding
sizeType et défini son étirement sur 1. De cette façon, lorsqu'il est visible, il a priorité sur l'espaceur, mais lorsqu'il est invisible, l'espaceur se développe pour remplir l'espace normalement occupé par le contrôle.
Une option consiste à implémenter une nouvelle sous-classe de QWidgetItem qui renvoie toujours false pour QLayoutItem::isEmpty
. Je soupçonne que cela fonctionnera en raison de l'exemple de sous-classe QLayout de Qt documentation :
Nous ignorons QLayoutItem :: isEmpty (); cela signifie que la mise en page traitera les widgets cachés comme visibles.
Cependant, vous trouverez peut-être que l'ajout d'éléments à votre mise en page est un peu ennuyeux de cette façon. En particulier, je ne suis pas sûr que vous puissiez facilement spécifier des dispositions dans les fichiers d'interface utilisateur si vous le faisiez de cette façon.
Voici une version PyQt de la classe C++ Hider de la réponse de Kuba Ober .
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)
Je pense que vous pouvez utiliser un QFrame comme wrapper. Bien qu'il puisse y avoir une meilleure idée.
Essayez void QWidget::erase ()
. Cela fonctionne sur Qt 3.
Peut-être que QWidget :: setWindowOpacity (0.0) est ce que vous voulez? Mais cette méthode ne fonctionne pas partout.