Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Advanced SVG image usage in QML
Forum Updated to NodeBB v4.3 + New Features

Advanced SVG image usage in QML

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
10 Posts 2 Posters 7.9k 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.
  • O Offline
    O Offline
    osmial
    wrote on last edited by osmial
    #1

    Hi all,

    I know I can display SVG image using QImage item in QML but I'm trying to display SVG image with multiple SVG elements and attach to some of them mouse events.
    I thought using QGraphicSvgItem class will be enough for me and done some example code:

    class SVGImage : public QQuickPaintedItem
    
    void SVGImage::paint(QPainter *painter)
    {
        qDebug() << "SVGImage::paint";
        QGraphicsSvgItem svgItem{QLatin1String("img.svg")};
        svgItem.setElementId("svg_34");
        svgItem.renderer()->render(painter);
    }
    

    and the image is being painted but it looks like whatever I'll set up into svgItem it will be ignored - whole image is being painted.
    I'm rather newbie in Qt's painting stuff, there is a lot of options and dependencies so to make it a little bit shorter I need some tips which classes should I learn to do this correctly.

    All I need is to create C++ implementation for QML object which will paint SVG image and to each SVG item it will attach mouse event (separately so I'll be able to find out which item has been clicked) and manage to handle this event correctly.

    Or maybe there is any public interface which is responsible for parsing SVG file? I've found some internal implementations but due to further problems with fixing issues triggered by changes in internal API I'm rather far from using it.

    Thanks in advance!

    raven-worxR 1 Reply Last reply
    0
    • O osmial

      Hi all,

      I know I can display SVG image using QImage item in QML but I'm trying to display SVG image with multiple SVG elements and attach to some of them mouse events.
      I thought using QGraphicSvgItem class will be enough for me and done some example code:

      class SVGImage : public QQuickPaintedItem
      
      void SVGImage::paint(QPainter *painter)
      {
          qDebug() << "SVGImage::paint";
          QGraphicsSvgItem svgItem{QLatin1String("img.svg")};
          svgItem.setElementId("svg_34");
          svgItem.renderer()->render(painter);
      }
      

      and the image is being painted but it looks like whatever I'll set up into svgItem it will be ignored - whole image is being painted.
      I'm rather newbie in Qt's painting stuff, there is a lot of options and dependencies so to make it a little bit shorter I need some tips which classes should I learn to do this correctly.

      All I need is to create C++ implementation for QML object which will paint SVG image and to each SVG item it will attach mouse event (separately so I'll be able to find out which item has been clicked) and manage to handle this event correctly.

      Or maybe there is any public interface which is responsible for parsing SVG file? I've found some internal implementations but due to further problems with fixing issues triggered by changes in internal API I'm rather far from using it.

      Thanks in advance!

      raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by
      #2

      @osmial
      QGraphics* classes are actually part of the graphics view framework and shouldn't be used outside of a QGraphicsView/QGraphicsScene.

      You should rather use QSvgRenderer instead.
      But when i undertsand you correctly you want interaction for each element inside a SVG file? I am not aware of such a suffisticated class in Qt.
      You could parse the SVG file for the elements using Qt's xml classes and add them separatley.

      Or i guess it's probably even better to write a custom class/widget which parses the SVG and caches it's element properties and process the mouse events accordingly. Handle the repaints on every change and write out a valid svg after the changes.

      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
      If you have a question please use the forum so others can benefit from the solution in the future

      1 Reply Last reply
      1
      • O Offline
        O Offline
        osmial
        wrote on last edited by
        #3

        Thanks @raven-worx for your response.
        I've already used QSvgRenderer but was looking for more functionalities so thought I should use QGraphicsSvgItem cause in Qt's documentation it is listed next to QSvgRenderer class...

        I thought there is some SVG parser provided as a public API (i.e. QImage objects are being parsed and displayed somehow), but it looks like I'll need to use XML parser and code some stuff.

        1 Reply Last reply
        0
        • O Offline
          O Offline
          osmial
          wrote on last edited by
          #4

          I get back to this task and have new challenge.
          I've created:

          class SVGElement : public QQuickItem {
          public:
              SVGElement(QRgb color) : _fillColor(color) {}
              virtual ~SVGElement() {}
              virtual void draw(QPainter* painter) = 0;
              
              QRgb _fillColor;
          };
          
          class SVGRect : public SVGElement {
          public:
          
              SVGRect(double width, double height, double x, double y, QRgb fillColor)
                  : SVGElement{fillColor}, _width{width}, _height{height}, _x{x}, _y{y}
              {
                  setAcceptedMouseButtons(Qt::AllButtons);
              }
          
              void draw(QPainter* painter) override
              {
                  QRectF r{_x, _y, _width, _height};
                  painter->drawRect(r);
              }
          
              void mousePressEvent(QMouseEvent* event) override
              {
                  qDebug() << "SVGRect mouse press event";
                  _fillColor = Qt::black;
                  update();
              }
          
          private:
              double _width;
              double _height;
              double _x;
              double _y;
          };
          

          SVGElement class is a base class for shapes which can be described inside SVG file.

          class SVGImage : public QQuickPaintedItem
          {
              [...]
              std::vector<std::unique_ptr<SVGElement>> m_elements;
          }
          

          The main part is inside SVGImage class where I have vector of SVG elements. I've made some tests and if I add mouse event handler inside SVGImage class then mouse events are being consumed, but if I'm trying to define mouse event handler for SVGRect class (pasted implementation) then mouse events are not being consumed. The main difference is that SVGImage is being instantiated in QML file (SVGImage is QML registered type) and SVGRect isn't. So what is correct approach to attach mouse events to SVGRect class?

          raven-worxR 1 Reply Last reply
          0
          • O osmial

            I get back to this task and have new challenge.
            I've created:

            class SVGElement : public QQuickItem {
            public:
                SVGElement(QRgb color) : _fillColor(color) {}
                virtual ~SVGElement() {}
                virtual void draw(QPainter* painter) = 0;
                
                QRgb _fillColor;
            };
            
            class SVGRect : public SVGElement {
            public:
            
                SVGRect(double width, double height, double x, double y, QRgb fillColor)
                    : SVGElement{fillColor}, _width{width}, _height{height}, _x{x}, _y{y}
                {
                    setAcceptedMouseButtons(Qt::AllButtons);
                }
            
                void draw(QPainter* painter) override
                {
                    QRectF r{_x, _y, _width, _height};
                    painter->drawRect(r);
                }
            
                void mousePressEvent(QMouseEvent* event) override
                {
                    qDebug() << "SVGRect mouse press event";
                    _fillColor = Qt::black;
                    update();
                }
            
            private:
                double _width;
                double _height;
                double _x;
                double _y;
            };
            

            SVGElement class is a base class for shapes which can be described inside SVG file.

            class SVGImage : public QQuickPaintedItem
            {
                [...]
                std::vector<std::unique_ptr<SVGElement>> m_elements;
            }
            

            The main part is inside SVGImage class where I have vector of SVG elements. I've made some tests and if I add mouse event handler inside SVGImage class then mouse events are being consumed, but if I'm trying to define mouse event handler for SVGRect class (pasted implementation) then mouse events are not being consumed. The main difference is that SVGImage is being instantiated in QML file (SVGImage is QML registered type) and SVGRect isn't. So what is correct approach to attach mouse events to SVGRect class?

            raven-worxR Offline
            raven-worxR Offline
            raven-worx
            Moderators
            wrote on last edited by raven-worx
            #5

            @osmial
            did you set the QuickItem::acceptedMouseButtons()?
            If not no mouse events will be delivered to the item.

            --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
            If you have a question please use the forum so others can benefit from the solution in the future

            1 Reply Last reply
            0
            • O Offline
              O Offline
              osmial
              wrote on last edited by
              #6

              @raven-worx,
              Thanks for your reply.

              In SVGRect's constructor I'm calling:

              setAcceptedMouseButtons(Qt::AllButtons);
              

              actually you can check it in attached code snippets I've pasted in previous post.

              When I've enabled mouse events in SVGImage's constructor and then clicked somewhere in app on SVGImage's area mouse events have been received properly (at least mousePressEvent - but it's enough for me), but SVGImage is a container for SVGElements (SVGRect and others derives from SVGElement class). And it looks for me like those elements which I put into vector haven't been registered somewhere in QML's engine and mouse events are not being sent to them.

              raven-worxR 1 Reply Last reply
              0
              • O osmial

                @raven-worx,
                Thanks for your reply.

                In SVGRect's constructor I'm calling:

                setAcceptedMouseButtons(Qt::AllButtons);
                

                actually you can check it in attached code snippets I've pasted in previous post.

                When I've enabled mouse events in SVGImage's constructor and then clicked somewhere in app on SVGImage's area mouse events have been received properly (at least mousePressEvent - but it's enough for me), but SVGImage is a container for SVGElements (SVGRect and others derives from SVGElement class). And it looks for me like those elements which I put into vector haven't been registered somewhere in QML's engine and mouse events are not being sent to them.

                raven-worxR Offline
                raven-worxR Offline
                raven-worx
                Moderators
                wrote on last edited by raven-worx
                #7

                @osmial said in Advanced SVG image usage in QML:

                And it looks for me like those elements which I put into vector haven't been registered somewhere in QML's engine and mouse events are not being sent to them.

                when you insert them into the vector do you also set their parent item to the SVGImage instance?
                If not you just have the item instances in the memory, but nowhere in the QML space, so then they of course cannot receive the events.

                --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                If you have a question please use the forum so others can benefit from the solution in the future

                1 Reply Last reply
                0
                • O Offline
                  O Offline
                  osmial
                  wrote on last edited by
                  #8

                  @raven-worx cause I'm far from my code I've created simple example without any SVG and other unneeded logic.

                  Here's the code:

                  #ifndef IMAGE_H
                  #define IMAGE_H
                  
                  #include <vector>
                  #include <memory>
                  
                  #include <QQuickPaintedItem>
                  #include <QPainter>
                  #include <QMouseEvent>
                  #include <QDebug>
                  
                  using namespace std;
                  
                  class ImgElement : public QQuickItem
                  {
                      Q_OBJECT
                  public:
                      ImgElement(QQuickItem* parent, QColor fillClr)
                          : QQuickItem{parent}, _fillClr{fillClr}
                      {
                          setAcceptedMouseButtons(Qt::AllButtons);
                      }
                      virtual ~ImgElement() {}
                      virtual void draw(QPainter* painter) {}
                  
                      void mousePressEvent(QMouseEvent* event) override
                      {
                          qDebug() << "mouse press event";
                          _fillClr = Qt::black;
                          update();
                      }
                  
                      QColor color() { return _fillClr; }
                  protected:
                      QColor _fillClr;
                  };
                  
                  class RectImgElement : public ImgElement
                  {
                  public:
                      RectImgElement(QQuickItem* parent, int x, int y, int w, int h, QColor fillClr)
                          : ImgElement{parent, fillClr}, _r{x, y, w, h} {}
                  
                      void draw(QPainter* painter) override
                      {
                          painter->drawRect(_r);
                      }
                  
                  private:
                      QRect _r;
                  };
                  
                  class Image : public QQuickPaintedItem
                  {
                      Q_OBJECT
                  public:
                      Image(QQuickPaintedItem* parent = 0) : QQuickPaintedItem{parent}
                      {
                          _elements.emplace_back(new RectImgElement{ this, 50, 50, 50, 50, Qt::red});
                      }
                  
                      void paint(QPainter *painter)
                      {
                          for(auto&& e : _elements)
                          {
                              QBrush b{e->color()};
                              painter->setBrush(b);
                              e->draw(painter);
                          }
                      }
                  
                  signals:
                  
                  public slots:
                  
                  private:
                      vector<unique_ptr<ImgElement>> _elements;
                  };
                  
                  #endif // IMAGE_H
                  

                  In this case I'm setting parent for each ImgElement in it's constructor to Image (container). Also in ImgElement's ctor I'm enabling mouse events but still failing to receive mouse event in ImgElement.

                  1 Reply Last reply
                  0
                  • O Offline
                    O Offline
                    osmial
                    wrote on last edited by
                    #9

                    Actually the same story is when instead of receiving mouse events we will change:
                    class ImgElement : public QQuickItem
                    to
                    class ImgElement : public QQuickPaintedItem
                    and of course will override paint method for them. The paint method is not being called at all.
                    So maybe somebody can provide an easy example of C++ backend class which contains container of elements which actually are being painted?

                    1 Reply Last reply
                    0
                    • O Offline
                      O Offline
                      osmial
                      wrote on last edited by
                      #10

                      I found this thread on this forum:
                      https://forum.qt.io/topic/42986/painting-a-dynamically-created-qquickpainteditem-subclass

                      and it looks like this guy had the same problem as I have.
                      Finally he solved it but I still have problems. I'm not sure if my approach is correct.

                      I've added this code to RectImgElement's ctor:

                              setWidth(w);
                              setHeight(h);
                              setSize({width(), height()});
                      

                      (in few different ways), but my elements ain't still painted...
                      Can anybody more experienced point me what am I doing wrong?

                      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