LedHead
LedHead

Reputation: 247

Dynamically create/destroy custom widgets in Qt

I'm just starting with Qt. I would like to make a GUI with custom widgets that can be used to display/edit my custom datatypes . I would also like to be able to dynamically create/destroy these widgets (without destroying the underlying data).

I've tried to accomplish this by storing a list of my data items (dataContainer) in the main Window class, and passing a pointer to a dataContainer to the constructor of my widget (customWidget), which stores this pointer. Then customWidget can alter the underlying data via this pointer.

The code below allows the user to repeatedly add dataContainer instances to the list and edit each of their "names". When I run this, everything works fine as long I don't actually edit the name, but if I do edit the name and then click the "Add" button, I get a segfault. This segfault occurs during the destructor of customWidget during delete myWidget; in Window::add().

Two questions:

  1. Why does this code crash? Am I not allowed to dynamically create/destroy widgets like this?
  2. Is there a more appropriate way to accomplish this?

I apologize if there's something basic I'm missing here, but I've been up-and-down lots of forums and tutorials, and not found anything helpful.

main.cpp

#include <QApplication>
#include "window.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Window mainWindow;
    mainWindow.show();
    return app.exec();
}

window.h

#ifndef WINDOW_H
#define WINDOW_H

#include <QWidget>
#include "customwidget.h"

class Window : public QWidget {
  Q_OBJECT
  public:
    Window();

  public slots:    
    void add();    

private:

  // the main data structure
  QList<dataContainer> dataList;

  // the widget that displays the custom data
  customWidget *myWidget;     

  QVBoxLayout *mainLayout;
  QPushButton *addButton;

};

#endif

window.cpp

#include <QtGui>
#include "window.h"

Window::Window(){

  // start with a single piece of data in the list
  dataContainer newData;
  dataList.append(newData);

  // create layout container
  mainLayout = new QVBoxLayout(this);
  mainLayout->setAlignment(Qt::AlignTop);

  // make the Add button, and connect its clicked() SIGNAL
  // to our add() SLOT
  addButton=new QPushButton(tr("Add"),this);
  connect(addButton,SIGNAL(clicked()),this,SLOT(add()));
  mainLayout->addWidget(addButton);  

  // create a custom widget to display our data and
  // give it a pointer to the data it will display/modify
  myWidget = new customWidget(this,&(dataList.last()) );
  mainLayout->addWidget(myWidget);

  setLayout(mainLayout);   

}

void Window::add(){

  // create a new piece of data, and add to the list
  dataContainer newData;
  dataList.append(newData);

  // debug: show the current list
  qDebug() << "Data List(" << dataList.size() << ")";
  for (int i=0;i<dataList.size();i++){
    qDebug() << dataList[i].getName().c_str();
  }

  // delete the old widget
  delete myWidget;

  // and make a new widget with the new data
  myWidget = new customWidget(this,&(dataList.last()) );
  mainLayout->addWidget(myWidget);   

}
#include "window.moc"

customwidget.h

#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H

#include <QWidget>
#include <QtGui>
#include <string>

class dataContainer {
  private:

    std::string name;

  public:
    dataContainer(){name="oooh";};
    std::string getName()const{return name;};
    std::string setName(const std::string& n){name=n;};

};

class customWidget : public QWidget {
  Q_OBJECT

  private:
    dataContainer *data;

  public slots:
    void nameChangedSlot(const QString&);

  public:
    customWidget(QWidget *parent,  dataContainer *d);

};
#endif

customwidget.cpp

#include "customwidget.h"

customWidget::customWidget(QWidget *parent,  dataContainer *d) : QWidget(parent) {

  // store a pointer to the data that we want to alter
  data = d;

  // create an edit box and initialize it with the data name
  QVBoxLayout *mainLayout=new QVBoxLayout(this);
  QLineEdit *edit=new QLineEdit(QString::fromStdString(data->getName()),this);
  mainLayout->addWidget(edit);

  connect(edit,SIGNAL(textChanged(const QString&)),this,SLOT(nameChangedSlot(const QString&)));

}

void customWidget::nameChangedSlot(const QString& name){

  // alter the contents of our data based on the changed string
  data->setName(name.toStdString());

}

#include "customwidget.moc"

Upvotes: 2

Views: 13262

Answers (1)

fatma.ekici
fatma.ekici

Reputation: 2837

A widget that is added to a layout can not be deleted directly. Instead, you can try below code which deletes a specific widget and corresponding layout item from layout:

QLayoutItem* item;
while ( ( item = mainLayout->takeAt( 0 ) ) != NULL )
{
    if(item->widget() == myWidget){
        delete item->widget();
        delete item;
    }
}

Upvotes: 4

Related Questions