Search code examples
c++qtqpainterpath

How to implement scribble drawing with possibility to erase paths in QT?


What would be the right approach to implement scribble like drawing in QT but also be able to erase some of the drawn paths?

I've seen some examples where drawing is implemented by adding ellipses as points to images (QImage) but as far as I understand, you would not be able to delete any of drawn lines? One thing I've been thinking about is to implement QPainterPaths. On mouse down, start a new path, on mouse move add points to the path at the mouse location and on mouse release finish this path. On the draw event I would go through an array of QPainterPaths and draw each of them. On erase, I would go through every QPainterPath in the array and check if any point collides, if yes, remove whole path from the array, effectively erasing it.

Is that some valid approach or is it something that cannot be achieved with QPaths?

Is there a better, smarter, way to do it?


Solution

  • This seems to be indeed a valid, or at least a working approach, even though my question is a bit broad, eventually someone will find the answer useful:

    /*
     * QList<QGraphicsPathItem *> mList;
     * bool mErase;
     * QPainterPath * mCurrentPath;
     * QPainterPathStroker mStroker;
     */
    
    void ScribbleArea::mousePressEvent ( QGraphicsSceneMouseEvent* event)
    {
        if(!mErase)
        {
            mCurrentPath = new QPainterPath();
            mCurrentPath->moveTo(event->lastScenePos());
            mList.append(addPath(mStroker.createStroke(*mCurrentPath), QPen(Qt::red), QBrush(Qt::red)));
        }
        QGraphicsScene::mousePressEvent(event);
    }
    
    void ScribbleArea::mouseMoveEvent ( QGraphicsSceneMouseEvent* event)
    { 
          if(!mErase)
          {
              mCurrentPath->lineTo(event->lastScenePos());
              mList[mList.count()-1]->setPath(mStroker.createStroke(*mCurrentPath));
          }
          else
          {
              for(int i=0; i < mList.count(); i++)
              {
                  if(mList[i]->isUnderMouse())
                  {
                      removeItem(mList[i]);
                      delete mList[i];
                      mList.removeAt(i);
                  } 
              }
          }    
          QGraphicsScene::mouseMoveEvent(event);
    }
    

    Those are the two key functions, on mousePressEvent I start a new path and move it to the current mouse location, next, I add the path to the QGraphicsScene to get a pointer to QGraphicsPathItem that I will use in the mouseMoveEvent function.

    In the mouseMoveEvent function, I first check if we are currently erasing or drawing. In case of drawing, I add a line from the paths last point to current mouse location and add the path again to the QGraphicsScene, otherwise it wont show those new lines (maybe there is a better way, I'm open for improvements), that's it for drawing. In case we are deleting, I go through a list of all paths and check if any of the paths is currently under the mouse, if yes, remove it.

    This is currently just a draft, more improvements to come.

    Some awesome example of the drawing/scribbling:

    Some awesome example of the drawing