Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: AlexeyChe от Март 18, 2011, 12:12



Название: оптимизация paint() QGraphicsItem'а
Отправлено: AlexeyChe от Март 18, 2011, 12:12
Доброго дня!

Ковыряюсь уже давно, но к сожалению не хватает опыта.
Задача нарисовать систему координат +прочие простые элементы, типа квадратиков кружочков, которых может быть не малое количество.
в основе граф.части лежит класс PolarPlot наследованный от QGraphicsView, в конструкторе которого создаётся scene

Код:
plotScene=new QGraphicsScene(0,0,700,700);
    this->setScene(plotScene);
   
после чего добавляется система координат в виде объекта класса PolarAxis наследованного от QGraphicsItem
Код:
    pa = new PolarAxis(350,350,300,cRotate);
    plotScene->addItem(pa);
   
в PolarAxis перегружен метод paint в котором и происходит всё рисование, не буду вдаваться в подробности рисования, ибо оно проходит нормально, да и ничего сложного там нет.

Всё работает, но очень медленно, дебаггер говорит что тот самый метод paint вызывается огромное количество раз, загружая систему под максимум. Про resizeEvent и написанный там scale, и нечего говорить, работает, но медленно.

Погуглил. Нашел Double Buffering. Я может толком его не понял, но пытаюсь сделать таким образом:
всё рисование идет в Pixmap, созданный здесь(конструктор PolarPlot):
Код:
    pa = new PolarAxis(350,350,300,cRotate);
    pm=new QPixmap(rectScene->height(),rectScene->width());
    pa->setPixmap(pm);  //мой метод в котором адрес Pixmap передается в поле данных QPixmap* в PolarAxis
    plotScene->addItem(pa);
   

рисование осуществил в методе класса PolarAxis  drawToBuff(), в котором создается и закрывается QPainter таким образом
Код:
QPainter *p=new QPainter(pm);  //pm - адрес того самого Pixmap'a
//всяческое рисование
p->end();

в PolarAxis::paint()
Код:
    if(!drawOk) drawToBuff();  //читай: если не нарисовано - рисуй!
    p->drawPixmap(0,0,*pm);

итого, рисуется просто черный квадрат
я наверно не правильно использую Pixmap, и рисование в него.

+многопоточность!
я не понимаю саму структуру как мне создать отдельный поток для рисования, посмотрел забугорные примеры, в основном, рисование идет прямо в классе наследованном от QThread, просто процедурой. Единственный ли это способ? хотелось бы сохранить структуру классов адекватной, чтобы рисование было возложено на объекты класса графических элементов.

буду благодарен за помощь


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: GreatSnake от Март 18, 2011, 12:19
Цитировать
в PolarAxis перегружен метод paint в котором и происходит всё рисование, не буду вдаваться в подробности рисования, ибо оно проходит нормально, да и ничего сложного там нет.
Получается что сей класс помимо осей отрисовывает "простые элементы, типа квадратиков кружочков, которых может быть не малое количество". ?


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: AlexeyChe от Март 18, 2011, 13:43
Получается что сей класс помимо осей отрисовывает "простые элементы, типа квадратиков кружочков, которых может быть не малое количество". ?
Нет, этот класс рисует только ось
в будущем планируется помимо осей добавлять на сцену самостоятельные объекты классов, наследованных от QGraphicsItem, аналогичных PolarAxis
Мне бы хотя бы оси нормально нарисовать
элементы типа квадратиков кружочков я упомянул здесь только для того чтобы показать как необходима адекватная оптимизация




Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: GreatSnake от Март 18, 2011, 13:48
Пока не покажешь как отрисовываешь элементы и как масштабируешь сцену никто тебе не поможет.
Но, то что сам пытаешься реализовать double-buffering, не решив проблемы с отрисовкой это не есть хорошо, тем более, что оный встроен в Qt.


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: AlexeyChe от Март 18, 2011, 14:16
Пока не покажешь как отрисовываешь элементы...

окей
Код:
void PolarAxis::paint(QPainter *p, const QStyleOptionGraphicsItem *option,QWidget *widget)
{
    p->setMatrix(scale_matrix);
    p->drawEllipse(x-rad,y-rad,rad*2,rad*2);
    p->drawLine(x,y,x,y-rad);

    QPen *dotPen=new QPen(Qt::DashDotLine);
    dotPen->setWidthF(0.3);
    QVector<qreal> dashes;
    dashes <<10 <<10;
    dotPen->setDashPattern(dashes);
    p->setPen(*dotPen);

    for (int i=rad-15;i>0;i-=15)
         p->drawEllipse(x-i,y-i,i*2,i*2);
         for (int i=0; i>-360; i-=10){
             p->drawLine(x,y,x+rad*cos(i*pi/180),y+rad*sin(i*pi/180));
             double x2=x+(rad+20)*cos(i*pi/180+90*pi/180+rotate*pi/180)-15;
             double y2=y+(rad+20)*sin(i*pi/180+90*pi/180+rotate*pi/180)-7.5;
             p->drawText(x2,y2,30,15,Qt::AlignCenter,QString("%1").arg(abs(i)));
         }
}
ничего интересного, тупое рисование.
масштабирование:
Код:
void PolarPlot::resizeEvent(QResizeEvent *re)
{
        QRect *rct=new QRect(0,0,re->size().height()-25,re->size().width()-25);
        plotScene->setSceneRect(*rct);
        double h1=re->size().height();
        double h2=re->oldSize().height();
        double koef=h1/h2;
        pa->rescale(koef);   //в pa объект класса PolarAxis
       
}
//----------------------

void PolarAxis::rescale(float f)
{
    scale_matrix.scale(f,f);  //scale_matrix типа QMatrix, используется первой строчкой в PolarAxis::paint
}


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: GreatSnake от Март 18, 2011, 14:23
Где PolarAxis::boundingRect() ?
Зачем аллакируете в куче и забываете
Цитировать
QPen *dotPen=new QPen(Qt::DashDotLine);
QRect *rct=new QRect(0,0,re->size().height()-25,re->size().width()-25);
???

Зачем при ресайзе меняете размер сцены и потом применяете матрицу?
Чем QGraphicsView::fitInView() не устроил?

Чем не устроил QString::number() при отрисовке числа?


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: AlexeyChe от Март 18, 2011, 14:56
Где PolarAxis::boundingRect() ?
Код:
QRectF PolarAxis::boundingRect() const
{
    return QRectF(0,0,rad*2+80,rad*2+80);
}
проект сырой, но если выкладывать весь код, то займет очень много места
без boundingRect ничего бы и не запустилось, так что я перенес этот пункт в категорию "очевидное"

вообще по сути, если убрать всё масштабирование, припонки с double buffering, и прочую ересь, проект будет и так страшно лагать, рисуя только оси

ЗЫ. про FitInView, просто незнал, спасибо


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: GreatSnake от Март 18, 2011, 15:06
Дело в том, QGraphicsItem заточен под динамику, а PolarAxis по сути статика.
Поэтому предлагаю перенести отрисовку PolarAxis в свою PolarPlot::drawBackground().
Если оставить PolarAxis элементом, то т.к. он является фоном для других мелких элементов, то он будет всегда перерисовываться при изменении последних. А это явные и большие тормоза.

Уже не раз здесь поднималась такая проблема. Странно, что в Qt книжках про такие ньюансы ни слова.
Может когда-нибудь соберусь написать "tips & tricks" про QGraphicsView.


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: AlexeyChe от Март 18, 2011, 15:16
GreatSnake, спасибо большое!
и впрямь странно что в книжках нету
только вот на будущее, у меня ведь элементов будет не меньше чем line'ов и string'овых лейблов на осях, что мне почитать чтобы делать нормальное отображение?
да и Pixmap, как правильно его использовать. я руководствовался в основном этой статьей
http://doc.trolltech.com/qq/qq06-flicker-free.html#paintingbycandlelight

сделал через drawBackgroung, нормально, только вот при изменении размеров, или наезде другого окна, то есть при любом намеке на repaint лагает. а значит фиговый встроенный double buffering или я не умею им пользоваться


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: GreatSnake от Март 18, 2011, 21:45
Цитировать
или я не умею им пользоваться
Более чем уверен, что именно так)

Цитировать
при любом намеке на repaint лагает
Как это проявляется? Давай код drawBackground().



Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: m_ax от Март 18, 2011, 22:05
Хм..
Цитировать
Код
C++ (Qt)
void PolarAxis::paint(QPainter *p, const QStyleOptionGraphicsItem *option,QWidget *widget)
{
   p->setMatrix(scale_matrix);
   p->drawEllipse(x-rad,y-rad,rad*2,rad*2);
   p->drawLine(x,y,x,y-rad);
 
   QPen *dotPen=new QPen(Qt::DashDotLine); // Зачем?
   dotPen->setWidthF(0.3);
   QVector<qreal> dashes;
   dashes <<10 <<10;
   dotPen->setDashPattern(dashes);
   p->setPen(*dotPen);
 
   for (int i=rad-15;i>0;i-=15)
        p->drawEllipse(x-i,y-i,i*2,i*2);
        for (int i=0; i>-360; i-=10){
            p->drawLine(x,y,x+rad*cos(i*pi/180),y+rad*sin(i*pi/180));
            double x2=x+(rad+20)*cos(i*pi/180+90*pi/180+rotate*pi/180)-15;
            double y2=y+(rad+20)*sin(i*pi/180+90*pi/180+rotate*pi/180)-7.5;
            p->drawText(x2,y2,30,15,Qt::AlignCenter,QString("%1").arg(abs(i)));
        }
}
 
Во-первых зачем создавать dotPen в куче?
Во-вторых из-за него у вас утечка, поскольку не видно где он уничтожается
В-третьих
Цитировать
Код
C++ (Qt)
void PolarPlot::resizeEvent(QResizeEvent *re)
{
       QRect *rct=new QRect(0,0,re->size().height()-25,re->size().width()-25); // Те же грабли
       plotScene->setSceneRect(*rct);
       double h1=re->size().height();
       double h2=re->oldSize().height();
       double koef=h1/h2;
       pa->rescale(koef);   //в pa объект класса PolarAxis
 
}
 
всё сказанное выше, но приминительно к rct

А теперь представте, что будет при интенсивном ресайзе и отрисовки..


Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: AlexeyChe от Март 19, 2011, 13:11
я разобрался с QPixmap и QImage, и отрисовка теперь происходит только тогда когда нужно
и как следствие отсутствуют тормоза
масштабирование через FitInView, и вообще картина стала гораздо лучше,
сейчас буду засовывать сцену в отдельный поток, и тогда вообще будет счастье

всем спасибо, если ещё есть какие нибудь рекомендации, буду благодарен

Цитировать
Во-вторых из-за него у вас утечка, поскольку не видно где он уничтожается
я честно сказать пока не разобрался в memory management qt, я почитаю насчёт этого момента, спасибо



Название: Re: оптимизация paint() QGraphicsItem'а
Отправлено: psgenn от Март 27, 2011, 13:58
А как собственно засунуть сцену в отдельный поток?