PDA

View Full Version : Reload widget contents



tiredtyrant
8th March 2010, 19:12
Hello.
I am having trouble finding a way to update a widget. I'm modifying the fridge magnets example (http://doc.trolltech.com/4.6/draganddrop-fridgemagnets.html) that comes with the Qt SDK.
Basically what I want to do is to be able to update the DragWidget contents by pressing a button. By update, I mean redrawing the draggable labels to reflect changes in the text file that it reads in order to draw the labels. For instance, I want to change a word in the text file, and after i click a button, i want the labels to be redrawn based on the changes in the text file.
In order to do that, what I did was create a new class that would contain both the DragWidget object and the button. I called it wrapwidget, and here's its definition and implementation:


class wrapWidget: public QWidget
{
Q_OBJECT
public:
wrapWidget();

};

wrapWidget::wrapWidget()
{
QGridLayout *gridlayout= new QGridLayout();
DragWidget *w = new DragWidget();
QPushButton *b = new QPushButton("refresh");
gridlayout->addWidget(w,0,0);
gridlayout->addWidget(b,1,0);
setLayout(gridlayout);

connect(b,SIGNAL(clicked()),w,SLOT(draw()));
}

Here's the draw() slot definition:


void DragWidget::draw(){
QFile dictionaryFile(":/dictionary/words.txt");
dictionaryFile.open(QFile::ReadOnly);
QTextStream inputStream(&dictionaryFile);

int x = 5;
int y = 5;

while (!inputStream.atEnd()) {
QString word;
inputStream >> word;
if (!word.isEmpty()) {
DragLabel *wordLabel = new DragLabel(word, this);
wordLabel->move(x, y);
wordLabel->show();
wordLabel->setAttribute(Qt::WA_DeleteOnClose);
x += wordLabel->width() + 2;
if (x >= 245) {
x = 5;
y += wordLabel->height() + 2;
}
}
}
}

That code was originally in the DragWidget constructor, I moved it out to the draw() method. I tried to set draw() as a slot, and call it by connecting it with the button's clicked() signal. It says there's no such slot though. What am I doing wrong?

Any tips on how I should be doing that?

norobro
9th March 2010, 04:37
Did you possibly forget Q_OBJECT in your DragWidget header file?

aamer4yu
9th March 2010, 04:44
Can you show the declaration for class DragWidget ?
Probably you didnt add slots: to draw() :rolleyes:

tiredtyrant
9th March 2010, 11:31
@norobro

Well, apart from moving the label drawing code out of the constructor into the draw() slot, I haven't modified anything in the DragWidget class. In its original form, there's no Q_OBJECT macro in DragWidget declaration, and it compiles and runs as expected. But adding the macro to the DragWidget class, like this:


class DragWidget : public QWidget
{
Q_OBJECT
public:
DragWidget(QWidget *parent = 0);

public slots:
void draw();

protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
void mousePressEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
};

gave off this error:

collect2: ld returned 1 exit status

@aamer4yu

But I did, look at the class declaration, its there.

aamer4yu
9th March 2010, 11:38
Did you recompile ?
run qmake again

tiredtyrant
9th March 2010, 11:46
I did recompile, the 'public slots:' declaration was there all the time, I didn't add it just now.
I ran qmake through Qt Creator, but the same error comes up. This is really weird, I've no idea what I'm doing wrong.

tiredtyrant
9th March 2010, 12:11
here's the project if anyone's interested in checking out what's the problem

http://www.4shared.com/account/file/237492040/e9df9c13/fridgemagnets.html

tiredtyrant
9th March 2010, 14:54
Okay, I don't know what manner of esoteric power was holding my computer back, but I added the Q_OBJECT macro to the DragWidget class and this time running qmake and running it again worked fine. Really weird.

My next step was storing the created labels in a QList and delete them before calling draw(). Seems to be working now. Here's the code to my modified DragWidget class:


class DragWidget : public QWidget
{
Q_OBJECT
public:
DragWidget(QWidget *parent = 0);

public slots:
void draw();
void store();
void refreshWidget();

protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
void mousePressEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);

private:
QList<DragLabel*> labels;
};

and the implementation:


DragWidget::DragWidget(QWidget *parent)
: QWidget(parent)
{
store();
draw();
QPalette newPalette = palette();
newPalette.setColor(QPalette::Window, Qt::white);
setPalette(newPalette);

setMinimumSize(400, 100);//qMax(200, y));
setWindowTitle(tr("Fridge Magnets"));
setAcceptDrops(true);
}

void DragWidget::draw(){
int x = 5;
int y = 5;
foreach(DragLabel* label, labels){
label->move(x,y);
label->show();
label->setAttribute(Qt::WA_DeleteOnClose);
x += label->width() + 2;
if (x >= 245) {
x = 5;
y += label->height() + 2;
}
}
}

void DragWidget::refreshWidget(){
store();
draw();
}


void DragWidget::store(){
foreach(DragLabel* label, labels){
label->deleteLater();
}
QFile dictionaryFile("../words.txt");
dictionaryFile.open(QFile::ReadOnly);
QTextStream inputStream(&dictionaryFile);

labels.clear();

while (!inputStream.atEnd()) {
QString word;
inputStream >> word;
if (!word.isEmpty()) {
DragLabel *wordLabel = new DragLabel(word, this);
labels.append(wordLabel);
}
}
}

and the connect statement now calls refreshWidget() as a slot instead of draw(). It works fine now if you don't drag any labels before clicking the button. If i drag a label out of place and click the button, the program crashes. Any idea why that happens?

norobro
9th March 2010, 15:12
Glad that you got it running. I was typing the following (slow typist) while you posted. I'll take a look at your next problem.

You have a stray comma in the wrapwidget.h that you uploaded:
#define WRAPWIDGET_H,
I removed the comma, uncommented Q_Object in dragwidget.h, reran qmake and it compiled.

tiredtyrant
9th March 2010, 17:32
Here's the project, the button works but dragging a label before clicking the button crashes the program

http://www.4shared.com/file/237667788/b6b88b40/fridgemagnets_2.html

I think it's because of the drop, but I'm not really sure what to do to fix this behaviour. I tried to append the new label created by the drop to the QList, but that didn't solve the problem.

norobro
9th March 2010, 18:44
When you drag an item a new QLabel is created in DragWidget::dropEvent. That seems to invalidate your QList "labels" resulting in a segfault.

Try this:
void DragWidget::store(){
QList <QObject *> newList = this->children();
foreach(QObject * label, newList) label->deleteLater();
// foreach(DragLabel* label, labels){
// label->deleteLater();
// }

tiredtyrant
9th March 2010, 19:18
It worked! I was so sure the error was on dropEvent()!
Could you elaborate more on why the segfault was happening? Also, you declared a QObject* list, could it have been a DragWidget* list instead?

How could I go about retrieving a particular set of objects instead of all the children?

norobro
10th March 2010, 18:27
Sorry for the delay in responding. Right after my last post I lost my internet connection. Called my ISP and as usual their first reaction was to blame the customer's equipment, in this case my 7 year old modem. So I bought a new modem and they still couldn't get me connected. Had to send out a service tech and of course the problem was on their end.


void DragWidget::mousePressEvent(QMouseEvent *event)
{
. . .
if (drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction) == Qt::MoveAction)
child->close();
. . .
When you move a "magnet" a new DragLabel is created and the above statement deletes the old one which leaves you with a dangling pointer in your list "labels". This results in a segfault when you iterate through the list because you are trying to delete a memory location that has already been deallocated. Try commenting out "child->close()" and use your foreach statement in DragWidget::store().

I tried using a DragLabel list first and got compile errors thus the QObject * list. You make a good point- you might not want to delete all of an object's children. I did find this (http://doc.qt.nokia.com/4.6/qobject.html#findChildren) in the docs so in this case the following should find all of the DragLabel children:
QList<DragLabel *> newList = this->findChildren<DragLabel *>();
Hope this makes sense.

tiredtyrant
10th March 2010, 19:39
Sorry to hear you had trouble with your ISP, I'm all too familiar with your situation. Nice to have you back on the internets ;)

I see why the application was crashing now. Thanks a bunch for your help!