Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QDeclarativeItem and update() method
Forum Updated to NodeBB v4.3 + New Features

QDeclarativeItem and update() method

Scheduled Pinned Locked Moved General and Desktop
11 Posts 2 Posters 6.3k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    AppsPat
    wrote on last edited by
    #1

    Hello,

    I created my own QDeclarativeItem for using in qml, but unfortunately when calling the update() method, the overloaded paint() method doesn't get called.

    What have I overseen.

    Header:
    @
    class SVGWIDGETSHARED_EXPORT SvgWidget : public QDeclarativeItem {
    Q_OBJECT
    private:
    QSvgRenderer *svgRenderer;
    QGraphicsScene *graphicsScene;

    public:
    explicit SvgWidget(QDeclarativeItem *parent = 0);
    ~SvgWidget();

    public slots:
    Q_INVOKABLE void startAnimation();
    void scenceChanged(const QList<QRectF> &region);

    protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
    };
    @

    Code:
    @
    SvgWidget::SvgWidget(QDeclarativeItem *parent) :
    QDeclarativeItem(parent)
    {
    svgRenderer = new QSvgRenderer(QString(":/images/state.svg"));
    graphicsScene = new QGraphicsScene(svgRenderer->viewBoxF(),this);
    setFlag(QGraphicsItem::ItemHasNoContents, false);

    // connect signal
    connect(graphicsScene,SIGNAL(changed(QList<QRectF>)),this,SLOT(scenceChanged(QList<QRectF>)));
    

    }

    SvgWidget::~SvgWidget()
    {
    delete graphicsScene;
    delete svgRenderer;
    }

    void SvgWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
    // scene empty?
    if( graphicsScene->items().count() == 0)
    {
    qDebug() << "Building scene";

        // build scene
        for(quint8 count = 1; count <= 3; count++)
        {
            QString elementName = QString("layer%1").arg(count);
            QGraphicsSvgItem *graphicItem = new QGraphicsSvgItem();
            graphicItem->setSharedRenderer(svgRenderer);
            QRectF boundingRect = svgRenderer->boundsOnElement(elementName);
            graphicItem->setElementId(elementName);
            graphicItem->setPos(boundingRect.x(),boundingRect.y());
            graphicsScene->addItem(graphicItem);
        }
    }
    
    // Render scene
    graphicsScene->render(painter);
    qDebug() << "Rendering scene";
    

    }

    void SvgWidget::startAnimation()
    {
    static bool animationStarted = false;

    if(!animationStarted)
    {
        qsrand(QDateTime::currentMSecsSinceEpoch());
        QGraphicsItem *item;
        foreach(item, graphicsScene->items())
        {
            QGraphicsSvgItem *svgItem = dynamic_cast<QGraphicsSvgItem*>(item);
            QPropertyAnimation *propertyAnimation = new QPropertyAnimation(svgItem,"opacity");
            propertyAnimation->setDuration(3000 + (qrand() % 4) * 1000 );
            propertyAnimation->setLoopCount(-1);
            propertyAnimation->setStartValue(1.0);
            propertyAnimation->setEndValue(1.0);
            propertyAnimation->setKeyValueAt(0.5,0.0);
            propertyAnimation->start();
        }
    
        animationStarted = true;
    }
    

    }

    void SvgWidget::scenceChanged(const QList<QRectF> &region)
    {
    static quint64 count = 0;
    qDebug() << "SceneChanged event " << count++;
    update();
    }
    @

    And the type registration:
    @
    Q_DECL_EXPORT int main(int argc, char *argv[])
    {
    QScopedPointer<QApplication> app(createApplication(argc, argv));

    QmlApplicationViewer viewer;
    qmlRegisterType<SvgWidget>("ApsQml",1,0,"SvgWidget");
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile&#40;QLatin1String("qml/SVGTestQml/main.qml"&#41;);
    viewer.showExpanded();
    
    return app->exec&#40;&#41;;
    

    }
    @

    I can see that when starting the animations, the scenceChanged() slot will be called several times a second, but a "repaint" is never done.
    If the window loses its focus a repaint is done immediately.

    Stay Hungry. Stay Foolish.

    1 Reply Last reply
    0
    • J Offline
      J Offline
      jys0224
      wrote on last edited by
      #2

      hi, little bit confused about your code,
      why do you create QGraphicsScene 'per' SvgWidget?..

      afaik, single QGraphicsScene contains 'multiple' QGraphicsItem, (i.e, SvgWidget in this case).
      QGraphicsScene is a sort of manager of QGraphicsItems.
      and usually single QGraphicsScene will suffice for simple qml viewer

      i think if multiple QGraphicsScenes are really needed,
      you'd better review your scene management,

      1 Reply Last reply
      0
      • A Offline
        A Offline
        AppsPat
        wrote on last edited by
        #3

        Just to understand, my SvgWidget contains one QGraphicsScene and multiple QGraphicItems.
        I'm using only one instance of the SvgWidget in my QML-App.

        One SvgWidget with one QGraphicsScene with multiple elements.

        Is there a better base class for using as type for QML? Should I use QObject directly, but I think I need a paint method to overload?

        Stay Hungry. Stay Foolish.

        1 Reply Last reply
        0
        • J Offline
          J Offline
          jys0224
          wrote on last edited by
          #4

          QmlApplicationViewer already has QGraphicsScene instance
          so i don't think you need to create graphics scene by yourself.
          ,, and when you call setMainQmlFile(...),
          your qml object (i.e., instance of SvgWidget) is created and added to
          (QmlApplicationViewer's internal) graphics scene automatically.

          QGraphicsScene instance is accessible using viewer's scene() method,
          and also using QGraphicsItem's scene() method (if item was added to scene())

          I'm not hundred percent sure what you want, but how about,,,

          create all three QGraphicsSvgItems in SvgWidget's constructor.
          and make all them as child of SvgWidget (maybe setParentItem() can be used)
          and make SvgWidget's paint() do nothing.
          and remove qgrpahicsscene instance and related code (because already has one inside viewer)
          ...

          this looks okay,
          because SvgWidget seems nothing more than a container of QGraphicsSvgItem,
          and actual painting happens with QGraphicsSvgItem, QSvgRenderer thing.

          ps. for iterating QGraphicsSvgItem, SvgWidget's childItem() can be used

          1 Reply Last reply
          0
          • A Offline
            A Offline
            AppsPat
            wrote on last edited by
            #5

            Ok, I think I will rewrite my code just using the SvgWidget to create some QDeclarativeItem childs containing my SVG layers.

            I reduced my code to some basic stuff, but it seems my initial problem is still there.

            @
            SvgWidget::SvgWidget(QDeclarativeItem *parent) :
            QDeclarativeItem(parent)
            {
            svgRenderer = new QSvgRenderer(QString(":/images/state.svg"));
            setFlag(QGraphicsItem::ItemHasNoContents, false);
            }

            SvgWidget::~SvgWidget()
            {
            delete svgRenderer;
            }

            void SvgWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
            {
            painter->drawRect(0,0,100,100);
            qDebug() << "Rendering scene";
            }

            void SvgWidget::startAnimation()
            {
            static bool animationStarted = false;

            if(!animationStarted)
            {
                qsrand(QDateTime::currentMSecsSinceEpoch());
                QPropertyAnimation *propertyAnimation = new QPropertyAnimation(this,"opacity");
                propertyAnimation->setDuration(3000 + (qrand() % 4) * 1000 );
                propertyAnimation->setLoopCount(-1);
                propertyAnimation->setStartValue(1.0);
                propertyAnimation->setEndValue(1.0);
                propertyAnimation->setKeyValueAt(0.5,0.0);
                propertyAnimation->start();
            
                animationStarted = true;
            }
            

            }
            @

            When starting the animation the paint() method gets called, but no update in my qml viewer.
            If I change the size of my qmlviewer window I can see the animation.

            How can I force an update of my QDeclarativeItem???

            Stay Hungry. Stay Foolish.

            1 Reply Last reply
            0
            • A Offline
              A Offline
              AppsPat
              wrote on last edited by
              #6

              Ok, I rewrote my code as follows:

              @
              SvgWidget::SvgWidget(QDeclarativeItem *parent) :
              QDeclarativeItem(parent)
              {
              svgRenderer = new QSvgRenderer(QString(":/images/state.svg"));
              //setFlag(QGraphicsItem::ItemHasNoContents, false);

              // build scene
              for(quint8 count = 1; count <= 3; count++)
              {
                  QString elementName = QString("layer%1").arg(count);
                  QGraphicsSvgItem *graphicItem = new QGraphicsSvgItem(this);
                  graphicItem->setSharedRenderer(svgRenderer);
                  QRectF boundingRect = svgRenderer->boundsOnElement(elementName);
                  graphicItem->setElementId(elementName);
                  graphicItem->setPos(boundingRect.x(),boundingRect.y());
              }
              

              }

              SvgWidget::~SvgWidget()
              {
              delete svgRenderer;
              }

              void SvgWidget::startAnimation()
              {
              static bool animationStarted = false;

              if(!animationStarted)
              {
                  qsrand(QDateTime::currentMSecsSinceEpoch());
                  QGraphicsItem *item;
                  foreach(item, childItems())
                  {
                      QGraphicsSvgItem *svgItem = dynamic_cast<QGraphicsSvgItem*>(item);
                      QPropertyAnimation *propertyAnimation = new QPropertyAnimation(svgItem,"opacity");
                      propertyAnimation->setDuration(3000 + (qrand() % 4) * 1000 );
                      propertyAnimation->setLoopCount(-1);
                      propertyAnimation->setStartValue(1.0);
                      propertyAnimation->setEndValue(1.0);
                      propertyAnimation->setKeyValueAt(0.5,0.0);
                      propertyAnimation->start();
                  }
              
                  animationStarted = true;
              }
              

              }
              @

              Hopefully last question :-):
              The QGraphicsSvgItems are now drawn in their original size an on the original coordinates.
              When resizing the qmlviewer, they size is also not changed?

              Is there a way to get the size, bounding rect of the component when getting instanciated from the qmlviewer?
              Or how can I improve my coordinate mapping?

              Stay Hungry. Stay Foolish.

              1 Reply Last reply
              0
              • J Offline
                J Offline
                jys0224
                wrote on last edited by
                #7

                afaik, calling update() is sufficient for repaint,, no more 'forcing' exists.
                and when view is exposed, resized,,,
                paint event is spontaneously delivered to graphics item, so don't have to care about.

                i don't see any big problems with given code,,
                hm, did you give a valid width, height property inside qml file ?...

                i tried belows based on what you wrote, and blinking red rect is visible.

                @
                #include <QTimer>
                #include <QtGui/QApplication>
                #include <QDeclarativeView>
                #include "svgwidget.h"
                #include "mainwindow.h"

                /* contents of a.qml

                import QtQuick 1.0
                import ApsQml 1.0

                SvgWidget {
                width: 200
                height: 200
                }

                */

                int main(int argc, char *argv[])
                {
                QApplication a(argc, argv);

                qmlRegisterType<SvgWidget>("ApsQml",1,0,"SvgWidget");
                
                QDeclarativeView view;
                view.setSource(QUrl::fromLocalFile&#40;"a.qml"&#41;&#41;;
                view.show(&#41;;
                
                QTimer::singleShot(2000, view.rootObject(&#41;, SLOT(startAnimation(&#41;));
                
                return a.exec&#40;&#41;;
                

                }
                @

                and

                @SvgWidget::SvgWidget(QDeclarativeItem *parent) :
                QDeclarativeItem(parent)
                {
                svgRenderer = new QSvgRenderer(QString("/images/state.svg"));
                setFlag(QGraphicsItem::ItemHasNoContents, false);
                }

                SvgWidget::~SvgWidget()
                {
                delete svgRenderer;
                }

                void SvgWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
                {
                painter->setBrush(Qt::red);
                painter->drawRect(0, 0,100, 100);
                qDebug() << "Rendering scene";
                }

                void SvgWidget::startAnimation()
                {
                static bool animationStarted = false;

                if(!animationStarted)
                {
                    qsrand(QDateTime::currentMSecsSinceEpoch());
                    QPropertyAnimation *propertyAnimation = new QPropertyAnimation(this,"opacity");
                    propertyAnimation->setDuration(3000 + (qrand() % 4) * 1000 );
                    propertyAnimation->setLoopCount(-1);
                    propertyAnimation->setStartValue(1.0);
                    propertyAnimation->setEndValue(1.0);
                    propertyAnimation->setKeyValueAt(0.5,0.0);
                    propertyAnimation->start();
                
                    animationStarted = true;
                }
                

                }

                @

                and startAnimation() is defined as Q_SLOTS

                regarding size, rect,,,

                viewer's rootObject() will return qml's root object as it's name describes,
                and you can get properly typed object by casting...
                i think you can get size, rect of component with that object's getter

                and also can check size of component in qml side,, like

                @
                SvgWidget {
                ...
                onWidthChanged: console.log("width =" + width);
                onHeightChanged: console.log("height =" + height);
                }
                @

                same pattern can be used for x, y property..

                hope this helps...

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  AppsPat
                  wrote on last edited by
                  #8

                  But are there any efficent methods for translating the SVG model coordinates to the qml model coordinates.

                  E.g. my SVG file consist of 3 elements and has a resolution of 640 x 480 pixels.
                  My qml component has a default size of 320 x 320 pixels.

                  When creating the child items with the following code snippet:
                  @
                  // build scene
                  for(quint8 count = 1; count <= 3; count++)
                  {
                  QString elementName = QString("layer%1").arg(count);
                  QGraphicsSvgItem *graphicItem = new QGraphicsSvgItem(this);
                  graphicItem->setSharedRenderer(svgRenderer);
                  QRectF boundingRect = svgRenderer->boundsOnElement(elementName);
                  graphicItem->setElementId(elementName);
                  graphicItem->setPos(boundingRect.x(),boundingRect.y());
                  }
                  @

                  The boundingRect has all the positions/coordinates from the original SVG file.
                  I need somehow a mechanism to scale the child items so that they will fit into my component.

                  I tried mapToParent() but with no luck.

                  With my first approach using the QGraphicScene this was all done automatically when rendering the scene.

                  @
                  // Render scene
                  graphicsScene->render(painter);
                  @

                  But unfortunately with the bad effect, that my animations are not displayed.

                  Stay Hungry. Stay Foolish.

                  1 Reply Last reply
                  0
                  • J Offline
                    J Offline
                    jys0224
                    wrote on last edited by
                    #9

                    then, how about introducing child svg item from QDeclarativeItem
                    to delegete render single element in svg

                    @
                    #include <QPainter>
                    #include "svgwidgetchild.h"

                    SvgWidgetChild::SvgWidgetChild(QSvgRenderer* r, const QString& str, QDeclarativeItem* parent) :
                    QDeclarativeItem(parent)
                    {
                    renderer = r;
                    element = str;
                    setFlag(ItemHasNoContents, false);
                    }

                    void SvgWidgetChild::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
                    {
                    if (!renderer || !renderer->elementExists(element))
                    return;

                    // full boundrect drawing
                    renderer->render(painter, element);
                    

                    }
                    @

                    and SvgWidget now using SvgWidgetChild rather than QGraphicsSvgItem

                    @

                    SvgWidget::SvgWidget(QDeclarativeItem *parent) :
                    QDeclarativeItem(parent)
                    {
                    svgRenderer = new QSvgRenderer(QString("SVG-logo.svg"));
                    child1 = new SvgWidgetChild(svgRenderer, "SVG", this);
                    child2 = new SvgWidgetChild(svgRenderer, "logo", this);

                    setFlag(ItemHasNoContents, false);
                    

                    }

                    SvgWidget::~SvgWidget()
                    {
                    delete svgRenderer;
                    }

                    @

                    and in viewer, resize mode is changed to SizeRootObjectToView,
                    so qml object's width/height is automatically adjusted according to view

                    @
                    QDeclarativeView view;
                    view.setSource(QUrl::fromLocalFile("a.qml"));
                    view.setResizeMode(QDeclarativeView::SizeRootObjectToView);
                    view.show();
                    @

                    ("SVG-logo.svg" can be downloaded from wikipedia site, http://en.wikipedia.org/wiki/File:SVG-logo.svg )

                    when i run above code with appropriate header,
                    animation is not blocked at all.

                    1 Reply Last reply
                    0
                    • A Offline
                      A Offline
                      AppsPat
                      wrote on last edited by
                      #10

                      Will try this tomorrow.

                      Stay Hungry. Stay Foolish.

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        AppsPat
                        wrote on last edited by
                        #11

                        Ok, so far so good, but…
                        the positions of the single elements, in your case SVG and logo are lost, means the logo image will be paint in foreground and the text in the background.
                        But I want to restore the original positions and scale the whole svg childs to fit into my qml window.
                        So I think I need somehow a mechanism to translate the positions to scene positions.

                        Stay Hungry. Stay Foolish.

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved