c++qt-creatorqslider

Why are my QLabels not dynamically created when creation is called with QSlider?


I am new to QtCreator. I am trying to make a slider and dynamically remove and recreate labels when I move the slider. I am able to create labels dynamically when I call it from constructor, but for some reason when I do the same via moving he slider it does not work.

I intend that everytime slider is moved all the labels are deleted and the created fresh. I am not sure if it is the deletion prosess that messes this or something else. When I run this in debug mode it seems that new QLabel* are inserted into labels_? vector, but for some reason they are not visible.

Here is my code:

mainwindow.cpp:

#include "mainwindow.hh"
#include "ui_mainwindow.h"
#include <QLabel>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->horizontalSlider->setRange(1,10);
    ui->horizontalSlider->setValue(5);

    connect(ui->horizontalSlider, &QSlider::sliderMoved, this, &MainWindow::move_slider);

    // This works
    //create_label(50);

    // This works
//    for (auto i = 0 ; i < ui->horizontalSlider->value(); i++) {
//        labels_.push_back(create_label(i*item_width_));
//    }
}

MainWindow::~MainWindow()
{
    delete ui;
 }

QLabel* MainWindow::create_label(int x)
{
    QLabel* label = new QLabel(ui->frame);
    label->setGeometry(x, 0, item_width_, item_width_);
    label->setText(QString::number(ui->horizontalSlider->value()));
    return label;
}

// This method is called when silder moves. It seems to remove labels,
// but does not make new labels.
void MainWindow::move_slider()
{
    // Remove labels and reset
    for (QLabel* label: labels_) {
        delete label;
    }
    labels_.clear();

    // Create labels
    for (auto i = 0 ; i < ui->horizontalSlider->value(); i++) {
        labels_.push_back(create_label(i*item_width_));
    }
}

mainwindow.hh

#ifndef MAINWINDOW_HH
#define MAINWINDOW_HH

#include <QMainWindow>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    int item_width_ = 20;

    std::vector<QLabel*> labels_;

    QLabel* create_label(int x);

    void move_slider();

    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_HH

contents of labels_ after moving slider as seen in debug window:

contents of labels_ after moving slider

EDIT: I tried to disconect slider from move_slider() and instead created PushButton and connected it with:

//connect(ui->horizontalSlider, &QSlider::sliderMoved, this, MainWindow::move_slider);
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::move_slider);

This also calls the method on clicked and removes labels out of sight, but does not return them.

Another thing I tried is just running this in constructor without any button presses or slider movement:

// Create labels
for (auto i = 0 ; i < ui->horizontalSlider->value(); i++) {
    labels_.push_back(create_label(i*item_width_));
}

// Remove labels and reset
for (QLabel* label: labels_) {
    delete label;
}
labels_.clear();

// Create labels again
for (auto i = 0 ; i < ui->horizontalSlider->value(); i++) {
    labels_.push_back(create_label(i*item_width_));
}

It shows the labels correctly. So I think this my deletion should work (at least this line above seems to work in constructor), but simply either being part of either a method or constructor seems to be key difference in wheter or not new labels can be shown, even though it shouldn't.


Solution

  • Quote from the Qt Docs of QWidget constructor:

    If you add a child widget to an already visible widget you must explicitly show the child to make it visible.

    Just add label->setVisible(true); to your create_label method:

    QLabel* MainWindow::create_label(int x)
    {
        QLabel* label = new QLabel(ui->frame);
        label->setGeometry(x, 0, item_width_, item_width_);
        label->setText(QString::number(ui->horizontalSlider->value()));
        label->setVisible(true);  // Add this
        return label;
    }
    

    It works in the constructor, because your window is not visible at that moment.