C++/Qt: изменить (размер) QImage, который рисуется

Прошу прощения, если даю больше деталей, чем необходимо. У меня есть класс Canvas, который выглядит так:

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);
    [...]
};

Функция Canvas::paintEvent(QPaintEvent *) реализована так:

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

И так класс CanvasDelegate выглядит так:

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);
};

Конструктор CanvasDelegate выглядит следующим образом:

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

Я не уверен, что это лучший дизайн, но это не мой вопрос (хотя любые комментарии приветствуются). Моя проблема в том, что происходит, когда размер окна (Canvas) изменяется. Вот как выглядит мой код:

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;
    [...]
}

Проблема в том, что когда я запускаю программу, она вылетает. По-видимому, возникает ошибка сегментации, когда painter->eraseRect(0, 0, sizeX, sizeY); вызывается в void CanvasDelegate::rescale(int sizeX, int sizeY). Я не понимаю, почему, я не вижу, в чем проблема.

В предыдущей версии я написал следующее (что сейчас кажется мне более сложным, чем нужно):

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;
    [...]
}

Но это не работает: я получаю сообщение об ошибке Qt QPaintDevice: не удается уничтожить рисующее устройство, которое рисуется. Если я уберу delete oldImage; и delete oldPainter;, все будет работать нормально, но это отвратительная утечка памяти, не так ли.

Кто-нибудь понимает, почему то, что я написал, не работает, и что мне нужно делать?

Спасибо вам большое за ваше внимание.


person Seub    schedule 04.10.2014    source источник


Ответы (1)


Я не совсем уверен, почему painter->eraseRect(0, 0, sizeX, sizeY); segfaults, но может быть так, что когда устройство рисования QPainter является изображением, его размер не должен меняться, и поэтому *image = QImage(sizeX, sizeY, QImage::Format_RGB32); нарушает это предположение.

Поэтому я бы попробовал перед изменением размера изображения удалить QPainter, затем изменить размер изображения, а затем выделить новый QPainter. В коде:

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;
    [...]
}
person Boris Dalstein    schedule 04.10.2014
comment
@Seub Смотрите мое обновление, я редко рисую прямо на изображениях, поэтому в данном случае мой ответ был неправильным. :) - person Boris Dalstein; 05.10.2014
comment
хорошо, я читаю ваш отредактированный ответ. Но разве то, что вы предлагаете (более или менее), не то, что я написал в своей предыдущей версии? - person Seub; 05.10.2014
comment
@Seub см. редактирование. Не совсем так, в вашем случае вы вызываете image = newImage;, в то время как oldPainter все еще рисует (потенциально) на image, т.е. ошибка QPaintDevice: Невозможно уничтожить рисуемое устройство, которое рисуется. Таким образом, delete oldPainter; должно стоять перед image = newImage;. - person Boris Dalstein; 05.10.2014
comment
@seub Или, на самом деле, простая замена delete oldImage; и delete oldPainter; должна работать. - person Boris Dalstein; 05.10.2014
comment
Отлично, ваше решение работает! Большое спасибо! Большое спасибо. - person Seub; 05.10.2014