PDA

View Full Version : Custom Widget in QListWidget doesn't show correct in list



astodolski
18th February 2014, 20:56
I created a custom widget in Qt designer, (with source for changing properties) consisting of a label, progress bar and a checkbox. I have that as an object in a mainwindow to display in a list when triggered. The first trigger displays the widget. Each successive trigger displays the widget in a subsequent row down the list but there only exists one widget with empty rows above.

Widget Code:
Controls.h


#ifndef CONTROLS_H
#define CONTROLS_H

#include <QWidget>

namespace Ui {
class Controls;
}

class Controls : public QWidget
{
Q_OBJECT

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

public:
Ui::Controls* ui;

public slots:
void WriteInfoNumber(int);

private:
// Ui::Controls *ui;
};

#endif // CONTROLS_H



Controls.cpp


#include "controls.h"
#include "ui_controls.h"

#include <QDebug>

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

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

void Controls::WriteInfoNumber(int value)
{
qDebug() << "ID Value = " << value;
ui->label->setText(QString::number(value));
}


MainWindow.h


#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "controls.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

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

signals:
void UpdateInfo(int);

private slots:
void on_actionFile_triggered();

void on_actionE_xit_triggered();

private:
Ui::MainWindow* ui;
Controls* ctls;
};

#endif // MAINWINDOW_H


MainWindow.cpp


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ui_Controls.h"
#include <QTime>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
ctls = new Controls();
connect(this, SIGNAL(UpdateInfo(int)), ctls, SLOT(WriteInfoNumber(int)));
}

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

void MainWindow::on_actionE_xit_triggered()
{
QApplication::quit();
}

void MainWindow::on_actionFile_triggered()
{
QTime t = QTime::currentTime();
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
item->setSizeHint(QSize(300, 35));
ui->listWidget->insertItem(ui->listWidget->size().height(),item);
ui->listWidget->setItemWidget(item, ctls);
emit UpdateInfo(t.msec());
}



It seems so straightforward. Don't know what I'm missing.

anda_skoa
18th February 2014, 21:32
You only have one object of type Controls, obviously it can only be displayed in one place.

Cheers,
_

P.S.:you don't need to insert the QListWidgetItem, passing the list widget to the constructor takes care of that already.

astodolski
19th February 2014, 00:15
You only have one object of type Controls, obviously it can only be displayed in one place.

Cheers,
_

P.S.:you don't need to insert the QListWidgetItem, passing the list widget to the constructor takes care of that already.

Yes that's correct. I only want one type of object displayed in the list. I want to add it to the list for every trigger so as to get a list of rows of the object displayed.

Hope that's clearer than what I originally wrote.

ChrisW67
19th February 2014, 02:22
What anda_skoa said was, "You only have one object of type Controls", not "You only have one type of object." It stands to reason that a single Controls widget can be displayed in only the one place. If you want to simultaneously display multiple widgets of type Controls then you need to create multiple objects of that type.

astodolski
19th February 2014, 04:22
What anda_skoa said was, "You only have one object of type Controls", not "You only have one type of object." It stands to reason that a single Controls widget can be displayed in only the one place. If you want to simultaneously display multiple widgets of type Controls then you need to create multiple objects of that type..

Right!

So call new Controls() in line 29 rather than ctls does the trick for handling multiple entries but breaks the emitted signal for updating the label part of the custom widget. What broke? Perhaps I need a different sender in the signal/slot line in the ctor?

anda_skoa
19th February 2014, 09:20
.
So call new Controls() in line 29 rather than ctls does the trick for handling multiple entries but breaks the emitted signal for updating the label part of the custom widget. What broke? Perhaps I need a different sender in the signal/slot line in the ctor?

How do you connect now?

Cheers,
_

astodolski
19th February 2014, 12:52
How do you connect now?

Cheers,
_

Line 10 MainWindow.cpp. I connect the emitted signal to the method WriteInfoNumber in Control.cpp

anda_skoa
19th February 2014, 13:40
That was from before, when you had only one instance.

If you want all instances of your widget to receive the signal, you need to connect to each one after its respective creation.

Cheers,
_

astodolski
19th February 2014, 14:32
That was from before, when you had only one instance.

If you want all instances of your widget to receive the signal, you need to connect to each one after its respective creation.

Cheers,
_

So my trigger method is now as follows:



void MainWindow::on_actionFile_triggered()
{
QTime t = QTime::currentTime();
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
item->setSizeHint(QSize(300, 35));
ui->listWidget->insertItem(ui->listWidget->size().height(),item);
ui->listWidget->setItemWidget(item, new Controls);
emit UpdateInfo(t.msec());
}


The slot connected to UpdateInfo does not change the label. If as you suggest, I connect to each successive instance, I still don't get a change in the label from the slot associated. What I do see however is a call to the slot for each entry in the list: i.e 1 call for the first, 2 calls for the second entry, 3 calls for the third in the list etc.

Should there be a different connect?

What I was able to do as perhaps an interitm step is to call the following which fortunately sets the text for the label:


item->setText(QString::number(t.msec()));


Since I have two other controls, that being a progress bar and the other a checkbox, I still am not sure how to set those other items in the custom widget

anda_skoa
19th February 2014, 18:44
So my trigger method is now as follows:



void MainWindow::on_actionFile_triggered()
{
QTime t = QTime::currentTime();
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
item->setSizeHint(QSize(300, 35));
ui->listWidget->insertItem(ui->listWidget->size().height(),item);
ui->listWidget->setItemWidget(item, new Controls);
emit UpdateInfo(t.msec());
}


That is still missing the connect



void MainWindow::on_actionFile_triggered()
{
QTime t = QTime::currentTime();
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
item->setSizeHint(QSize(300, 35));

// create instance of Controls
Controls *ctls = new Controls;

// connect signal to Controls instance
connect(this, SIGNAL(UpdateInfo(int)), ctls, SLOT(WriteInfoNumber(int)));

ui->listWidget->setItemWidget(item, ctls);
emit UpdateInfo(t.msec());
}


Cheers,
_

astodolski
20th February 2014, 01:22
That is still missing the connect



void MainWindow::on_actionFile_triggered()
{
QTime t = QTime::currentTime();
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
item->setSizeHint(QSize(300, 35));

// create instance of Controls
Controls *ctls = new Controls;

// connect signal to Controls instance
connect(this, SIGNAL(UpdateInfo(int)), ctls, SLOT(WriteInfoNumber(int)));

ui->listWidget->setItemWidget(item, ctls);
emit UpdateInfo(t.msec());
}


Cheers,
_

I may be misunderstanding. The Controls instance is the object named ctls. The signal UpdateInfo is connected to the WriteInfoNumber slot belonging to the Controls class.

anda_skoa
20th February 2014, 09:14
The Controls instance is the object named ctls.

Yes.



The signal UpdateInfo is connected to the WriteInfoNumber slot belonging to the Controls class.
Yes. The connect() call's arguments are:

1) sender object
2) signal
3) receiver object
4) slot

Your original code was creating a Controls instance in the constructor and connected to it.
Your new code creates new Controls instances on every actionFile triggered(). If you want those instances to receive the signal as well, you connect to them as well.

Cheers,
_

astodolski
20th February 2014, 12:46
Yes.


Yes. The connect() call's arguments are:

1) sender object
2) signal
3) receiver object
4) slot

Your original code was creating a Controls instance in the constructor and connected to it.
Your new code creates new Controls instances on every actionFile triggered(). If you want those instances to receive the signal as well, you connect to them as well.

Cheers,
_

If I copy the connect call from the ctor to the actionFile_triggered() method, the SLOT gets called for every entry that's in the list. That is for the initial entry, one call. The next instance doesn't signal the slot once, it get's called twice and so on. How do I fix that?

anda_skoa
20th February 2014, 17:11
I am not sure I understand, the slot should only get called once per receiver object.

Do something like this in the slot



qDebug() << Q_FUNC_INFO << "this=" << this;

and check if "this" is the same twice per emit.

Cheers,
_

astodolski
20th February 2014, 17:49
I am not sure I understand, the slot should only get called once per receiver object.

Do something like this in the slot



qDebug() << Q_FUNC_INFO << "this=" << this;

and check if "this" is the same twice per emit.

Cheers,
_

I will try this but in the meantime look at the slot in Controls.cpp It contains a debug statement:


qDebug() << "ID Value = " << value;


For every instance the slot gets called the number of entries in the list. All the code for this small sample is posted. Maybe you can load it and run it to see what I mean. A UI which isn't posted will have to be created to have the three controls that is supposed to be in the custom widget.

Update:

The results of your suggestion is as follows:



ID Value = 268
void __thiscall Controls::WriteInfoNumber(int) this= Controls(0x389ef0, name = "Controls")
ID Value = 147
void __thiscall Controls::WriteInfoNumber(int) this= Controls(0x389ef0, name = "Controls")
ID Value = 147
void __thiscall Controls::WriteInfoNumber(int) this= Controls(0x389ef0, name = "Controls")
ID Value = 627
void __thiscall Controls::WriteInfoNumber(int) this= Controls(0x389ef0, name = "Controls")
ID Value = 627
void __thiscall Controls::WriteInfoNumber(int) this= Controls(0x389ef0, name = "Controls")
ID Value = 627
void __thiscall Controls::WriteInfoNumber(int) this= Controls(0x389ef0, name = "Controls")


That's when calling on_actionFile_triggered() 3 times!

anda_skoa
20th February 2014, 20:03
I pieced together the bits and created dummy .ui files and I get this



created new control Controls(0x20c5340, name = "Controls")
this= Controls(0x20c5340, name = "Controls") ID Value = 503
created new control Controls(0x20757d0, name = "Controls")
this= Controls(0x20c5340, name = "Controls") ID Value = 511
this= Controls(0x20757d0, name = "Controls") ID Value = 511
created new control Controls(0x20f4d40, name = "Controls")
this= Controls(0x20c5340, name = "Controls") ID Value = 26
this= Controls(0x20757d0, name = "Controls") ID Value = 26
this= Controls(0x20f4d40, name = "Controls") ID Value = 26


The "created new control" comes right from after creating the control inside the slot


void MainWindow::on_actionFile_triggered()
{
QTime t = QTime::currentTime();
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
item->setSizeHint(QSize(300, 35));
ctls = new Controls();
qDebug() << "created new control" << ctls;
connect(this, SIGNAL(UpdateInfo(int)), ctls, SLOT(WriteInfoNumber(int)));
ui->listWidget->setItemWidget(item, ctls);
emit UpdateInfo(t.msec());
}

The other one obviously from the slot in Controls.

As we can see, the first control is created, it receives the signal. Then he second control is created and both instances get the signals, as expected
Then a third control is created and, again as expected, all three get the signal.

Now, I have no idea why you want all of them to show the same value, but the do. No receiver's slot is called twice per emit.

Cheers,
_

astodolski
20th February 2014, 20:51
I pieced together the bits and created dummy .ui files and I get this



created new control Controls(0x20c5340, name = "Controls")
this= Controls(0x20c5340, name = "Controls") ID Value = 503
created new control Controls(0x20757d0, name = "Controls")
this= Controls(0x20c5340, name = "Controls") ID Value = 511
this= Controls(0x20757d0, name = "Controls") ID Value = 511
created new control Controls(0x20f4d40, name = "Controls")
this= Controls(0x20c5340, name = "Controls") ID Value = 26
this= Controls(0x20757d0, name = "Controls") ID Value = 26
this= Controls(0x20f4d40, name = "Controls") ID Value = 26


The "created new control" comes right from after creating the control inside the slot


void MainWindow::on_actionFile_triggered()
{
QTime t = QTime::currentTime();
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
item->setSizeHint(QSize(300, 35));
ctls = new Controls();
qDebug() << "created new control" << ctls;
connect(this, SIGNAL(UpdateInfo(int)), ctls, SLOT(WriteInfoNumber(int)));
ui->listWidget->setItemWidget(item, ctls);
emit UpdateInfo(t.msec());
}

The other one obviously from the slot in Controls.

As we can see, the first control is created, it receives the signal. Then he second control is created and both instances get the signals, as expected
Then a third control is created and, again as expected, all three get the signal.

Now, I have no idea why you want all of them to show the same value, but the do. No receiver's slot is called twice per emit.

Cheers,
_

I appreciate you staying with me on this problem. However your observation is incorrect. The "ID Value = " does in fact get displayed more times than it is supposed to. There should ONLY be one ID value for each instance - at least that's what the goal is:

10067



created new control Controls(0x5b16b8, name = "Controls")
ID Value = 309
void __thiscall Controls::WriteInfoNumber(int) this = Controls(0x5b16b8, name = "Controls")
created new control Controls(0x5df218, name = "Controls")
ID Value = 802
void __thiscall Controls::WriteInfoNumber(int) this = Controls(0x5b16b8, name = "Controls")
ID Value = 802
void __thiscall Controls::WriteInfoNumber(int) this = Controls(0x5df218, name = "Controls")
created new control Controls(0x5dfda8, name = "Controls")
ID Value = 896
void __thiscall Controls::WriteInfoNumber(int) this = Controls(0x5b16b8, name = "Controls")
ID Value = 896
void __thiscall Controls::WriteInfoNumber(int) this = Controls(0x5df218, name = "Controls")
ID Value = 896
void __thiscall Controls::WriteInfoNumber(int) this = Controls(0x5dfda8, name = "Controls")


If I debug into the slot for Controls, it does get hit as many times as the debug statements show.

anda_skoa
20th February 2014, 21:32
That shows the same output as for me, i.e. each Controls's slot is called once per signal emit.

Every time the signal gets emitted, the main window sends a single value to all objects that are connected to that signal.
At first there is only one object, so only one debug output it shown.
Then there are two receiver objects, so two debug outputs are shown, and so on.

A signal "broadcasts" information, anyone interested in that information can connect to the signal and get notified about these broadcasts. Each receiver is independent of any other, all receive the information equally, just like if they were the only receiver.

The current code is doing what your initial code attempted to do: having "one" object in every row, the value updating simultaniously in all rows.

Is this is not what you were aiming at then this is the wrong approach :)

Cheers,
_

astodolski
21st February 2014, 12:58
That shows the same output as for me, i.e. each Controls's slot is called once per signal emit.
For each emit of the signal UpdateInfo, a breakpoint on the slot it is connected to gets a hit more than once after the initial instance. I illustrated that with the debug output as well as the screen shot.



The current code is doing what your initial code attempted to do: having "one" object in every row, the value updating simultaniously in all rows.


Well, not exactly. One object per row, sure. The ID which I represented by a time interval is expected to be a unique number for each instance. The same value in all rows is definitely not what is intended.



Is this is not what you were aiming at then this is the wrong approach :)
Cheers,
_

I hope I at least conveyed the right approach :)

anda_skoa
21st February 2014, 13:54
For each emit of the signal UpdateInfo, a breakpoint on the slot it is connected to gets a hit more than once after the initial instance.

A breakpoint on a method is hit every time the method is invoked. Since you have multiple objects of that class, and all their slots are connected, your breakpoint is hit once per instance.



Well, not exactly. One object per row, sure. The ID which I represented by a time interval is expected to be a unique number for each instance. The same value in all rows is definitely not what is intended.

So does this value change over time?
If not, why use a signal and not just imply pass the value via a setter or constructor argument?

Cheers,
_

astodolski
21st February 2014, 15:09
A breakpoint on a method is hit every time the method is invoked. Since you have multiple objects of that class, and all their slots are connected, your breakpoint is hit once per instance.


So I am clear on what you wrote, the "method" referring to is which one? The method which seeks to update the label Controls::WriteInfoNumber is hit once for each item in the list. If there are more than one item in the list, then there are as many hits to the breakpoint in that method.



So does this value change over time?
If not, why use a signal and not just imply pass the value via a setter or constructor argument?

Cheers,
_

The value, once assigned is to be static for the remainder of the life of the object. Going to consider what was suggested.

Thanks very much.

Oh BTW your Anna Fenninger is some awesome skier:eek:

anda_skoa
21st February 2014, 17:25
So I am clear on what you wrote, the "method" referring to is which one?

The slot, WriteInfoNumber


The method which seeks to update the label Controls::WriteInfoNumber is hit once for each item in the list. If there are more than one item in the list, then there are as many hits to the breakpoint in that method.

Exactly!

The breakpoint can not distinguish between instances of the same class, so it is hit for every single one. Obviously "this" will be different.



Oh BTW your Anna Fenninger is some awesome skier:eek:

:D

Cheers,
_