Qt: Widget method addButtons() does NOT work as ne

2019-04-17 06:36发布

This question is an exact duplicate of:

I am using Qt5 (beginner) on Windows7.
In the main window of my app I want to display and remove some push-buttons.

widget = new ButtonWidget(ui->frame); // frame is a QScrollArea
connect(ui->addBtns,    SIGNAL(clicked()), widget, SLOT(addButtons()));
connect(ui->deleteBtns, SIGNAL(clicked()), widget, SLOT(deleteButtons()));

And the ButtonWidget class is here:

ButtonWidget::ButtonWidget(QWidget * parent) : QWidget(parent)
{
   //addButtons();
}

void ButtonWidget::addButtons()
{
   QStringList texts{"1\nok", "2\nok", "3\nok", "4\nok", "5\nok", "6\nok"};
   gridLayout = new QGridLayout;
   for(int i = 0; i < texts.size(); i++)
   {
      QPushButton * button = new QPushButton(texts[i]);
      gridLayout->addWidget(button, i / 5, i % 5);
   }
   setLayout(gridLayout);
}

// I'm not sure this method/function is ok... :(
void ButtonWidget::deleteButtons()
{
    QLayoutItem * child;
    while((child = gridLayout->takeAt(0)) != 0)
    {
        gridLayout->removeWidget(child->widget());
        delete child->widget();
        delete child;
    }
    delete gridLayout;
}

Problem is: when I click on add_buttons, I get all buttons displayed, but they are shrunk, tiny or something... :
enter image description here

OTOH... if I remove the comment from addButtons() call in the constructor (hence calling from within the constructor), the result is ok:
enter image description here

So, finally I have 2 questions:
1) How to fix the code to be able to add those buttons properly (when add_buttons is clicked)?
2) Is the deleteButtons() method ok?

2条回答
倾城 Initia
2楼-- · 2019-04-17 06:50

First, I would recommend to do

gridLayout = new QGridLayout(this);

in the constructor. You don't need to delete all the grid, create it again and set in as a layout when you can just remove its content (with delete button for example) and fill it again afterwards.

EDIT : comment of Werner Erasmus : No need to set the layout. The fact that it has a parent widget implies that it sets itself up.

The problem is that is you do not modify addButtons() you will substitute the previous buttons without knowing where they go.

Also, try to give the QPushButton a parent :

new QPushButton(texts[i],this);

For your second point : Removing widgets from QGridLayout

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-04-17 06:53

EDIT:

After some more testing (without looking at the source code, but suspecting that it must be safe to delete button, else things would be brittle), I've implemented removeButtons as follows:

void ButtonWidget::deleteButtons()
{
    while(myLayout->count())
    {
        delete myLayout->itemAt(0)->widget();
    }
}

This works, and it proves that deleting a widget also removes the widget from it's parent, and layouts associated with the parent (by slots hooked up to when a child widget is deleted). The above code confirms this, as count(), which refers to number of layout items, decrease to zero (and those layout items are managed by the Layout). takeAt(x) is never called, and doesn't need to be - simply delete the widgets (buttons). Wholla!

ORIGINAL ANSWER

As mentioned in my other post, you only need to delete the buttons (it is removed from its parent automatically). However, if a widget was added to a layout, the parent of that layout becomes the parent of the widget, and an associated QLayoutItem is created that is managed by the layout itself. To the delete the buttons, the safest way is to take all the layout items (ownership the taker's responsibility), delete each items associated widget, and the delete each item. I'll try and find relevant references apart from the sources...

The following code works:

//ButtonWidget.h
#include <QWidget>
#include <QScrollArea>
#include <QHBoxLayout>

class ButtonWidget : public QScrollArea
{
    Q_OBJECT

  public:
    ButtonWidget(QWidget *parent = 0);
    ~ButtonWidget();
    void addButtons();
    void deleteButtons();
  private:
    QHBoxLayout* myLayout;
};

//ButtonWidget.cpp

#include "ButtonWidget.h"
#include <QGridLayout>
#include <QPushButton>
#include <QLayoutItem>

ButtonWidget::ButtonWidget(QWidget * parent) :
  QScrollArea(parent),
  myLayout(new QHBoxLayout(this))
{
}

ButtonWidget::~ButtonWidget()
{
}

void ButtonWidget::addButtons()
{
   QStringList texts{"1\nok", "2\nok", "3\nok", "4\nok", "5\nok", "6\nok"};

   for(int i = 0; i < texts.size(); i++)
   {
      myLayout->addWidget(new QPushButton(texts[i]));
   }
}

void ButtonWidget::deleteButtons()
{
    QLayoutItem * child;

    while((child = myLayout->takeAt(0)) != 0)
    {
      delete child->widget();
      delete child;
    }
}

#include "ButtonWidget.h"
#include <QApplication>
#include <QScrollArea>
#include <QPushButton>
#include <QGridLayout>
#include <QHBoxLayout>
#include <memory>

std::unique_ptr<QScrollArea> makeArea()
{
  std::unique_ptr<QScrollArea> area(new QScrollArea);
  auto layout = new QGridLayout(area.get());
  auto addButton  = new QPushButton("Add");
  auto removeButton  = new QPushButton("Remove");
  layout->addWidget(addButton, 0, 0);
  layout->addWidget(removeButton, 0, 1);
  auto btnWidget = new ButtonWidget;
  layout->addWidget(btnWidget,1,0,1,2);


  QObject::connect(addButton, &QPushButton::clicked, [=]()
  {
    btnWidget->addButtons();
  });

  QObject::connect(removeButton, &QPushButton::clicked, [=]()
  {
    btnWidget->deleteButtons();
  });
  return move(area);
}

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  auto area = makeArea();
  area->show();
  return a.exec();
}

You need to enable c++11 in your config (.pro) to get the lambdas working.

CONFIG += c++11

I've used QHBoxLayout for your buttons, as it better models what you want. Although strictly not necessary, I'm returning unique_ptr from the makeArea in main, as it has not parent, I'm not sure whether it gets some parent because it is the first widget created, but unique_ptr shows intent.

NOTE:

Apparently the layout item is not the parent of the widget, but the widget associated with the layout itself is the parent of widgets belonging to its layout item.

查看更多
登录 后发表回答