PDA

View Full Version : Signal/slot between widgets



sr1s
22nd March 2018, 20:25
Hello everybody,
I have just started using Qt, and immediately I found the first difficulties in understanding the use of signal / slot.
The problem is simple: I have a window (QWidget) with a QlineEdit and a button: I would like that, after typing a text in the Qline and pressing the button, another window (QWidget) would open with a QlineEdit field, filled with the text previously entered.
I can do the reverse (from first page open the second page, and from here -pressing a button - to fill the parent window), but it seems that in my code there is no link between the signal and the generated window slot ... I found this topic: http://www.qtcentre.org/threads/23989-Communication-between-two-QDialog-windows?highlight=
but I can't apply the suggestions.

Page1.h:

#ifndef PAGE1_H
#define PAGE1_H

#include <QWidget>

namespace Ui {
class Page1;
}

class Page1 : public QWidget
{
Q_OBJECT

public:
explicit Page1(QWidget *parent = 0);
~Page1();
signals:
void transfer(QString);

private:
Ui::Page1 *ui;

private slots:
void on_pushButton_clicked();

};

#endif // PAGE1_H


Page1.cpp:

#include "page1.h"
#include "ui_page1.h"
#include "page2.h"
Page1::Page1(QWidget *parent) :
QWidget(parent),
ui(new Ui::Page1)
{
ui->setupUi(this);
}

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

void Page1::on_pushButton_clicked()
{
Page2 *form = new Page2();
QString testo;
testo=ui->to_pass->text();
emit(transfer(testo));
connect(this, SIGNAL(trasfer(QString)), form, SLOT(UpdateField(QString)));
form->show();
}


Page2.h:

#ifndef PAGE2_H
#define PAGE2_H

#include <QWidget>

namespace Ui {
class Page2;
}

class Page2 : public QWidget
{
Q_OBJECT

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

private slots:
void UpdateField(QString text);

private:
Ui::Page2 *ui;
};

#endif // PAGE2_H

Page2.cpp:


#include "page2.h"
#include "ui_page2.h"

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

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

void Page2::UpdateField(QString text)
{
ui->receive->clear();
ui->receive->setText(text);
}


Thanks for any suggestion!

d_stranz
23rd March 2018, 00:58
emit(transfer(testo));
connect(this, SIGNAL(trasfer(QString)), form, SLOT(UpdateField(QString)));


This is backwards. If you emit the signal before you connect the other window's slot to it, how will the other window receive it? The signal doesn't hang around, asking "Hey, anybody listening? Hello? Hello? Anybody?" until a slot eventually gets connected. Instead, it says, "Hey, anybody listening? No? Then I'm out of here."

Your code has a serious memory leak. Each time the button is clicked, you create a new Page2 instance, completely unrelated to and independent of any others created during previous clicks. Even if you eventually hide them, they are still around, lurking invisibly in the background.

A better solution is to create a Page2 instance in the Page1 constructor (as a child of the Page1 instance: page2 = new Page2( this )) and store the pointer as a member variable of Page1 Page2 * page2). When you create it you also make the connection between your signal and the Page2 slot. Once. In the Page1 constructor.

In your button click slot, you can simply call page2->show() and then emit your signal. Or vice-versa. The Page2 widget instance will receive the signal whether it is visible or not. Because you create the Page2 widget with the Page1 instance as its parent, it will be deleted automatically when the Page1 widget is deleted.

sr1s
23rd March 2018, 23:15
Thank you very much for your explanation! Now it's clear the way to work. By now, I have this problem: when the child page2=new Page2 (this) is created, it superimposes itself to the Page1, and, as soon as I push the button, I obtain a "mixed" window with Page1 and Page2 widgets together. I can't hide the Page1, because so I remove the whole window... perhaps I have to use different solution than Qwidget-with-Qwidget? Thanks a lot!

d_stranz
23rd March 2018, 23:32
I obtain a "mixed" window with Page1 and Page2 widgets together.

Ah, yes, that's probably right. If you are not using a layout on Page1, then it will merge Page2 into Page1 when it is shown. OK, when you create page2, do not make it a child of page1 (this). In that case, you need to make sure you delete it in the Page1 destructor, just like you delete the ui pointer.

sr1s
23rd March 2018, 23:37
SOLVED...just page2=new Page2 ();
If someother newbe as me is interested, I resend the code:

Pag1.h

#ifndef PAGE1_H
#define PAGE1_H

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

namespace Ui {
class Page1;
}

class Page1 : public QWidget
{
Q_OBJECT

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

signals:
void transfer(QString);

private:
Ui::Page1 *ui;
Page2* page2;

private slots:
void on_pushButton_clicked();

};

#endif // PAGE1_H

Page1.cpp


#include "page1.h"
#include "ui_page1.h"
#include "page2.h"
#include <qdebug.h>

Page1::Page1(QWidget *parent) :
QWidget(parent),
ui(new Ui::Page1)
{
ui->setupUi(this);
page2=new Page2;
connect(this, SIGNAL(transfer(QString)), page2, SLOT(UpdateField(QString)));
}

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

void Page1::on_pushButton_clicked()
{
QString testo;
testo=ui->to_pass->text();
emit(transfer(testo));
page2->show();
}


Page2.h and Page2.cpp were not changed.
Thanks again!

d_stranz
24th March 2018, 17:02
Still have a small memory leak - you create page2 but never delete it. Change the Page1 destructor to this:



Page1::~Page1()
{
delete ui;
delete page2; // <-- add
}

sr1s
24th March 2018, 19:51
You are right!
Thank you for your help, d_stranz, and have a nice weekend!

ado130
26th March 2018, 10:04
d_stranz: What about this solution?


void Page1::on_pushButton_clicked()
{
Page2 *form = new Page2();
form->setAttribute(Qt::WA_DeleteOnClose); // added this line
connect(this, SIGNAL(transfer(QString)), form, SLOT(UpdateField(QString)));
QString testo=ui->to_pass->text();
emit(transfer(testo));
form->show();
}


I understand your solution, in general I do not have problem with that, but I think this looks "better" - at least you do not have to remember to delete it in destructor.

d_stranz
26th March 2018, 17:41
What about this solution?

If you want a completely new Page2 instance every time you click the button, this is one way to do it that avoids a member variable. It depends on how complex Page2 really is and whether constructing it on-the-fly like this would cause a noticeable pause between the click and the show.

On the other hand, if you want the Page2 instance to "remember" what it was displaying the last time it was used, this approach will not be very good. Making a new instance each time will always initialize it to a fresh state with no memory of what was there before. So, if Page2 is used for setting options, with check boxes, radio buttons, and so forth, having them reset to the default settings each time will not make users very happy. If you have a single instance and simply show() and hide() it, then it will keep its state between uses.

My preference is to do as I suggested: create a single instance in the constructor and re-use it when needed. If you do need to set it to a default state, then add a method to the Page2 class to do that and call it before you show it.

sr1s
29th March 2018, 15:39
It's a really good solution!
My first problem was simply to make it working, but of course the second step is "make it work better"...
With your second suggestion I can optimize my code, and now I'm trying to create a more complex Page2 for better understanding the differences of two ways.
Have a nice day