PDA

View Full Version : Weird problems when destructor is called



cyberboy
21st October 2008, 12:17
Hi everybody,


I'm busy for a long time on this project, and currently it's killing me.

The goal is to print a excel sheet look-a-like this is the code that I wrote.

The function that invokes the proces


void MainWindow::printSurvey()
{
QPrinter printer;

printer.setOrientation(QPrinter::Landscape);

QPrintDialog dlg(&printer, this);
if(dlg.exec() == QDialog::Accepted)
{
TablePrint *print = new TablePrint(printer, this);


print->setOffsetTop(10);
print->setOffsetBottom(10);
print->setOffsetLeft(10);
print->setOffsetRight(10);

QMap<int, QMap<QString, QString> > header;
QMap<int, QMap<int, QMap<QString, QString> > > data;

//header format header[columnNumber][identifier] = value
header[0]["width"] = "100";
header[0]["height"] = "20";
header[0]["fillColor"] = "blue";
header[0]["text"] = "column1";
header[1]["width"] = "100";
header[1]["height"] = "20";
header[1]["fillColor"] = "blue";
header[1]["text"] = "column2";
header[2]["width"] = "100";
header[2]["height"] = "20";
header[2]["fillColor"] = "blue";
header[2]["text"] = "column3";

//data format data[rowNumber][columnNumber][identifier] = value
data[0][0]["text"] = "column_1_row_1_artikel";
data[0][0]["fillColor"] = "white";
data[0][1]["text"] = "column_2_row_1_artikel";
data[0][1]["fillColor"] = "red";
data[0][2]["text"] = "column_3_row_1_artikel";
data[0][2]["fillColor"] = "green";

data[1][0]["text"] = "column_1_row_2_artikel";
data[1][0]["fillColor"] = "white";
data[1][1]["text"] = "column_2_row_2_artikel";
data[1][1]["fillColor"] = "red";
data[1][2]["text"] = "column_3_row_2_artikel";
data[1][2]["fillColor"] = "green";

data[2][0]["text"] = "column_1_row_3_artikel";
data[2][0]["fillColor"] = "white";
data[2][1]["text"] = "column_2_row_3_artikel";
data[2][1]["fillColor"] = "red";
data[2][2]["text"] = "column_3_row_3_artikel";
data[2][2]["fillColor"] = "green";

data[3][0]["text"] = "column_1_row_4_artikel";
data[3][0]["fillColor"] = "white";
data[3][1]["text"] = "column_2_row_4_artikel";
data[3][1]["fillColor"] = "red";
data[3][2]["text"] = "column_3_row_4_artikel";
data[3][2]["fillColor"] = "green";



print->dataPrint(header, data);

delete print;

}



}


I divided the responsibility to several scripts:

Tableprint
Tablerow
Tablecolumn
Tablecell


Maybe you all ready noticed that you don't need 3 things to receive the coordinates of a 2D frame.

So what I did was delegating the responsibility to table row. So you can see tablerow as the controller. The row controls the columns which controls the cells.

A simple example, when you want to retrieve row 2 column 1 than you will access the table row, which can access the column in this case 1 and the column contains all the cell beneath him, so we pass the parameter 2 so the column knows that we want to retrieve cell 2.

I thought that this idea would be the solution to my problem, but it's giving me troubles.

Here are the other scripts:

Tableprint.h


#ifndef TABLEPRINT_H
#define TABLEPRINT_H

#include <QObject>
#include <QPainter>
#include <QStringList>
#include <QPrinter>
#include <QString>
#include <QList>

class TableRow;


class TablePrint : public QObject
{
Q_OBJECT
public:
TablePrint(QPrinter&, QObject *parent = 0);

//setters
void setOffsetTop(int offset);
void setOffsetLeft(int offset);
void setOffsetRight(int offset);
void setOffsetBottom(int offset);
void setDynamicHeight(bool b);

//print functions
void dataPrint(QMap<int, QMap<QString, QString> > header, QMap<int, QMap<int, QMap<QString, QString> > > data);

~TablePrint();
private:
int offsetTop;
int offsetLeft;
int offsetRight;
int offsetBottom;
int width;
int height;
bool dynamicHeight;
int rows;
TableRow *tableRow;
QPainter painter;

};

#endif

Tableprint.cpp

#include "tablePrint.h"
#include <QtGui>
#include <QPainter>
#include <QPrinter>
#include "tableRow.h"
#include <QMap>
#include <QMapIterator>


TablePrint::TablePrint(QPrinter& printer, QObject *parent)
: QObject(parent), painter(&printer){
qDebug("TablePrint::TablePrint():start printing");
this->rows = 0;
this->offsetTop = 0;
this->offsetRight = 0;
this->offsetBottom = 0;
this->offsetLeft = 0;
this->dynamicHeight = false;
this->width = 0;
this->height = 0;


this->width = printer.pageRect().width() - this->offsetLeft - this->offsetRight;
this->height = printer.pageRect().height() - this->offsetBottom - this->offsetTop;




}


void TablePrint::dataPrint(QMap<int, QMap<QString, QString> > header, QMap<int, QMap<int, QMap<QString, QString> > > data)
{
qDebug("TablePrint::dataPrint():printing row data");

this->tableRow = new TableRow(QRect(this->offsetLeft, this->offsetTop, (this->width - this->offsetLeft - this->offsetRight), (this->height - this->offsetTop - this->offsetBottom)), this);

//set the header data
this->tableRow->setHeaderData(header);

//sets the dynamic height to false, so all the cells will have the same height
this->tableRow->setDynamicHeight(false);


QMap<int, QMap<int, QMap<QString, QString> > >::const_iterator i = data.constBegin();

//loop the iterator
while(i != data.constEnd() )
{
QMap<int, QMap<QString, QString> > rowData = data.value(i.key());
this->tableRow->setRowData(rowData);
this->rows++;

i++;

}

//column print
this->tableRow->draw(0, this->painter);

for( int i = 2; i < (this->rows+2); i++)
{
this->tableRow->draw(i, this->painter);


}


this->painter.end();


}

void TablePrint::setOffsetTop(int offset){this->offsetTop = offset; if(!this->offsetBottom) this->offsetBottom = offset;};
void TablePrint::setOffsetLeft(int offset){this->offsetLeft = offset; if(!this->offsetRight) this->offsetRight = offset;};
void TablePrint::setOffsetRight(int offset){this->offsetRight = offset;};
void TablePrint::setOffsetBottom(int offset){this->offsetBottom = offset;};
void TablePrint::setDynamicHeight(bool b){this->dynamicHeight = b;};

Tablerow.h

#ifndef TABLEROW_H
#define TABLEROW_H

#include <QObject>
#include <QPainter>
#include <QStringList>
#include <QRect>

class TableCell;
class TableColumn;

class TableRow : public QObject
{
Q_OBJECT
public:
TableRow(QRect, QObject *parent = 0);
void setHeaderData(QMap<int, QMap<QString, QString> > data);
void setRowData(QMap<int, QMap<QString, QString> > data);
void draw(int row, QPainter&);
void setRemember(bool rememberLocal){this->remember = rememberLocal;};
void setDynamicHeight(bool b) {this->dynamicHeight = b;};
int getMaxColumnHeight(){return this->maxColumnHeight;};
~TableRow();

private:
int columns;
int currentRow;
int maxColumnHeight;
TableColumn *tableColumns[];

QRect rectArea;
bool remember;
int offsetTop;
int offsetLeft;
int width;
bool dynamicHeight;

QObject *parent;
};


#endif




To be continued in the next post

cyberboy
21st October 2008, 12:18
Tablerow.cpp

#include "tableRow.h"
#include <QMap>
#include <QList>
#include <QMapIterator>
#include <QDebug>
#include "tableCellPrinter.h"
#include "tableColumnPrint.h"

TableRow::TableRow(QRect rect, QObject *parent)
: QObject(parent), parent(parent)
{
qDebug("TableRow::TableRow(): Start drawing the tables");
this->maxColumnHeight = 0;
qDebug("rect.width:%i", rect.width());

this->offsetLeft = rect.x();
this->offsetTop = rect.y();
this->width = rect.width();
this->currentRow = 0;

}



void TableRow::setHeaderData(QMap<int, QMap<QString, QString> > data)
{
qDebug("TableRow::setHeaderData(): Set the header data");
this->columns = data.count();

QMap<int, QMap<QString, QString> >::const_iterator i = data.constBegin();

int totalOffsetLeft = 0;

//loop the iterator
while(i != data.constEnd() )
{
QMap<QString, QString> rowData = data.value(i.key());


QRect rectColumn(totalOffsetLeft, this->offsetTop, rowData["width"].toInt(), 0);

//remember the highest height, so we can apply it to all columns!
if(rowData["height"].toInt() > this->maxColumnHeight)
this->maxColumnHeight = rowData["height"].toInt();
qDebug("keeeeyyy:%i", i.key());
this->tableColumns[i.key()] = new TableColumn(QColor(rowData["fillColor"]), rowData["text"], rectColumn, this);


i++;
totalOffsetLeft += rowData["width"].toInt();

}


}

void TableRow::setRowData(QMap<int, QMap<QString, QString> > data)
{
qDebug("TableRow::setRowData(): set the row data");
// add cell to the matching table column!
qDebug("this->width:%i", this->width);

QMap<int, QMap<QString, QString> >::const_iterator i = data.constBegin();

//loop the iterator
while(i != data.constEnd() )
{
QMap<QString, QString> rowData = data.value(i.key());

if(rowData["height"].toInt() > this->maxColumnHeight)
this->maxColumnHeight = rowData["height"].toInt();
qDebug("column number to add a cell to:%i", i.key());

this->tableColumns[i.key()]->addCell(this->currentRow, QColor(rowData["fillColor"]), QString(rowData["text"]));

i++;
}

this->currentRow++;




}

void TableRow::draw(int row, QPainter &painter)
{
//lets start drawing!
qDebug("TableRow:draw(): start drawing");

qDebug("this->width:%i", this->width);

int offsetLeftColumn = this->offsetLeft;
int offsetTopColumn = (row == 0)?this->offsetTop : this->offsetTop + this->maxColumnHeight*(row-1);
//loop each column
for(int i = 0; i < this->columns; i++)
{
//check if the column stays on the page
if(offsetLeftColumn + this->tableColumns[i]->columnWidth < this->width ){
this->tableColumns[i]->draw(row, offsetLeftColumn, offsetTopColumn, painter);

offsetLeftColumn += this->tableColumns[i]->columnWidth;

}else{
qDebug("column width:%i width:%i", offsetLeftColumn + this->tableColumns[i]->columnWidth, this->width);
}


}



offsetLeftColumn = 0;
offsetTopColumn = 0;


}


TableRow::~TableRow()
{

qDebug("Destruct rows");
for(int i = 0; i < this->columns; i++)
{
delete this->tableColumns[i];
}
}
Tablecolumn.h

#ifndef TABLECOLUMNPRINT_H
#define TABLECOLUMNPRINT_H

#include <QObject>
#include "tableCellPrinter.h"

class QRect;
class QPainter;

class TableColumn : public QObject
{
Q_OBJECT
public:
TableColumn(QColor colorfill, QString text, QRect &rectColumn, QObject *parent);
void addCell(int row, QColor colorfill, QString text);
void draw(int row, int offsetLeft, int offsetTop, QPainter &painter);
int columnWidth;
~TableColumn();

TableCell *cells[];
private:
int columnHeight;
int cellsCount;
QObject *parent;
};


#endif



Tablecolumn.cpp

#include "tableColumnPrint.h"
#include "tableCellPrinter.h"
#include <QColor>
#include <QString>
#include <QRect>
#include <QPainter>
#include <QDebug>

TableColumn::TableColumn(QColor colorfill, QString text, QRect &rectColumn, QObject *parent)
:QObject(parent), cellsCount(0)
{
qDebug("TableColumn::TableColumn(): Define header column");
this->cellsCount = 0;

this->columnWidth = rectColumn.width();
this->columnHeight = rectColumn.height();


this->cells[0] = new TableCell(colorfill, text, this);

this->parent = parent;
}




void TableColumn::addCell(int row, QColor colorfill, QString text)
{
qDebug("TableColumn::addCell(): add a cell to the column");
row += 2;
this->cells[row] = new TableCell( colorfill, text, this);
qDebug("###########################################");
qDebug() << this->cells[row]->text;
qDebug("column:");
qDebug() << this->cells[0]->text;
qDebug("cel number for this column:%i", (row));
qDebug("###########################################");

//the plus one is there so we don't overwrite the header column
this->cellsCount = row;

}

void TableColumn::draw(int row, int offsetLeft, int offsetTop, QPainter &painter)
{
//draw the row
qDebug("TableColumn:draw(): start drawing");

painter.setBrush(this->cells[row]->fillColor);
painter.setPen(Qt::black);

qDebug("Drawing area: offsetLeft:%i offsetTop:%i width:%i height:%i", offsetLeft, offsetTop, this->columnWidth, this->columnHeight);

QRect rowRect(offsetLeft, offsetTop , this->columnWidth, 20);

qDebug("row for this column:%i", row);

painter.drawRect(rowRect);


this->cells[row]->draw(painter, this->cells[row]->text, offsetLeft, offsetTop , this->columnWidth, 20);
}

TableColumn::~TableColumn()
{
qDebug("Destruct column");

delete this->cells;

}



Tablecell.h

#ifndef TABLECELLPRINTER_H
#define TABLECELLPRINTER_H

#include <QObject>
#include <QColor>
#include <QString>
#include <QRect>

class QPainter;

class TableCell : public QObject
{
Q_OBJECT
public:
TableCell(QColor fillColor, QString &txt, QObject *parent =0);
void draw(QPainter&, QString &txt, int offsetLeft, int offsetTop , int width, int height);
~TableCell();
QColor fillColor;
QString text;

};

#endif
Tablecell.cpp

#include "tableCellPrinter.h"
#include <QDebug>
#include <QPainter>
#include <QString>
#include <QObject>

TableCell::TableCell(QColor fillColor, QString &txt, QObject *parent)
:QObject(parent)
{
qDebug("TableCell::TableCell(): Initialize a new cell");

this->fillColor = fillColor;
this->text = QString(txt);
}


void TableCell::draw(QPainter &painter, QString &txt, int offsetLeft, int offsetTop , int width, int height)
{
//start drawing the text
qDebug("TableCell:draw(): start drawing");

painter.drawText(QRect(offsetLeft, offsetTop, width, height), Qt::AlignCenter, txt);
}

TableCell::~TableCell()
{

qDebug("destruct cell");
qDebug() << this->text;
}


The script works fine with some test data inserted in mainWindow.cpp but when I add more test data, let's say 4 rows and 1 column row.
The whole program crashes, and in my debugger I saw that it crashed when the destructor of the QObject is called!

Can somebody help me please, I'm desperate.

Many many thanks,

Cyberboy

aamer4yu
21st October 2008, 12:48
It would have been better if you had posted a compilable attachment /files, instead of writing the whole code. ;)

cyberboy
21st October 2008, 12:54
Smart idea

This is the link to the zip file with all the files of the complete project!
http://www.zchosting.nl/BestelApplicatie.zip

Greetings,

Cyberboy

aamer4yu
21st October 2008, 12:57
Is the zip file filename correct ? I am not able to open it. However only the site address is accessible.

EDIT : Now it opens... may be i tried a little early :D

EDIT : File corrupt :(

cyberboy
21st October 2008, 13:11
I'm sorry I posted the link while I was still uploading the project. So that may explain the corrupt file.

I uploaded the project again, and now I finished uploading before I posted this message!

Kumosan
21st October 2008, 13:15
No idea why the destructor is called. I am a bit reluctant to go through the whole code. It is too much 'fun' to read.

But in Tablecolumn.cpp (addCell) you add 2 to row, leaving one cells empty, i.e. cells[1] is never initialized (I think).

Furthermore you delete an array with 'delete' instead of 'delete []'. This is for sure a bug.

There are many more problems. Mostly just bad style, e.g. passing QString where const QString & would be sufficient, which btw. should be no problem, but strangely was the cause for spurious crashes in an old Qt 3.0 program I wrote years ago.

cyberboy
21st October 2008, 13:27
That's the problem with this project, I have rewritten the code so many times that the original style is gone for a long time.....

But this is a beautiful moment to learn from!

I couldn't find the reason why cells[1] isn't initialized, but after guessing and playing around I found a solution (A BAD ONE) I was glad that it worked not mentioning the state.

But do you advice to rewrite the whole project.... or just to rewrite the tableprint part....

Kumosan
21st October 2008, 13:44
But do you advice to rewrite the whole project.... or just to rewrite the tableprint part....

I don't know the whole project, but a refactoring is always a good idea. ;-)

For instance I'd throw out the whole *cells[]. Use QVector, or if you must an STL vector, but forget the ancient C stuff. Always trouble.

Btw. even if you delete cells with 'delete []' instead of 'delete', there is a problem. Your cells are as far as I can see children of TableColumn and therefore of QObject, so they would be automatically deleted when the parent is deleted. No need to do this manually in the destructor.

Do you originally come from a JAVA environment? Prepending every member variable with 'this->' is not wrong, but not necessary and make IMHO the whole program harder to read.

cyberboy
21st October 2008, 13:59
No a php environment..

I hope that I have enough time to rewrite it today!

Many thanks for the useful advices!

Greetings,


Cyberboy