drkalmenius drkalmenius - 3 months ago 22
C++ Question

Passing arguments to a slot in qt5 c++

I'm creating a ToDo list app in c++ qt. When a plus button is pressed, it adds a QHBoxLayout containing a QLabel and a QToolButton to a vertical layout inside my gui, so I get new boxes with the 'ToDos' inside them, and buttons to remove them next to them. I have set up the various widgets inside my slot which is called when the add button is clicked. However, I need to pass them as arguments to the slot which is called when a remove button is pressed. I have researched already, and all I have found is QSignalMapper. However, I cannot find any cases close enough to mine to replicate, and I have read it only works with certain arguments, and not the three I need (QHBoxLayout, QLineEdit and QToolButton).
Some of the code for the slot which is called when the 'add' button is pressed is:

//Creates a read only LineEdit which the user will add
QLineEdit *toDoBox = new QLineEdit(this);
toDoBox->setText(ui->lineEdit->text());
toDoBox->setReadOnly(true);

//Creates a new X button for removal of ToDo's
QToolButton *removeButton = new QToolButton;
removeButton->setText("X");

//Adds a horizontal layout with the ToDo and the remove button in it, to keep them together
QHBoxLayout *toDoLayout = new QHBoxLayout;
toDoLayout->addWidget(toDoBox);
toDoLayout->addWidget(removeButton);

//Removes a ToDo when the remove button is clicked
connect(removeButton, SIGNAL(clicked()), this, SLOT(on_removeButton_clicked()));


My code is hosted on GitHub if you want to see the whole project:
https://github.com/DanWilkes02/ToDoList

Thanks for bearing with me- I struggle explaining things that are so clear in my head!

Answer

If I understand well your problem, you want to get the allocated objects which represent a todo in order to free them and to update your View.

You could achieve this by simply wrapping your QLineEdit, QToolButton and QHBoxLayout objects into a class, and use a container (a vector for instance) in your ToDoList class. That way, you push_back your "todo object" each time you press the on_toolButton_clicked method.

Then, you simply have to use a signal with an index triggering an on_delete_todo slot which deletes a "todo object" from your vector and update the view.

Also, take a look at this Qt Model-View Programming

Here is a sample (tested and working under QT5):

Your Todo Widget

#ifndef TODOVIEW_H
#define TODOVIEW_H

#include <QString>

class QLineEdit;
class QToolButton;
class QHBoxLayout;

#include <QWidget>

class TodoView : public QWidget
{
    Q_OBJECT

private:
    QLineEdit*      frame;
    QToolButton*    removeButton;
    QHBoxLayout*    toDoLayout;
    int             index;

public:
    TodoView(const QString& what, int index, QWidget* parent);
    ~TodoView();

    inline void setIndex(int i) { index = i; }
    inline int getIndex(){ return index; }

private slots:
    void emitIndex();

signals:
    void selectedIndex(int);

};

#endif // TODOVIEW_H

#include "todoview.h"

#include <QLineEdit>
#include <QToolButton>
#include <QHBoxLayout>

TodoView::TodoView(const QString& what, int index, QWidget* parent) : QWidget(parent), index(index)
{
    frame = new QLineEdit(this);
    frame->setText(what);
    frame->setReadOnly(true);

    removeButton = new QToolButton(this);
    removeButton->setText("X");

    toDoLayout = new QHBoxLayout(this);
    toDoLayout->addWidget(frame);
    toDoLayout->addWidget(removeButton);

    connect(removeButton, SIGNAL(clicked()), this, SLOT(emitIndex()));
}

TodoView::~TodoView() {}

void TodoView::emitIndex()
{
    emit selectedIndex(getIndex());
}

Your MainWindow

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <vector>

class TodoView;
class QVBoxLayout;

namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void addTodo();
    void delTodo(int);

private:
    Ui::MainWindow*         ui;

    QVBoxLayout*            vBoxLayout;

    std::vector<TodoView*>  todoView;
    int                     max = -1;
};

#endif // MAINWINDOW_H

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "todoview.h"

#include <QVBoxLayout>
#include <QAction>

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

    vBoxLayout = new QVBoxLayout(centralWidget());

    QAction* add = new QAction(ui->mainToolBar);
    ui->mainToolBar->addAction(add);

    connect(add, SIGNAL(triggered()), this, SLOT(addTodo()));
}

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

void MainWindow::addTodo()
{
    if(max > 9)
    {
        // Error msg.
    }
    else
    {
        TodoView* tdV = new TodoView("Yolo", max, centralWidget());
        connect(tdV, SIGNAL(selectedIndex(int)), this, SLOT(delTodo(int)));

        vBoxLayout->addWidget(tdV);
        todoView.push_back(tdV);
        ++max;
    }
}


void MainWindow::delTodo(int i)
{
    // check if i < todoView.size().
    delete todoView.at(i);
    // update vector indexes !!!
    --max;
}

I have edited this piece of code rapidly, I may have made several mistakes, but you have an idea of at least one solution.

It is also possible to use a fixed size for the vector (better solution). Setting the TodoView deleted objects to nullptr in the vector and search for nullptr when you want to add new Todo view components:

In the MainWindow constructor

todoView.reserve(10);
for(std::size_t i = 0; i < 10; ++i)
{
    todoView[i] = nullptr;
}

In the addTodo Slot

// Do not use push back.
// retrieve the max index.
// if < 10
for(std::size_t i = 0; i < todoView.size(); ++i)
{
    if(todoView[i] == nullptr)
    {
        // allocate TodoView and affect it to the i° element
    }
}

In the delTodo slot

delete todoView[i];
todoView[i] = nullptr;

Using a vector of pair is also possible (a pair of int TodoView).

Comments