PDA

View Full Version : Qt GraphicsView stop getting paint event



karankumar1609
30th July 2013, 16:29
Hello,

I am working with QGraphicsView and QGraphicsScene for drawing of QPixmap data on QGraphicsItem.
What i am doing is start a QTimer on each timeout of QTimer i draw data on QPixmap via QtConcurrent.



QtConcurrent::run(this, &LinePlotter::plotDataOnImage);


On plotDataOnImage i call update() after drawing on QPixmap.
It works fine but unfortunatly my QGraphicsView and QGraphicsScene stop getting paint events.
but when i click on window it repaint again but it does not repaint on call of update() method.

customizations for QGraphicsView i have made are:


setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOf f);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff) ;

setRenderHints(QPainter::HighQualityAntialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing);

setViewportUpdateMode(QGraphicsView::SmartViewport Update);

setCacheMode(QGraphicsView::CacheNone);

setOptimizationFlags(QGraphicsView::DontAdjustForA ntialiasing
| QGraphicsView::DontClipPainter
| QGraphicsView::IndirectPainting);


I have never got this kind of problem before but i dont know why my view not get its paint event properly.
any idea about the problem?


-----------------------------------------------
There is something i have seen i just start my application and keep it opened without any mouse interaction or any other kind of interactions.
Then i start my stop watch and checks the log. :eek:
:eek::eek:
It also got stop painting(stop getting events) when keeping in background (rarely occured)...
What is the issue?
I am using Cent OS 6.4
Qt 5.0

Please tell me what is the issue?

wysota
30th July 2013, 20:33
QPixmap cannot be used from threads other than the GUI thread.

karankumar1609
31st July 2013, 06:20
i have changed my function to drawing on QImage and then i send it a signal


signals:
void drawingFinished(const QImage&);


but the same problem occured.
the problem will not occur if i direct call that function.
Somehow it stop getting paint events when i use threads.

wysota
31st July 2013, 08:35
Show your exact code related to this call and tell us why you expect a paint event (i.e. where you call update() or sth).

karankumar1609
31st July 2013, 08:45
here is my run method implementation of QThread



void LinePlotter::run()
{
QTime timeSpan;
timeSpan.start();

forever {
const QRectF& visibleRectL = visibleRect();

qDebug() << "Timer Out : " << visibleRectL;

// *pix = pix->scaled(::max(visibleRectL.width(), 1.0), ::max(visibleRectL.height(), 1.0));
QImage image(visibleRectL.size().toSize(), QImage::Format_ARGB32);

QPainter paint;

paint.begin(&image);
paint.setRenderHint(QPainter::SmoothPixmapTransfor m, true);
paint.setBrush(QBrush(Qt::black));
paint.setClipRect(visibleRectL);
paint.drawRect(0, 0, visibleRectL.width(), visibleRectL.height());

const qreal& horizontalPP = horizontalPaddingPixel();
const qreal& verticalPP = verticalPaddingPixel();
const qreal& zoomFactorInverse = 1 / zoomFactor();
const qreal& frontL = front();
const qreal &gridY0 = 0, &gridY1 = visibleRectL.height();
const double& height = (this->boundingRect().height() - (2* verticalPP));
const double& xFactor = zoomFactor(), xPadding = + horizontalPP - ::qCeil(frontL);
int startPoint = (frontL * zoomFactorInverse) - (horizontalPP * zoomFactorInverse);
int xPos = mappingLine().x1();
uint mapperWidth = mapper()->size().width();
uint date = 00000000, time = 0000;
QVariantList points;

const QVector<PlotData*>& plotDataVector = plotData();

if(startPoint < 0)
startPoint = 0;
int endPoint = startPoint + (visibleRectL.width() * zoomFactorInverse) + 5;


foreach (PlotData* plotDataValue, plotDataVector) {
// Each vector contains the data of one day
const std::vector<ID_Element> &ID = plotDataValue->ID();

int IDIndex = 0;
foreach (const ID_Element& idElement, ID) {
int j = 0;
const KAxisRange& yRange = plotDataValue->yRange(IDIndex);
const double yFactor = 1.f / (double(yRange.max() - yRange.min()) / height);

if(endPoint > plotDataValue->totalPixel())
endPoint = plotDataValue->totalPixel();

const std::vector<Date_Element>& dateElement = idElement.ID_vector;
int timeSize = dateElement.front().Time_Vector.size();
qreal min = 0, max = 0;
bool init = true;

foreach (const Date_Element& dateData, dateElement) {

if((j + timeSize) < startPoint) {
j += timeSize;
} else {
const std::vector<Time_Element>& timeElement = dateData.Time_Vector;
QPolygonF LineToDraw;
QVector<QLineF> gridLines, halfGridLines;

foreach (const Time_Element& timeData, timeElement) {
if(j >= startPoint &&
j <= endPoint) {
// value contains some data in it
const std::vector<float>& value = timeData.Value;

qreal x = j;
x = (x * xFactor) + xPadding;

if(value.size() > 0) {
qreal y = value[0] - yRange.min();
y = height - (y * yFactor) + verticalPP;
LineToDraw << QPointF(x, y);

if(j == xPos) {
// value contains some data in it
date = dateData.Date;
time = timeData.Time;

QVariantMap mapVal;
mapVal.insert("color", plotDataValue->IDColor(IDIndex));
mapVal.insert("value", QString::number(value[0]));

points.append(mapVal);
}

if(init) {
init = false;
min = value[0];
max = min;
} else {
qreal range = value[0];

if(min > range)
min = range;

if(max < range)
max = range;
}
}

{
// Draw Grid
if(timeData.Time % 100 == 0)
gridLines << QLineF(QPointF(x, gridY0), QPointF(x, gridY1));
else if((timeData.Time % 100) % 15 == 0)
halfGridLines << QLineF(QPointF(x, gridY0), QPointF(x, gridY1));
}

} else if(j > endPoint) {
break;
}
++j;
}

/// Draw graph on pixmap
if(!LineToDraw.isEmpty()) {
paint.setPen(plotDataValue->IDColor(IDIndex));
paint.drawPolyline(LineToDraw);
}

{
paint.setPen(QPen(QBrush(Qt::red), 3));
paint.drawLines(gridLines);
}

{
paint.setPen(QPen(QBrush(Qt::gray), 1, Qt::DotLine));
paint.drawLines(halfGridLines);
}
}
}

// Update the grapg again for new range series
if(yRange.min() != min ||
yRange.max() != max) {
plotDataValue->setYRange(KAxisRange(min, max), IDIndex);
emit rangeChanged(min, max, IDIndex);
// update();
}
++IDIndex; // increase the ID index number
}
}

// Draw pixmap with graph
// Draw mapper line and set mapper position
{
paint.setPen(QPen(QBrush(Qt::green), 1));
paint.setOpacity(1.0);

// insert date time
{
QString dateString = QDateTime::fromString(QString::number(date), "yyyyMMdd").toString("yyyy-MM-dd");
QString timeString = QTime::fromString(QString::number(time), "hhmm").toString("hh:mm");

QVariantMap dateTimeMap;
dateTimeMap.insert("date", dateString);
dateTimeMap.insert("time", timeString);

points.append(dateTimeMap);
}

// Draw mapper line
{
xPos = (xPos * zoomFactor()) + xPadding;
const QLineF &lineToDraw = QLineF(QPointF(xPos, 0), QPointF(xPos, mappingLine().y2()));

xPos = xPos + 3;
if((xPos + mapperWidth) > visibleRectL.topRight().x()) { // out from right
xPos -= mapperWidth + 4;
}

// use setPos_ for animated positioning
mapper()->setPos(QPointF(xPos, 0));

mapper()->setDataValues(points);
paint.drawLine(lineToDraw);
}
}

paint.end();
}

emit image_changed(image);
}


and on SLOT i have done a simple QImage to QPixmap conversion


// pix is the pointer of QPixmap which will draw on QGraphicsItem
*pix = QPixmap::fromImage(image);


this is everything i have dont with QThread..

wysota
31st July 2013, 09:26
Ok, but where do you call update() to actually update the UI?

karankumar1609
31st July 2013, 10:04
sorry for not mentioning the update() call...
well update will be called just after the image to pixmap transformation.



*pix = QPixmap::fromImage(image);
update();


the above code is GUI SLOT.

Added after 16 minutes:

and i have implemented the QThread with QGraphicsItem as



class LinePlotter : public QThread, public QGraphicsPixmapItem, public QGraphicsLayoutItem
{
....
// CODE

void run();
....
};


Added after 17 minutes:

I have also used QtConcurrent and QThread.
Both have the same issue.
Both will block paint events after unknown time.

wysota
31st July 2013, 10:09
and i have implemented the QThread with QGraphicsItem as



class LinePlotter : public QThread, public QGraphicsPixmapItem, public QGraphicsLayoutItem
{
....
// CODE

void run();
....
};

What did you do that for?

karankumar1609
31st July 2013, 10:31
actually there are many local variables which i have to calculate during drawing.

but instead of this., if i do simple implementation like



class LinePlotter : public QObject, public QGraphicsPixmapItem, public QGraphicsLayoutItem
{
....
// CODE

void functionForQtConcurrent();
....
};


and in this i call a function ("functionForQtConcurrent") on every QTimer timeout SIGNAL,
In the functionForQtConcurrent i did everything for calculation because it is the member function of the class. and then i emit the signal at the end like i did in Qthread run().

So, in this case, i didnt do any painting on pixmap in main tyhread but still my program not recieve paint event.

wysota
31st July 2013, 11:18
When you are supposed to draw then draw, don't calculate. Calculate prior to drawing. When you're done calculating everything then call update() on all items that have changed during the calculation. Right now you might be either violating thread synchronization or just starving your application by trying to repaint the view after changing each single item separately. You might also be starving Qt Concurrent itself -- remember it uses a thread pool that has its size constrained to the number of processing cores on a machine. So scheduling threaded updates of 20 items on a machine with 2 cores will only update two items at once, so it is likely your view will have to be redrawn up to 20 times instead of just once with all the changes batched into a single call.

karankumar1609
31st July 2013, 11:29
Thanks wysota,

i will try to create a separate thread for my calculation.

But there is an another question in my mind. I have 4-5 Plot so that each will have its thread process.
and each thread will follow the same forever loop in run() function of QThread.
should i worry in this case? coz each thread works on a forever loop.?

So is there any limitation for one process to create a maximum number of thread at a time.,

wysota
31st July 2013, 11:45
Thanks wysota,

i will try to create a separate thread for my calculation.

But there is an another question in my mind. I have 4-5 Plot so that each will have its thread process.
and each thread will follow the same forever loop in run() function of QThread.
should i worry in this case? coz each thread works on a forever loop.?

So is there any limitation for one process to create a maximum number of thread at a time.,

Why do you want to create so many threads? Are you sure you really need any of them?

karankumar1609
31st July 2013, 13:04
Hello wysota,

well my concept is something like there is a scene on which we can plot many graph stack wise.
and each graph have many plot vectors (means there will be many graph lines in each graph)

so it is like

Scene ->(Multiple PlotWidgets)
plotWidget have its own thread for drawing
plotWidget can draw many graphs in it

this is why if there will be 5-6 plot widget stacked together then they will be handled by its own thread.

wysota
31st July 2013, 13:42
I still don't see why you'd want to use threads here. Contrary to a popular belief, threads do not make your programs run faster.

karankumar1609
31st July 2013, 13:55
ok i think i am unable to make u understand,
ummmm.....
well there is a mandelbrot example (http://harmattan-dev.nokia.com/docs/library/html/qt4/threads-mandelbrot.html) available which use threads.
Suppose we have to do two mandelbrot at the same time on a single mainwindow. this means in this case we have two threads execute at the same time for each mandelbrot.

well the same case is with me. each plot equals to each mandelbrot. ;)

wysota
31st July 2013, 14:46
ok i think i am unable to make u understand,
ummmm.....
well there is a mandelbrot example (http://harmattan-dev.nokia.com/docs/library/html/qt4/threads-mandelbrot.html) available which use threads.
Suppose we have to do two mandelbrot at the same time on a single mainwindow. this means in this case we have two threads execute at the same time for each mandelbrot.

well the same case is with me. each plot equals to each mandelbrot. ;)

Do you understand why the example uses a thread? How is it related to your situation? E.g. it is not true that if there were two fractals rendered, they would have required two worker threads instead of one.

karankumar1609
31st July 2013, 14:52
well i really dont know what to do in this case?
What should i do if i have to do two or more heavy work.
i really appreciate your suggestion in this case..

wysota
31st July 2013, 16:02
It depends what you mean by "heavy work". First of all the existance of your "forever" loop seems wrong. If data doesn't change there is completely no point recalculating it or redrawing it. Taken that I don't see a single mutex in your code, I'm assuming that the data doesn't change at all. If the data does change then you should protect it from concurrent access from more than one thread. Either way the loop is not correct.

karankumar1609
31st July 2013, 16:27
Thanks wysota for your suggestions.