PDA

View Full Version : QGLWidget doesn't update



qtbnl
10th February 2011, 15:33
Hi,

I modified the example project 2dpainting coming with QT. If I send the timeOut signal directly to the slot animate() in GLWidget class, everything works fine. However, if I send the timeOut signal to another slot calc() in main window, do something in calc(), then emit a new signal newData() to animate(), the program got crashed.

Could you please take a look at the code? I attached it to this post.

Thanks in advance for the help.

QtBNL

franz
10th February 2011, 16:22
Your GLWidget::animate() function assumes that sender() is a QTimer. You might consider changing that.

On a side note, this was fairly easy to figure out using a debugger. QtCreator can use one as well.

qtbnl
10th February 2011, 17:47
Franz,

Thanks for your reply. I am kind of newBie to Qt and appreciate your help if you explain more for me.

Basically, there are three "connect" statements in the constructor of Window class. If I use the first one only (that's the case of emitting timeOut signal directly to animate), everything works.

If I comment out the first statements and use the next two statements, I am emitting timeOut to calc, and from there emitting newData to animate. In this case, the program crashes.

Could you please tell me what's wrong?

Thanks,
QtBNL

qtbnl
11th February 2011, 15:18
Dear All,
I am still waiting for somebody to help me with this issue. As a newBie to Qt, I and don't know too much about the internal of Qt and completely lost my way at this point.

I appreciate your help in advance.

QtBNL

wysota
11th February 2011, 16:43
What's wrong with the reply you were given?

qtbnl
11th February 2011, 17:02
wysota,

Franz's first reply didn't solve the problem I have. Franz said "Your GLWidget::animate() function assumes that sender() is a QTimer". Actually that's what I need if I want to send timeOut signal directly to animate. For the other case that I want timeOut signal to trigger a data processing function, e.g. calc() and then emit newData signal to animate after the data processing is done, I have to comment out that statement and enable the other two as shown below. In this case, my program crashes.

// connect(timer, SIGNAL(timeout()), openGL, SLOT(animate()));
connect(timer, SIGNAL(timeout()), this, SLOT(calc()));
connect(this, SIGNAL(newData()), openGL, SLOT(animate()));

For some reasons, my attachment is corrupted. I am attaching it again.

Thanks,
QtBNL

wysota
11th February 2011, 17:41
I don't see your point. The answer you were given is 100% precise. If you reemit the signal from a different class than QTimer then qobject_cast will return 0 and your modified application will crash.

qtbnl
11th February 2011, 19:03
wysota,

Let's see the following code

case 1.

connect(timer, SIGNAL(timeout()), openGL, SLOT(animate()));
//connect(timer, SIGNAL(timeout()), this, SLOT(calc()));
//connect(this, SIGNAL(newData()), openGL, SLOT(animate()));

case 2.

// connect(timer, SIGNAL(timeout()), openGL, SLOT(animate()));
connect(timer, SIGNAL(timeout()), this, SLOT(calc()));
connect(this, SIGNAL(newData()), openGL, SLOT(animate()));

with calc() as

void Window::calc()
{
{
do somthing here for data processing;
}

emit newData();//data);
}


What's the difference between 1 and 2? Why does 2 cause crash?

Thanks,
QtBNL

stampede
11th February 2011, 19:20
I guess more people will be able to help if you post your code for 'animate' and 'calc' methods.
Previous answer from wysota explains everything. Look, if you do this:

void Window::calc()
{
//...
emit newData();
}
then "sender()" in 'animate()' is not a QTimer anymore, its now an instance of Window, so qobject_cast<QTimer*>(sender()) will return 0.

qtbnl
11th February 2011, 19:38
I am attached my code.

window.cpp

Window::Window()
: QWidget()
{
GLWidget *openGL = new GLWidget(&helper, this);

QTimer *timer = new QTimer(this);
// connect(timer, SIGNAL(timeout()), openGL, SLOT(animate()));
connect(timer, SIGNAL(timeout()), this, SLOT(calc()));
connect(this, SIGNAL(newData()), openGL, SLOT(animate()));
timer->start(250);

ellipse = 0.0;

setWindowTitle(tr("2D Painting on Native and OpenGL Widgets"));
}
//! [0]

void Window::calc()
{
// ...
emit newData();
}



GLWidget::GLWidget(Helper *helper, QWidget *parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
elapsed = 0;
setFixedSize(600, 600);
setAutoFillBackground(false);
}

void GLWidget::animate()
{
elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
repaint();
}

void GLWidget::paintEvent(QPaintEvent *event)
{
QImage image(100, 100, QImage::Format_RGB16);
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
image.setPixel(i,j,rand());//data[i][j]);
// qDebug() << image.pixel(i,j);
}
}
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setViewport(0,0,600,600);
painter.scale(6,6);
painter.fillRect(event->rect(), QBrush(QColor(64, 32, 64)));
painter.drawImage(0,0,image);
painter.end();

}

Thanks in advance for the help.
QtBNL

totem
11th February 2011, 21:28
Has said at least twice, in void GLWidget::animate() qobject_cast<QTimer*>(sender()) will probably return NULL pointer

wysota
11th February 2011, 23:06
I'm in a good mood, let's analyze the code together.

I'm skipping irrelevant parts of the code.


Window::Window()
: QWidget()
{
GLWidget *openGL = new GLWidget(&helper, this);

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(calc()));
connect(this, SIGNAL(newData()), openGL, SLOT(animate()));
timer->start(250);
}

The above code makes two connections which are quite obvious so let's not explain them.


void Window::calc()
{
// ...
emit newData();//data);
}

The calc() routine emits "newData()" signal on behalf of an instance of a Window class.


void GLWidget::animate()
{
elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
repaint();
}

Now the hard part, let's go step by step. The animate() routine says


qobject_cast<QTimer*>(sender())
"if sender() is a QTimer() then return a pointer to it cast to QTimer, otherwise return 0".


elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
"Do some math asking for QTimer::interval() as part of the calculation".

Since sender() is Window and not QTimer, qobject_cast returns 0 which effectively transforms the last expression to:

elapsed = (elapsed + null->interval()) % 1000;
Since null is not QTimer, then asking for its interval() member fails and causes a crash.

Do you understand what the problem is?

qtbnl
14th February 2011, 17:41
Wysota,

I appreciate your help with such detailed explanation. You're a nice person.

QtBNL