[SOLVED]Performance of drawing png files. (VERY slow on QWIdgets/QGraphicsScene)



  • Hi all,

    I am trying to write simple 2d game (80' space shooter style).
    Jambi QT - Eclipse - ubuntu

    I reached the wall I can't break from some time.
    In any way I load an image (either QImage or QPixmap), when I try to draw those on paintEvent it's working very slow.
    Printing 10 small (250:250 pix) png-s is taking around 100-200 ms.
    I was trying to do multiple things. Tried QGraphicsScene but the result was the same.
    I seen multiple people having simmilar problems on forums but never seen anwser that helped me nor could I find working example that is doing what I want. (only with drawRect/Circle, not pixmaps)

    Below you can see example code that is redoing the issue.
    I am a bit afraid my habbits from SWING might be the issue but I can't find what is the problem.
    Or maybe I do, but I don't know why it behave like this and how should I do it properly. (in swing drawing 100 png-s the same way takes maybe 10ms)

    @
    import com.trolltech.qt.core.QDir;
    import com.trolltech.qt.core.QTimer;
    import com.trolltech.qt.gui.QApplication;
    import com.trolltech.qt.gui.QPaintEvent;
    import com.trolltech.qt.gui.QPainter;
    import com.trolltech.qt.gui.QPixmap;
    import com.trolltech.qt.gui.QWidget;

    public class PNGDrawing extends QWidget{

    QTimer timer = new QTimer(this);
    QPixmap image;
    int n;
    QPainter painter;

    PNGDrawing(){
    resize(600,600);
    image = new QPixmap("classpath:" + QDir.separator() + "res" + QDir.separator() + "ttt.png");
    timer.start(10);
    timer.timeout.connect(this, "move()");
    timer.timeout.connect(this, "update()");

    }

    public void move(){
    if(n>360)
    n=0;
    n++;
    }

    @Override
    protected void paintEvent(QPaintEvent e){
    painter = new QPainter(this);
    painter.save();
    painter.translate(300, 300);
    painter.rotate(n);
    System.out.print("| before" + System.currentTimeMillis() % 10000);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    System.out.print(" after" + System.currentTimeMillis() % 10000);
    painter.restore();
    }

    public static void main(String[] args) {
    QApplication.initialize(args);

    PNGDrawing wig = new PNGDrawing();
    wig.show();
    QApplication.exec();
    }

    }

    @

    Will appriciate any advice!


  • Moderators

    Hi, and welcome to the Qt Dev Net!

    [quote]Tried QGraphicsScene but the result was the same. [/quote]That's odd, because QGraphicsScene is optimized for your use case.

    Can you post your QGraphicsScene code?

    Also, what version of Jambi and Qt are you using?



  • It's 4.7 (can't even see any download on Jambi web page anymore).

    With QTGraphicsScene to be honest It's a bit messy there as I was doing tons of testing and switching so please don't scream when you see it!
    (previous code I prepared clean so you could see the issue)

    Short description >>

    QWidget having scene and view.

    I called Graphics Items "Rectt", don't ask me why.. Just came through my mind then ;)
    Rectt has timer on "move()" method. (I was watching "CollidingMice" example but there are only drawElipse, not drawPixmaps)

    Move method is runing "scene.update()" as this was the only way I found to make things moving. (I seen timer working as I could resize window and force movement)
    I heard that "setPos" or "advance" should do the job but it wasn't.

    @
    import com.trolltech.qt.core.QDir;
    import com.trolltech.qt.core.QObject;
    import com.trolltech.qt.core.QRectF;
    import com.trolltech.qt.core.QTimer;
    import com.trolltech.qt.gui.QApplication;
    import com.trolltech.qt.gui.QColor;
    import com.trolltech.qt.gui.QGraphicsItem;
    import com.trolltech.qt.gui.QGraphicsScene;
    import com.trolltech.qt.gui.QGraphicsView;
    import com.trolltech.qt.gui.QGridLayout;
    import com.trolltech.qt.gui.QPainter;
    import com.trolltech.qt.gui.QPixmap;
    import com.trolltech.qt.gui.QStyleOptionGraphicsItem;
    import com.trolltech.qt.gui.QWidget;

    public class MyScene extends QWidget {
    // private Signal0 stop = new Signal0();
    // private Signal0 start = new Signal0();
    QGraphicsScene scene;
    QPixmap image = new QPixmap();
    QPixmap map;
    MyScene(QWidget parent){
    super(parent);
    //QGraphicsSystem("raster");
    System.out.print("before" + System.currentTimeMillis()000);
    //image = new QPixmap("classpath:" + QDir.separator() + "res" + QDir.separator() + "ttt.png");
    //image.save("classpath:" + File.separator + "res" + File.separator + "ttt.png");
    image.load("classpath:" + QDir.separator() + "res" + QDir.separator() + "ttt.png");
    //map = new QPixmap("classpath:" + QDir.separator() + "res" + QDir.separator() + "ttt.png");
    System.out.print(" after" + System.currentTimeMillis()000);
    //setFixedSize(500, 500);
    resize(500,500);

    //QTimer timer = new QTimer();
    //timer.start(100);
    //timer.timeout.connect(scene, "update()");

    scene = new QGraphicsScene(this);
    //QBoxLayout lay = new QBoxLayout(QBoxLayout.Direction.LeftToRight);
    //lay.addWidget(scene);

    scene.setSceneRect(-300, -300, 600, 600);
    scene.setItemIndexMethod(QGraphicsScene.ItemIndexMethod.NoIndex);

     QGraphicsView view = new QGraphicsView(scene);
        view.setRenderHint(QPainter.RenderHint.Antialiasing);
        view.setDragMode(QGraphicsView.DragMode.NoDrag);
        view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.SmartViewportUpdate);
        view.setCacheMode(new QGraphicsView.CacheMode(
                QGraphicsView.CacheModeFlag.CacheBackground));
        QGridLayout layout = new QGridLayout();
        layout.addWidget(view, 0, 0);
        setLayout(layout);
        
        Rectt rec = new Rectt(this,10);
        Rectt rec2 = new Rectt(this,300);
        Rectt rec3 = new Rectt(this,200);
        Rectt rec4 = new Rectt(this,250);
        Rectt rec5 = new Rectt(this,280);
        Rectt rec6 = new Rectt(this,290);
        
        scene.addItem(rec);
        scene.addItem(rec2);
        scene.addItem(rec3);
        scene.addItem(rec4);
        scene.addItem(rec5);
        scene.addItem(rec6);
    

    }

    public static void main(String args[]) {
    QApplication.initialize(args);
    MyScene my = new MyScene(null);
    my.show();
    QApplication.exec();
    }

    public class Rectt extends QGraphicsItem{

    int n;

    Rectt(QObject parent, int n){
    this.n=n;
    QTimer time = new QTimer(MyScene.this);
    time.start(100);
    time.timeout.connect(this, "move()");

    // start.connect(time, "start()");
    // stop.connect(time, "stop()");
    }

    @Override
    public QRectF boundingRect() {
    // TODO Auto-generated method stub
    return null;
    }

    public void move(){

    if(n>360)
    n=20;

    n++;
    //setPos(mapToParent(0, 0));
    scene.update();
    //scene.advance();
    //if(n==100)
    //System.out.print("teraz ");
    //
    }
    @Override
    public void paint(QPainter painter, QStyleOptionGraphicsItem arg1,
    QWidget arg2) {
    // TODO Auto-generated method stub
    painter.setBrush(QColor.blue);
    painter.drawEllipse(n, -20, 20, 40);
    painter.save();
    painter.rotate(n);
    System.out.print("| before" + System.currentTimeMillis()000);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    painter.drawPixmap(0, 0, image);
    System.out.print(" after" + System.currentTimeMillis()000);
    painter.restore();
    }
    }
    }
    @

    there is a bit more images here but not that many to make it few FPS.
    Do I make creation of Pixmap correctly?
    Or maybe, am I doing anything correctly? :)


  • Moderators

    I suspect that your code generates the pixmap from scratch every time a paint command is received. That's why it's so slow.

    Instead of implementing Rectt, try using "QGraphicsPixmapItem":http://qt-project.org/doc/qt-5/qgraphicspixmapitem.html and call setPixmap(). You don't have to paint anything yourself. QGraphicsPixmapItem will cache the pixmap, making it much faster.

    Then, move your QGraphicsPixmapItem around by calling setPos() (but don't call mapToParent() because setPos() already uses parent coordinates).

    P.S. In the future, please clean your code before posting! All the commented-out lines made it hard to read.



  • Why on earth I haven't asked that question earlier. :)
    It does work!
    I passed pixmap through constructor and in painting I used "pixmap()".

    Sorry for leaving comments but want'ed to keep it so you could see the ways I was trying to resolve it.

    The pixmap() method made my curious though.
    If QGraphicsPixmapItem has method that allows to draw it, does normal Pixmap has it as well? (went through docs and can't see anything simmilar.)

    In Scene I made it like this:
    @
    painter.drawPixmap(0, 0, pixmap());
    @

    but If I were in widget do you think there is a way to access it other way then:
    @
    painter.drawPixmap(0, 0, image);
    @

    Huge huge thanks! Can't expres how helpfull your reply was! :)


  • Moderators

    You're welcome! :) If your problem is solved, please edit your original post and add "[SOLVED]" to the title.

    [quote author="mr_jaro" date="1410057174"]I passed pixmap through constructor and in painting I used "pixmap()".

    ...

    In Scene I made it like this:
    @
    painter.drawPixmap(0, 0, pixmap());
    @
    [/quote]You don't need QPainter, and you don't need to override paint() or paintEvent().

    Just add your PNG file to the scene like this:
    @
    QGraphicsPixmapItem pic = new QGraphicsPixmapItem();
    pic.setPixmap(new QPixmap("ttt.png"));

    QGraphicsScene scene = new QGraphicsScene();
    scene.addItem(pic);

    QGraphicsView view = new QGraphicsView(scene);
    view.show();

    // (My Java is rusty, so my code might be slightly wrong)
    @

    That's it. Your PNG image will now be displayed.

    Then, move it around like this:
    @
    pic.setPos(100, 100);
    @

    If you want to show an image in a widget, just use a QLabel. (Again, you don't need to override paintEvent())

    [quote]Sorry for leaving comments but want’ed to keep it so you could see the ways I was trying to resolve it.[/quote]That's fair enough :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.