qt - font - qwidget background
¿Cómo hacer invisible un widget Qt sin cambiar la posición de los otros widgets Qt? (10)
Tengo una ventana llena de QPushButtons y QLabels y varios otros QWidgets divertidos, todos expuestos dinámicamente usando varios objetos QLayout
... y lo que me gustaría hacer es ocasionalmente hacer que algunos de esos widgets se vuelvan invisibles. Es decir, los widgets invisibles seguirían ocupando su espacio normal en el diseño de la ventana, pero no se representarían: en su lugar, el usuario solo vería el color de fondo de la ventana en el rectángulo / área del widget.
hide()
y / o setVisible(false)
no harán el truco porque hacen que el widget se elimine por completo del diseño, permitiendo que otros widgets se expandan para ocupar el espacio "recién disponible"; un efecto que quiero evitar
Supongo que podría hacer una subclase de cada tipo de QWidget
que anule paintEvent()
(y mousePressEvent()
y etc.) para que sea no mousePressEvent()
(cuando corresponda), pero preferiría una solución que no me requiera crear tres docenas de diferentes subclases de QWidget
.
Aquí hay una versión PyQt de la clase C ++ Hider de la respuesta 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)
Creo que podrías usar un QFrame como envoltorio. Aunque podría haber una mejor idea.
Este problema fue resuelto en Qt 5.2. La linda solución es:
QSizePolicy sp_retain = widget->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
widget->setSizePolicy(sp_retain);
http://doc.qt.io/qt-5/qsizepolicy.html#setRetainSizeWhenHidden
La única forma decente que conozco es adjuntar un filtro de eventos al widget y filtrar los eventos de repintar. Funcionará sin importar cuán complejo sea el widget: puede tener widgets secundarios.
A continuación se muestra un ejemplo completo independiente. Sin embargo, viene con algunas advertencias y necesitaría un mayor desarrollo para completarlo. Solo se reemplaza el evento de pintura, por lo que aún puede interactuar con el widget, simplemente no verá ningún efecto.
Clics con el mouse, eventos de entrada / salida del mouse, eventos de enfoque, etc. seguirán llegando al widget. Si el widget depende de ciertas cosas que se realizan después de un repintado, tal vez debido a una actualización () desencadenada sobre esos eventos, puede haber problemas.
Como mínimo, necesitaría una declaración de caso para bloquear más eventos; por ejemplo, mover el mouse y hacer clic en eventos. Manejar el enfoque es una preocupación: tendrás que pasar el foco al siguiente widget de la cadena si el widget se oculta mientras está enfocado y cada vez que vuelve a adquirir el foco.
El rastreo de mouse también plantea algunas inquietudes, querría pretender que el widget perdió el seguimiento del mouse si estaba realizando un seguimiento antes. Implícitamente emular esto requeriría un poco de investigación, no sé de qué se trata el protocolo exacto de eventos de seguimiento del mouse que Qt presenta a los 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"
Prueba void QWidget::erase ()
. Funciona en Qt 3.
Puede ser QWidget :: setWindowOpacity (0.0) es lo que quieres? Pero este método no funciona en todas partes.
Puede usar un QStackedWidget. Coloque su botón en la primera página, un QWidget en blanco en el segundo, y cambie el índice de la página para hacer desaparecer su botón mientras conserva su espacio original.
Tengo 3 soluciones en mi mente:
1) Subclase su QWidget y use un método de reemplazo especial / propio setVisible () que active / desactive la pintura del widget (si el widget debe ser invisible, simplemente ignore la pintura con un método paintEvent () anulado). Esta es una solución sucia, no la use si puede hacerlo de otras maneras.
2) Use un QSpacerItem como marcador de posición y configure su visibilidad en el opuesto al QWidget que desea ocultar, pero conserve su posición + tamaño en el diseño.
3) Puede usar un widget de contenedor especial (heredar de QWidget) que obtiene / sincroniza su tamaño según el tamaño de los widgets de su hijo / hijo.
Tuve un problema similar y terminé colocando un espaciador junto a mi control con un tamaño de 0 en la dimensión que me importaba y un tipo de tamaño desplegable. Luego marqué el control con un Tipo de tamaño desplegable y establecí su extensión en 1. De esta manera, cuando está visible, tiene prioridad sobre el espaciador, pero cuando es invisible, el espaciador se expande para ocupar el espacio normalmente ocupado por el control.
Una opción es implementar una nueva subclase de QWidgetItem que siempre devuelve falso para QLayoutItem::isEmpty
. Sospecho que funcionará debido a la documentation subclase del ejemplo QLayout:
Ignoramos QLayoutItem :: isEmpty (); esto significa que el diseño tratará los widgets ocultos como visibles.
Sin embargo, es posible que agregar elementos a su diseño sea un poco molesto de esa manera. En particular, no estoy seguro de que pueda especificar fácilmente los diseños en los archivos UI si lo hiciera de esa manera.