Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Правильное удаление QGraphicsItem
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Система рисования. Печать
urdnot
Имеется следующая функция ,которая вызывается посредствам выбора пункта контекстного меню MyQGraphicsItem:
void MyQGraphicsView::deleteItem(QGraphicsItem *item)
{
....
removeItem(item);
delete item;
....
}

При выполнении программы выскакивает Segfault. Похоже после вызова removeItem QGraphicsView почему-то пытается его перерисовать, но на тот момент я уже освободил память под item.
И вот как тут быть? Как удалить итем со сцены и освободить память.
FireBlack
Цитата(urdnot @ 4.9.2014, 18:36) *
При выполнении программы выскакивает Segfault. Похоже после вызова removeItem QGraphicsView почему-то пытается его перерисовать, но на тот момент я уже освободил память под item.

Показывайте проект полностью, так сложно что то сказать.

Цитата(urdnot @ 4.9.2014, 18:36) *
И вот как тут быть? Как удалить итем со сцены и освободить память.

Со сцены item'ы убираются через QGraphicsScene::removeItem(QGraphicsItem * item) и после удаляются через обычный delete. Скорее всего у Вас где то закралась ошибка и без кода сложно сказать где именно.
urdnot
Item Point:
Раскрывающийся текст
class Point : public QGraphicsItem
{
public:
    Point(const QPointF & p = QPointF(0,0), int pos = 0);

    void setIn(Line *e) { in = e; }
    void setOut(Line *e) { out = e; }
    QVariant itemChange(GraphicsItemChange change, const QVariant &value);
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget);
private:
    Line * in, * out;

    int Position;
};

Point::Point(const QPointF & p, int pos) :
    in(nullptr),out(nullptr)
{
    setFlags(ItemIsMovable | ItemIsSelectable | ItemIgnoresTransformations | ItemSendsGeometryChanges);
    setCursor(Qt::PointingHandCursor);
    setZValue(pos);

    setPos(p);
    Position = pos;
}

QVariant Point::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
    if(change == ItemPositionHasChanged && scene()) {
        if(in)
            in->adjust();
        if(out)
            out->adjust();
    }

    return QGraphicsItem::itemChange(change, value);
}

QRectF WayPoint::boundingRect() const
{
    return QRectF( -5, -5 ,10 ,10);
}

void WayPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
     painter->drawEllipse(QRectF(-5, -5, 10,10));
}


Item Line:
Раскрывающийся текст
class Line : public QGraphicsItem
{
public:
    Line(Point *, Point *);

    void setStart(Point *);
    void setFinish(Point *);

    void adjust();
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget);

private:
    Point *start, *finish;
};

Line::Line(Point *s, Point *f) :
    start(s), finish(f)
{
    s->setOut(this);
    f->setIn(this);

    setZValue(-2);
    
    adjust();
}

void Line::setStart(Point * start_)
{
    start = start_;
    start->setOut(this);

    adjust();
}

void Line::setFinish(Point * finish_)
{
    finish = finish_;
    finish->setIn(this);

    adjust();
}

QRectF Line::boundingRect() const
{
    return QRectF(start->pos(), finish->pos()).normalized().adjusted(-2,-2,2,2);
}

void Line::adjust()
{
    prepareGeometryChange();
}

void Line::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
    qreal q = scene()->views().at(0)->transform().m11();
    QPen pen(QColor(0, 145, 255), 5 * (1. / q));
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(pen);
    painter->drawLine(start->pos(), finish->pos());
}


И наконец MyGraphicsView:

Раскрывающийся текст
class MyGraphicsView : public QGraphicsView
{
    Q_OBJECT

public:
    MyGraphicsView(QWidget * parent = 0);
    ~MyGraphicsView();

public slots:
    void addPoint();
    void removePoint();

private:
    typedef QVector<Point *> p_vector;
    typedef QVector<Line *> line_vector;

    p_vector ps;
    line_vector lines;
};

void MyGraphicsView::addPoint(const QPointF & local)
{
    Point * p = new Point(local, ps.size() + 1);
    if(!ps.empty())
    {
        line * e = new Line(ps.back(), p);
        lines.push_back(e);
        scene()->addItem(e);
    }
    ps.push_back(p);
    scene()->addItem(p);
}

void MyGraphicsView::removePoint(int i)
{
    if(i <= 1)
    {
        auto it_p = ps.begin();
        scene()->removeItem(*it_p);
        delete *it_p;
        ps.erase(it_p);

        if(!lines.empty())
        {
            auto it_l = lines.begin();
            scene()->removeItem(*it_l);
            delete *it_l;
            lines.erase(it_l);
            (*(ps.begin()))->setIn(nullptr);
        }
    } else {
        if(i > 1 && i < wps.size())
        {
            auto it_p = ps.begin() + i - 1;
            auto it_l = lines.begin() + i - 2;

            scene()->removeItem(*it_p);
            scene()->removeItem(*it_l);
            scene()->removeItem(*(it_l + 1));
            delete *it_p;
            delete *it_l;
            delete *(it_l + 1);
            Line * e = new Line(*(it_p - 1),*(it_p + 1));
            (*it_l) = e;
            scene()->addItem(e);
            ps.erase(it_p);
            lines.erase(it_l + 1);
        } else {
            if(i >= ps.size())
            {
                auto it_p = ps.begin() + ps.size() - 1;
                scene()->removeItem(*it_p);
                delete *it_p;
                ps.erase(it_p);

                if(!lines.empty())
                {
                    auto it_l = lines.begin() + lines.size() - 1;
                    scene()->removeItem(*it_l);
                    delete *it_l;
                    lines.erase(it_l);
                    (*(ps.end() - 1))->setOut(nullptr);
                }
            }
        }
    }
}


Проблема в removePoint.
FireBlack
Цитата(urdnot @ 5.9.2014, 11:51) *
Проблема в removePoint.

Нет, проблема не там. После правок удалось скомпилить Ваш код - он полностью рабочий. Попробуйте пересобрать проект, предварительно удалив все сборочные файлы.
Litkevich Yuriy
delete *it_p;
ps.erase(it_p);
Сначала удалил объект, потом обращаешся к нему
FireBlack
Цитата(Litkevich Yuriy @ 5.9.2014, 15:35) *
delete *it_p;
ps.erase(it_p);
Сначала удалил объект, потом обращаешся к нему

Да, объект удаляется. А вот обращение уже идет к iterator'у, чтобы исключить его из vector'а. Ничего противозаконного здесь вроде нет :)
urdnot
Litkevich Yuriy it_p это итератор на указатель. Я освобождаю память на которую ссылается указатель , но итератор остается валидным, поэтому я могу по нему удалить элемент из QVector.
Я решил проблему. Во-первых в самом removePoint проводить изменения в нужном порядке (корень проблемы как оказалось совсем не здесь, но мне кажется эти ошибки тоже могут проявить себя):

Раскрывающийся текст
void QGraphicsView::removePoint(int i)
{
    if(i <= 1)
    {
        auto it_p = ps.begin();
        if(!lines.empty())
        {
            auto it_l = lines.begin();
            (*it_p)->setOut(nullptr);
            (*(it_p + 1))->setIn(nullptr);
            scene()->removeItem(*it_l);
            delete *it_l;
            lines.erase(it_l);
        }
        scene()->removeItem(*it_p);
        delete *it_p;

        ps.erase(it_p);
    } else {
        if(i > 1 && i < ps.size())
        {
            auto it_p = ps.begin() + i - 1;
            auto it_l = lines.begin() + i - 2;

            (*it_p)->setOut(nullptr);
            (*(it_p+1))->setIn(nullptr);
            scene()->removeItem(*(it_l + 1));
            delete *(it_l + 1);

            (*it_p)->setIn(nullptr);
            (*it_l)->setFinish(*(it_p + 1));

            scene()->removeItem(*it_p);
            delete *it_p;

            ps.erase(it_p);
            lines.erase(it_l + 1);
        } else {
            if(i >= ps.size())
            {
                auto it_p = ps.begin() + ps.size() - 1;
                if(!lines.empty())
                {
                    auto it_l = lines.begin() + lines.size() - 1;

                    (*(it_p-1))->setOut(nullptr);
                    (*it_p)->setIn(nullptr);
                    scene()->removeItem(*it_l);
                    delete *it_l;
                    lines.erase(it_l);
                }
                scene()->removeItem(*it_p);
                delete *it_p;
                ps.erase(it_p);
            }
        }
    }
}


И самая главная моя ошибка в Line:

Раскрывающийся текст
class Line : public QGraphicsItem
{
public:
    Line(Point *, Point *);

    void setStart(Point *);
    void setFinish(Point *);

    void adjust();
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget);

private:
    Point *start, *finish;
    QPointF startPoint, finishPoint;   // именно по ним будет идти отрисовка
};

.....

QRectF Line::boundingRect() const
{
    return QRectF(startPoint, finishPoint).normalized().adjusted(-2,-2,2,2);
}

void Line::adjust()  // корень зла
{
    prepareGeometryChange();  //эта функция должна вызываться строго до изменения геометрии ограничивающей области

    startPoint = start->pos();
    finishPoint = finish->pos();
}

void Line::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
    qreal q = scene()->views().at(0)->transform().m11();
    QPen pen(QColor(0, 145, 255), 5 * (1. / q));
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(pen);
    painter->drawLine(startPoint, finishPoint);
}


Я не замечал эту ошибку потому-что она не проявлялась до того момента пока я не начал удалять точки. Как оказалось название темы почти не связано с решением.
Тему можно закрывать.
Litkevich Yuriy
Цитата(urdnot @ 5.9.2014, 18:26) *
Litkevich Yuriy it_p это итератор на указатель.
да, недоглядел
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.