QGraphicsView - how to organize scene and items



  • Hi,
    I'm developing a joystick for mobile but my questions is not about the code itself but how to organize it.

    This is what I have:
    !http://dl.dropbox.com/u/6416035/OpenPilot/Erros/post-644-0-02457200-1333675026_thumb.png(joystick)!

    And is organized that way:
    !http://dl.dropbox.com/u/6416035/OpenPilot/Qt/model.png(model)!

    That way I noticed that I need to hardcoded a lot of values because I don't have information about the bigger circles (limit for small pads). I'm thinking in change that and these big circles be a scene or a qgraphicsellipseitem inside the joystick class.

    Basically I want you to suggest how do you change this.

    Thanks :)



  • Hello,

    I can recommend creating small elipses (circles) as child items of the bigger ones. In this case you can take advantage of the QGraphicsItem api and use functions as mapToParent, mapRectToParent etc. so you won't need to hardcode any sizes.

    Personally I don't like to integrate core functionality into view items. So, my proposal is to translate your joystick "values" into some "percentage" from -100 to 100 and in each direction, pass values to the graphics item and let them to manage this values properly, based on the current item sizes on the scene. In this approach you have separated view from the core functionality and flexibiliby when i.e. want to change appearance of the view.



  • [quote author="gmaro" date="1334049230"]Hello,

    I can recommend creating small elipses (circles) as child items of the bigger ones. In this case you can take advantage of the QGraphicsItem api and use functions as mapToParent, mapRectToParent etc. so you won't need to hardcode any sizes.

    Personally I don't like to integrate core functionality into view items. So, my proposal is to translate your joystick "values" into some "percentage" from -100 to 100 and in each direction, pass values to the graphics item and let them to manage this values properly, based on the current item sizes on the scene. In this approach you have separated view from the core functionality and flexibiliby when i.e. want to change appearance of the view.[/quote]

    Thanks for you suggestion :)
    Can you tell me how I set the small ellipses child item of the bigger ones? and this, small ellipse(drag-able) and bigger circle(fixed) will be my joystick class, right?

    PS: I could upload the code if someone is interested but it is very bad lol



  • For adding item as child of other one is simple - you just need to pass the parent inside of the QGraphicsItem constructor

    @
    QGraphicsItem ( QGraphicsItem * parent = 0 )
    @

    Well, if I'm feeling your case well, I think it may be good to create something like Joystick class (which will be some class inherited from QGraphicsItem and if necessary from QGraphicsObject), and inside this class you creating small dragable child elipse item.
    When dragging this small elipse item can inform about any position change i.e. via some signal inside dragEvent.



  • Now my class inherited from a QGraphicsEllipseItem and I re-implement (doesn't know if it is the correct the word) the sceneEvent(QEvent *event) to handle the touchevents.

    Because I only have touch events in the small circles shouldn't it be a class? Maybe I need to create two classes, a pad class (small circle), and a joystick class where I design the big ellipse and create the object pad that have as parent the joystick object.

    joystick.h
    @#ifndef JOYSTICK_H
    #define JOYSTICK_H

    #include <QGraphicsItem>
    #include <QFeedbackHapticsEffect>
    #include <QDebug>

    QTM_USE_NAMESPACE

    class Joystick : public QObject, public QGraphicsEllipseItem
    {

    Q_OBJECT
    

    public:
    Joystick(int x, int y, int radiusLimitParam, bool fixX, bool fixY);

    bool sceneEvent(QEvent *event);
    int getX();
    int getY();
    

    private:
    //QFeedbackHapticsEffect rumble;
    int radiusLimit;
    QPointF initialCenter;
    bool limiteAlert;
    int x;
    int y;
    bool fixXaxis;
    bool fixYaxis;

    signals:
    void updatedSticks(int x, int y);

    };

    #endif // JOYSTICK_H@

    joystick.cpp
    @#include "joystick.h"

    #include <QBrush>
    #include <QTouchEvent>
    #include "math.h"

    #define PI 3.1415
    #define BIGRADIUS 150

    using namespace std;

    #include <QDebug>

    Joystick::Joystick(int x, int y, int radiusLimitParam, bool fixX, bool fixY)
    : QGraphicsEllipseItem(0, 0, 100, 100)
    {
    setAcceptTouchEvents(true);
    setBrush(Qt::lightGray);
    radiusLimit = radiusLimitParam*radiusLimitParam;
    initialCenter = QPointF(x,y);
    limiteAlert=false;
    fixXaxis=fixX;
    fixYaxis=fixY;
    qDebug()<<this->boundingRect().height();
    qDebug()<<this->boundingRect().width();
    qDebug()<<"LOOL";

    }

    float mapValues(float x, float in_min, float in_max, float out_min, float out_max)
    {
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }

    bool Joystick::sceneEvent(QEvent *event)
    {
    switch (event->type()) {
    case QEvent::TouchBegin:
    {
    //rumble.setIntensity(0.5);
    //rumble.setDuration(50);
    //rumble.start();
    event->accept();
    break;
    }
    case QEvent::TouchUpdate:
    {
    QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
    if (touchEvent->touchPoints().count() == 1) {
    QTouchEvent::TouchPoint touchPoint1 = touchEvent->touchPoints().first();

            if(touchPoint1.scenePos().x()>0 && touchPoint1.scenePos().y()>0){
    
                float xDist,xDistTemp,yDist,yDistTemp, dist;
    
                xDist=touchPoint1.scenePos().x()-50.0-initialCenter.x();
                xDistTemp=xDist*xDist;
    
                yDist=touchPoint1.scenePos().y()-50.0-initialCenter.y();
                yDistTemp=yDist*yDist;
    
                dist = xDistTemp+yDistTemp;
    
                if (dist < radiusLimit){
                    (deleted to fit in post)
                }else{
                    (deleted to fit in post)
                }
    
            }
        }
    
        touchEvent->accept();
        break;
    }
    case QEvent::TouchEnd:
    {
        limiteAlert=false;
        if(fixXaxis){
            y=mapValues(initialCenter.y()+y,initialCenter.y()-150.0,initialCenter.y()+150.0,-150,150);
            setPos(initialCenter.x(),initialCenter.y()+y);
            emit updatedSticks(0,y);
        }else{
            setPos(initialCenter);
            emit updatedSticks(0,0);
        }
        break;
    }
    default:
        return QGraphicsItem::sceneEvent(event);
    }
    
    return true;
    

    }

    int Joystick::getX(){
    return x;
    }

    int Joystick::getY(){
    return y;
    }@

    main.cpp
    @#include <QApplication>
    #include <QGraphicsView>
    #include <QDebug>
    #include <QTimer>

    #include "joystick.h"
    #include "cccom.h"

    int main(int argc, char **argv)
    {
    QApplication app(argc, argv);
    QGraphicsScene scene;

    scene.setSceneRect(0,0,854,480);
    
    QGraphicsView view(&scene);
    view.viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
    
    scene.setBackgroundBrush(QBrush(Qt::black, Qt::SolidPattern));
    
    view.setFixedSize(854,480);
    view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view.setStyleSheet( "QGraphicsView { border-style: none; }" );
    
    int horMargin = 55;
    int diameterJoystickOut=300;
    int radiusJoystickOut=diameterJoystickOut/2;
    
    QGraphicsEllipseItem *item = new QGraphicsEllipseItem(horMargin,view.height()/2-radiusJoystickOut,diameterJoystickOut,diameterJoystickOut);
    QGraphicsEllipseItem *item2 = new QGraphicsEllipseItem(view.width()-horMargin-diameterJoystickOut,view.height()/2-radiusJoystickOut,diameterJoystickOut,diameterJoystickOut);
    
    item->setBrush(QBrush(Qt::gray, Qt::SolidPattern));
    item2->setBrush(QBrush(Qt::gray, Qt::SolidPattern));
    
    scene.addItem(item);
    scene.addItem(item2);
    
    Joystick *joy1 = new Joystick(horMargin+radiusJoystickOut-50,view.height()/2-50,radiusJoystickOut,true,false);
    Joystick *joy2 = new Joystick(view.width()-horMargin-radiusJoystickOut-50,view.height()/2-50,radiusJoystickOut,false,false);
    
    joy1->setPos(horMargin+radiusJoystickOut-50,view.height()/2-50);
    joy2->setPos(view.width()-horMargin-radiusJoystickOut-50,view.height()/2-50);
    
    scene.addItem(joy1);
    scene.addItem(joy2);
    
    view.showFullScreen();
    
    cccom teste;
    QObject::connect(joy1,SIGNAL(updatedSticks(int,int)),&teste,SLOT(updateSticksTY(int,int)));
    QObject::connect(joy2,SIGNAL(updatedSticks(int,int)),&teste,SLOT(updateSticksPR(int,int)));
    
    return app.exec&#40;&#41;;
    

    }@



  • Hi,

    I'm not veryfing all of the code. If it's working for you it's fine. I can't tell you "this is wrong" but I can't tell you what would I change here.

    For me, from top level view, there should be only 2 items visible: joy1 and joy2 of joystick class. I think it will be much cleaner. Let's look at the Joystick class:
    this is QGraphicsElipseItem/QObject - ok, but why put the "Stick" (small QGraphicsItem outside this class?). I would suggest to compose this inside the Joystick class. In your case you probably don't need to subclass this, It can be just a small elipse with position driven by touch events.

    Maybe some "skeleton" of the code:
    @
    class Joystick : public QObject, public QGraphicsEllipseItem
    {
    //...
    //your stuff
    //...

    private:
    QGraphicsElipseItem *stick;

    signals:
    void updatedSticks(int x, int y);

    };
    @

    @
    Joystick::Joystick(int x, int y, int radiusLimitParam, bool fixX, bool fixY)
    : QGraphicsEllipseItem(0, 0, 100, 100)
    {
    //...
    //your stuff
    //...
    stick = new QGraphicsElipseItem( this );
    //... set some stick position, properties, appearance

    }
    @

    @
    bool Joystick::sceneEvent(QEvent *event)
    {
    //...
    //do stuff
    //...
    //update the stick position
    }
    @



  • sorry to post the code it is not my intention to take your time looking at it I just want show the struct of the class.

    But that way the touch events will happen every time I touch the big circle, isn't it?



  • Ok, I got your idea how it should work now.
    That's no problem. My intention was to handle everything in "big circle" if you want to handle the touches inside the "small circle", you need to do as you said - reimplement also QGraphicsElipseItem class for the "stick" item, and then put the Stick as a member of Joystick. That sounds good :)



  • To get the information from "big circle" inside the "stick" I will need to add QGraphicsItem * parent = 0 as a parameter to "stick" class right? Or because "Stick" is created inside the "big circle" it have any kind of relation?



  • Yes. You just need to add stick this way:

    @
    Joystick::Joystick(int x, int y, int radiusLimitParam, bool fixX, bool fixY)
    : QGraphicsEllipseItem(0, 0, 100, 100)
    {
    stick = new QGraphicsElipseItem( this );
    }
    @

    Thanks to setting Joystick as parent of stick you got whan you need inside the stick and from "outside" you don't need to add the stick explicitly to the scene, so it's clean and nicely encapsulated.



  • I defined the struct as:

    @class Joystick : public QObject, public QGraphicsEllipseItem
    {
    Q_OBJECT
    public:
    Joystick(int posx, int posy, int radiusvalues, bool fixX=false, bool fixY=false);

    private:

    signals:
    void updateSticks(int x, int y);

    public slots:
    void updatedSticks(int t, int y);
    };@

    @Joystick::Joystick(int x, int y, int radiusLimitParam, bool fixX, bool fixY)
    : QGraphicsEllipseItem(0, 0, 100, 100)
    {
    //...
    //do stuff
    //...
    stick = new StickItem(this, fixX, fixY);
    }@

    @class StickItem : public QObject, public QGraphicsEllipseItem
    {
    Q_OBJECT
    public:
    Stick( QGraphicsItem * parent = 0, bool fixX=false, bool fixY=false);

    private:

    signals:
    void updateSticks(int x, int y);
    };@

    @int main(int argc, char **argv)
    {

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    
    left = new Joystick(/*...*/)
    right = new Joystick(/*...*/)
    
    scene.addItem(left);
    scene.addItem(right);
    
    //...
    //do stuff
    //...
    
    return app.exec&#40;&#41;;
    

    }@

    My last doubt (I hope lol) is how I should set the Joystick position and size instead of use the fix values? Instead of QGraphicsEllipseItem(0, 0, 100, 100) can I create it with all zeros, and after set the position and size? something like:

    @Joystick::Joystick(int x, int y, int radius, bool fixX, bool fixY)
    : QGraphicsEllipseItem(0, 0, 0, 0)
    {
    this.setRect(x,y,radius,radius)
    //...
    //do stuff
    //...
    stick = new StickItem(this, fixX, fixY);
    }@

    To change the height and width of the ellipse it is normal to use setRect?


Log in to reply
 

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