2011-01-31 39 views
12

Estoy creando una aplicación en Qt que permite a los usuarios arrastrar varios "módulos" en un QGraphicsView. Cada vez que se selecciona uno de estos módulos, que emite una señal que luego se recogió por un "ConfigurationWidget" que es un lado del panel que debe mostrar todos los parámetros relevantes del módulo seleccionado:Limpiar un diseño en Qt

class ConfigurationWidget : public QWidget 
{ 
    Q_OBJECT 

    public: 
    ConfigurationWidget(QWidget *parent) : QWidget(parent) {} 

    public slots: 
    void moduleSelected(Module* m) 
    { 
     if(layout()) 
     { 
     while (itsLayout->count()>0) 
     { 
      delete itsLayout->takeAt(0); 
     } 
     } 
     delete layout(); 

     itsLayout = new QFormLayout(this); 
     itsLayout->addRow(QString(tr("Type:")),  new QLabel(m->name())); 
     itsLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID())); 
     // ... Display a whole bunch of other fields that depends on the module 
    } 
}; 

El problema es que ConfigurationWidget nunca se borra cuando se selecciona un módulo. Los nuevos campos se dibujan sobre los antiguos. He intentado varias combinaciones de hide() y show(), invalidate(), update(), etc. en vano.

¿Cuál es la forma correcta de hacer un widget que puede cambiar sus campos de esta manera sobre la marcha?

+2

Debe utilizar un QStackedWidget – armonge

Respuesta

4

Parece que la mejor manera de hacer esto es utilizar un QStackedLayout, como insinuado por armonge:

void ConfigurationWidget::moduleSelectedSlot(Module* m) 
{ 
    QStackedLayout *stackedLayout = qobject_cast<QStackedLayout*>(layout()); 

    QWidget *layoutWidget = new QWidget(this); 
    QFormLayout *formLayout = new QFormLayout(layoutWidget); 
    formLayout->addRow(QString(tr("Type:")),  new QLabel(m->name())); 
    formLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID())); 
    // ... Display a whole bunch of other fields that depends on the module 

    delete stackedLayout->currentWidget(); 
    stackedLayout->addWidget(layoutWidget); 
    stackedLayout->setCurrentWidget(layoutWidget); 
} 
+4

Cuando se utiliza la clase QObject derivados, se debe utilizar en lugar de qobject_cast dynamic_cast: http://doc.qt.nokia.com/latest/qobject.html#qobject_cast –

+0

@PatriceBernassola: Fijo. – rcv

23

El bucle de código que he usado antes es como sigue:

 
void clearLayout(QLayout *layout) 
    QLayoutItem *item; 
    while((item = layout->takeAt(0))) { 
     if (item->layout()) { 
      clearLayout(item->layout()); 
      delete item->layout(); 
     } 
     if (item->widget()) { 
      delete item->widget(); 
     } 
     delete item; 
    } 
} 

¡Espero que sea útil para usted!

+2

Agregaría 'if (QSpacerItem * SpacerItem = Item-> spacerItem()) {delete SpacerItem; } 'a eso también :) – sjwarner

+0

La investigación adicional también sugiere que' eliminar elemento' es innecesario, ya que Qt maneja eso para nosotros (use 'Qlayout :: count()' para verificar) – sjwarner

+1

No necesita el 'if (item-> widget()) '. Eliminar 'NULL' es un nop. – Timmmm

8

Si transfiere el diseño a otro widget asignado en la pila, los widgets en ese diseño se convierten en los elementos secundarios del nuevo widget. Cuando el objeto temporal queda fuera del alcance destruye automáticamente el diseño y todos los widgets en él.

void moduleSelected(Module* m) 
{ 
    if(layout()) 
     QWidget().setLayout(layout()); 

    itsLayout = new QFormLayout(this); 
    itsLayout->addRow(QString(tr("Type:")),  new QLabel(m->name())); 
    itsLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID())); 
    // ... Display a whole bunch of other fields that depends on the module 
} 
+2

Esto es increíblemente feo e increíblemente inteligente al mismo tiempo. ;) Gracias por compartir este truco. Desearía que hubiera 'QLayout :: clear()' ... – leemes

1

Recientemente me encontré con el mismo problema con un QFormLayout. Lo que funcionó para mí (y también está en la documentación de Qt: http://qt-project.org/doc/qt-5/layout.html) fue el método takeAt (int).

void clearLayout(QLayout *layout) 
{ 
    QLayoutItem *item; 
    while ((item = layout->takeAt(0))) 
     delete item; 
} 
+0

Tenga cuidado: esto no eliminará los widgets, solo los eliminará del diseño, ya que los elementos de diseño no poseen widgets. Entonces, el programa puede terminar consumiendo toda la memoria si recrea el ui muchas veces con ese código.Esta no es una pérdida de memoria real, porque los widgets secundarios se eliminarán con el widget principal, pero esto puede tener el mismo efecto. – galinette

1

Usando while((item = layout->takeAt(0))) causará mensaje de advertencia "Índice no tomar a 0". utilizo count() lugar

void clearLayout(QLayout *layout) 
{ 
    if (layout) { 
     while(layout->count() > 0){ 
      QLayoutItem *item = layout->takeAt(0); 
      QWidget* widget = item->widget(); 
      if(widget) 
       delete widget; 
      delete item; 
     } 
    } 
} 
+1

Un elemento de diseño válido puede devolver un puntero nulo con item-> widget(), por ejemplo con diseños secundarios o espaciadores. Este código se bloqueará en estos casos. – galinette

+0

@galinette Depende, pero sí, en general, no es aconsejable llamar a eliminar en nulltpr. http://stackoverflow.com/questions/6731331/is-it-still-safe-to-delete-nullptr-in-c0x – Dorian

+1

En lugar de usar 'eliminar widget', sugeriría usar widget-> deleteLater(). En la mayoría de los casos, sería difícil notar la diferencia, pero puede haber una. Como sugiere la documentación sobre QObject: 'El objeto se eliminará cuando el control regrese al bucle de evento'. Este es el punto principal. Es posible que el widget envíe alguna señal, y después de enviar la señal, se realizan otras llamadas en la misma instancia de widget. Si eliminara el widget de forma "cruda", saldría del alcance y, en este caso, no podrá manejar nada después de enviar la señal. –

0

Reparent los artículos a NULL cuando usted quiere que desaparezcan de una ventana. Recientemente tuve problemas similares y la reparación los resolvió.

+0

Bienvenido a Stack Overflow. Esta pregunta ya tiene un puñado de respuestas y una que ha sido aceptada. Explique cómo su respuesta es diferente o mejor que las soluciones ya provistas. – avojak