Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QMainWindow auto resize



  • Hi all,

    Please take a look at these files:

    List.h:

    #ifndef LIST_H
    #define LIST_H
    
    #include <QMainWindow>
    #include "task.h"
    #include <QVector>
    
    class QLabel;
    class QPushButton;
    class QHBoxLayout;
    class QVBoxLayout;
    
    class List : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit List(QWidget *parent = nullptr);
        void updateStatus();
        ~List();
    
    public slots:
        void addTask();
        void removeTask(Task*);
        void taskStatusChanged();
    
    private:
        QVector<Task*> mTasks;
        QLabel* statusLabel;
        QPushButton* addTaskButtun;
        QHBoxLayout* hBox;
        QVBoxLayout* vBox;
    };
    
    #endif // LIST_H
    

    List.cpp:

    #include "list.h"
    #include <QPushButton>
    #include <QLabel>
    #include <QHBoxLayout>
    #include <QVBoxLayout>
    #include <QString>
    #include <QInputDialog>
    #include <QLineEdit>
    
    List::List(QWidget *parent)
        : QMainWindow(parent), mTasks()
    {
        statusLabel = new QLabel(tr("Status: 0 todo / 0 done"));
        addTaskButtun = new QPushButton(tr("Add task"));
    
        hBox = new QHBoxLayout;
        hBox->addWidget(statusLabel);
        hBox->addStretch();
        hBox->addWidget(addTaskButtun);
    
        vBox = new QVBoxLayout;
        vBox->addLayout(hBox);
        vBox->addStretch();
    
        QWidget *widget = new QWidget;
        widget->setLayout(vBox);
        setCentralWidget(widget);
    
        connect(addTaskButtun, &QPushButton::clicked, this, &List::addTask);
        updateStatus();
    }
    
    //************************************************
    
    void List::addTask() {
       bool ok;
       QString name = QInputDialog::getText(this, tr("Add Task"), tr("Task name"),
                                           QLineEdit::Normal, tr("Untitled task"), &ok);
    
       if(ok && !name.isEmpty()) {
           Task* task = new Task(name);
           connect(task, &Task::removed, this, &List::removeTask);
           connect(task, &Task::statusChanged, this, &List::taskStatusChanged);
           mTasks.append(task);
           vBox->addWidget(task);
           updateStatus();
       }
    }
    
    //*********************************************
    
    void List::removeTask(Task* task) {
       mTasks.removeOne(task);
       vBox->removeWidget(task);
       delete task;
       updateStatus();
    }
    
    //**************************************
    
    void List::taskStatusChanged() {
        updateStatus();
    }
    
    //************************************
    
    void List::updateStatus() {
        int completedCount = 0;
    
        for(auto t: mTasks)
            if(t->isCompleted())
                completedCount++;
    
        int todoCount = mTasks.size() - completedCount;
        statusLabel->setText(QString("Statuse: %1 todo / %2 completed")
                                    .arg(todoCount) .arg(completedCount));
    }
    
    //*****************************
    
    List::~List()
    {
        delete vBox;
    }
    
    

    Task.h:

    #ifndef TASK_H
    #define TASK_H
    
    #include <QWidget>
    #include <QString>
    
    class QCheckBox;
    class QPushButton;
    class QHBoxLayout;
    
    class Task : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit Task(const QString&, QWidget* parent = nullptr);
    
        void setName(const QString&);
        QString name() const;
        bool isCompleted() const;
    
        ~Task();
    
    public slots:
        void rename();
    
    signals:
       void removed(Task*);
       void statusChanged(Task*);
    
    private slots:
       void checked(bool);
    
    private:
       QCheckBox* checkbox;
       QPushButton* editButton;
       QPushButton* removeButton;
       QHBoxLayout* hBox;
    };
    
    #endif // TASK_H
    
    

    Task.cpp:

    #include "task.h"
    #include "list.h"
    #include <QInputDialog>
    #include <QWidget>
    #include <QHBoxLayout>
    #include <QPushButton>
    #include <QCheckBox>
    
    Task::Task(const QString& name, QWidget* parent) : QWidget (parent)
    {
        checkbox = new QCheckBox;
        editButton = new QPushButton(tr("Edit"));
        removeButton = new QPushButton(tr("Remove"));
    
        hBox = new QHBoxLayout;
        hBox->addWidget(checkbox);
        hBox->addStretch();
        hBox->addWidget(editButton);
        hBox->addWidget(removeButton);
    
        setLayout(hBox);
    
        setName(name);
    
        connect(editButton, &QPushButton::clicked, this, &Task::rename);
        connect(removeButton, &QPushButton::clicked, [this]()->void {
            emit removed(this);
                });
        connect(checkbox, &QCheckBox::toggled, this, &Task::checked);
    };
    
    //*************************************
    
    void Task::setName(const QString& name) {
        checkbox->setText(name);
    }
    
    //********************************************
    
    QString Task::name() const {
        return checkbox->text();
    }
    
    //*****************************
    
    bool Task::isCompleted() const {
        return checkbox->isChecked();
    }
    
    //****************************************
    
    void Task::rename() {
        bool ok;
        QString value = QInputDialog::getText(this, tr("Edit task"), tr("Task name"),
                                              QLineEdit::Normal, this->name(), &ok);
        if(ok && !value.isEmpty())
            setName(value);
    }
    
    //************************************
    
    void Task::checked(bool checked) {
        QFont font(checkbox->font());
        font.setStrikeOut(checked);
        checkbox->setFont(font);
    
        emit statusChanged(this);
    }
    
    //**************************************************
    
    Task::~Task() {
        delete hBox;
    }
    
    

    When I add tasks "untitled task 1" to "untitled task 3", number 1 below:

    0_1563610239950_3.PNG

    then remove "untitled task 2", the frame/window doesn't shrink and the space for that task is just vacant, number 2. I like it to decrease and fit the remained tasks, like number 3. When a task is added, the window increases in size, so when one is removed, it should decreases in size too.

    I tested the followings one by one in void List::updateStatus() {, but none worked as expected!

    What should be used instead, please?

    this->sizeHint();
    this->update();
    this->resize(sizeHint());
    this->adjustSize();
    


  • When the central widget resizes, call adjustSize on the window to make the window shrink to fit the size of the central widget. So, call adjustSize(); at the end of addTask and removeTask.



  • @Kerndog73

    thanks for the answer. I used that:

    void List::removeTask(Task* task) {
       mTasks.removeOne(task);
       vBox->removeWidget(task);
       delete task;
       updateStatus();
       adjustSize();
    }
    

    But no changes in result! :|



  • @tomy The central widget isn't being resized when widgets are removed from the vBox layout. Call setSizeConstraint to ensure that the layout will set the size of the parent widget when child widgets are added and removed. So call vBox->setSizeConstraint(QLayout::SetFixedSize); in the List constructor.



  • @Kerndog73

    SizeConstraint and the fixedsize, as the names suggest, keep the size fixed, although it doesn't in effect and the window is being larger each time a new task is added. Of course, it doesn't work vice versa, Yet!

    I hoped there's a simple built-in function for that either in central widget or the window itself.


  • Moderators

    @tomy
    don't let the name fool you, according to this thread, it's exactly what you're looking for, and the name- setSizeConstraint(QLayout::SetFixedSize);- is just misleading

    https://stackoverflow.com/questions/14980620/qt-layout-resize-to-minimum-after-widget-size-changes



  • @J.Hilk

    setSizeConstraint(QLayout::SetFixedSize);- is just misleading

    Do you mean it works in practice for the problem, even though the name suggests something else?
    But in effect, it doesn't solve the issue.

    0_1563775695542_3.png



  • @tomy

    I didn't find a correct answer for my question here, but I like first to review most of the code. Starting from the header file task.h, on the top, we've got:

    class Task : public QWidget
    {
        Q_OBJECT
    

    we here say that we need to inherit QWdiegt because without that, Task is a simple C++ class with no GUI. Right, completely?

    This way, we both create a GUI widget and inherit worthwhile functions from QWidget. Right, once again?

    But QWidget is a big and heavy class, the QWidget class is the base class of all user interface objects, while the output window we create using it ultimately is very simple:

    1.PNG

    Couldn't we inherit from a lighter class for that purpose?


  • Lifetime Qt Champion

    @tomy said in QMainWindow auto resize:

    while the output window we create using it ultimately is very simple:

    But it's still a widget what you want to show - so yes you must derive from QWidget and there is also no way around it (and also no need for it).


Log in to reply