2

I apologize if I give more details than necessary. I have a class Canvas that looks like this:

class Canvas : public QWidget
{
    Q_OBJECT
public:
    explicit Canvas(int width = 700, int height = 700, QWidget *parent = 0);
    void setDelegate(CanvasDelegate *delegate);
private:
    CanvasDelegate *delegate;
    void paintEvent(QPaintEvent *event);
    void resizeEvent(QResizeEvent *resizeEvent);
    [...]
};

The Canvas::paintEvent(QPaintEvent *) function is implemented like this:

void Canvas::paintEvent(QPaintEvent *)
{
    delegate->redrawBuffer();
    QPainter canvas_painter(this);
    canvas_painter.drawImage(0, 0, *(delegate->getImage()));
}

And so the class CanvasDelegate looks like this:

class CanvasDelegate
{
    friend class Canvas;

public:
    CanvasDelegate(const Canvas *canvas);
    ~CanvasDelegate();

    const QImage * getImage() const;

    void drawPoint(const Complex &z, const QColor &color = "black", int width = 3);
    [...]
    virtual void redrawBuffer(const H2Isometry &mobius = H2Isometry::identity()) = 0;
    virtual void mousePress(QMouseEvent * mouseEvent) = 0;
    [...]


protected:
    const Canvas *canvas;

    int sizeX, sizeY;
    [...]

    QPen *pen;
    QImage *image;
    QPainter *painter;

    void rescale(int sizeX, int sizeY);
};

The constructor of CanvasDelegate is as follows:

CanvasDelegate::CanvasDelegate(const Canvas *canvas) : canvas(canvas)
{
    pen = new QPen;
    image = new QImage(canvas->width(), canvas->height(), QImage::Format_RGB32);
    painter = new QPainter(image);
    [...]
}

I'm not sure this is the best design ever but this is not my question (any comments are welcome, though). My problem is what happens when the window (Canvas) is resized. Here is what my code looks like:

void Canvas::resizeEvent(QResizeEvent *resizeEvent)
{
    QSize newSize = resizeEvent->size();
    delegate->rescale(newSize.width(), newSize.height());
    //update();
}


void CanvasDelegate::rescale(int sizeX, int sizeY)
{
    *image = QImage(sizeX, sizeY, QImage::Format_RGB32);
    painter->eraseRect(0, 0, sizeX, sizeY);

    this->sizeX = sizeX;
    this->sizeY = sizeY;
    [...]
}

The problem is that when I run the program, it crashes. Apparently there is a segmentation fault when painter->eraseRect(0, 0, sizeX, sizeY); is called in void CanvasDelegate::rescale(int sizeX, int sizeY). I don't understand why, I don't see what the problem is.

In a previous version, I had written the following (which now seems to me more complicated than necessary):

void CanvasDelegate::rescale(int sizeX, int sizeY)
{
    QImage * oldImage = image;
    QImage * newImage = new QImage(sizeX, sizeY, QImage::Format_RGB32);

    QPainter * oldPainter = painter;
    QPainter * newPainter = new QPainter(newImage);
    newPainter->eraseRect(0, 0, sizeX, sizeY);
    newPainter->setPen(*pen);

    image = newImage;
    painter = newPainter;

    delete oldImage;
    delete oldPainter;

    this->sizeX = sizeX;
    this->sizeY = sizeY;
    [...]
}

But that does not work: I get a Qt error QPaintDevice: Cannot destroy paint device that is being painted. If I remove delete oldImage; and delete oldPainter;, everything works fine but that is a disgusting memory leak, isn't it.

Does someone understand why what I have written does not work, and what I need to do?

Thank you very much for your attention.

Seub
  • 2,451
  • 4
  • 25
  • 34

1 Answers1

1

I'm not exactly sure why painter->eraseRect(0, 0, sizeX, sizeY); segfaults, but it may be that when the paintdevice of a QPainter is an image, its size shoudn't change, and therefore *image = QImage(sizeX, sizeY, QImage::Format_RGB32); breaks this assumption.

Therefore, I would try, before resizing the image, to delete the QPainter, then resize the image, then allocate a new QPainter. In code:

void CanvasDelegate::rescale(int sizeX, int sizeY)
{
    delete painter;
    *image = QImage(sizeX, sizeY, QImage::Format_RGB32);
    painter = new QPainter(image);
    painter->eraseRect(0, 0, sizeX, sizeY);
    painter->setPen(*pen);

    this->sizeX = sizeX;
    this->sizeY = sizeY;
    [...]
}
Boris Dalstein
  • 7,015
  • 4
  • 30
  • 59
  • @Seub See my update, I rarely paint directly on images so my answer was actually wrong in this case. :) – Boris Dalstein Oct 04 '14 at 22:13
  • ok I'm reading your edited answer. But isn't what you are suggesting (more or less) what I've written in my "previous version"? – Seub Oct 04 '14 at 22:17
  • @Seub see edit. Not exactly, in your case, you call `image = newImage;`, while `oldPainter` is still "painting" (potentially) on `image`, i.e. the error *QPaintDevice: Cannot destroy paint device that is being painted*. So `delete oldPainter;` must occur before `image = newImage;`. – Boris Dalstein Oct 04 '14 at 22:21
  • @seub Or actually, simply swapping `delete oldImage;` and `delete oldPainter;` should work. – Boris Dalstein Oct 04 '14 at 22:24