Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: _Vitaliy_ от Февраль 02, 2012, 10:31



Название: умное выделение графика
Отправлено: _Vitaliy_ от Февраль 02, 2012, 10:31
Доброго времени суток.
На сцену нужно вывести порядка двадцати графиков функций, каждый график представляет собой "достаточно ломанную кривую", есть необходимость перемещать графики по сцене, как организовать правильное выделение каждого графика?

Код:
QRectF Grafik::boundingRect() const
{
    return QRectF(0, 0, my_model->rowCount()+10, size_y);
}

Код:
QPainterPath Grafik::shape() const
{
    QPainterPath path;
    path.addRect(1, 1, size_x, size_y);
    return path;
}

сама отрисовка:
Код:
void Grafik::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(widget);

    QColor fillColor = (option->state & QStyle::State_Selected) ? color.dark(150) : color;
    if (option->state & QStyle::State_MouseOver)
        fillColor = fillColor.light(125);

    const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());

    QVarLengthArray<QPointF> pointsss;
    QList<double> x;

    for (int i = 0; i <= size_x-1; i++)
    {
        x.append(my_model->item(i, stolbec)->text().toFloat());
        pointsss.append(QPointF(i,x[i]));
    }

    painter->setPen(QPen(color, 2));
    painter->drawPoints(pointsss.data(), pointsss.size());

}


QPainterPath Grafik::shape() будет выделять прямоугольник, который может накладываться друг на друга и правильного выделения не происходит.
Как это победить? Т.е. необходимо, чтобы при наведении на сам график +- например 3 пикселя происходило выделение.
П.С. Предварительный поиск по форуму в первом приближении результатов не принес, может не то искал.


Название: Re: умное выделение графика
Отправлено: _Vitaliy_ от Февраль 02, 2012, 13:53
на мой взгляд несколько вариантов?
1. правильно задать shape()
2. переопределить mousePressEvent
3. использование QPainterPath::LineTo и задать толщину например если линия у меня рисуется в 2 пикселя, то для PainterPath задать например в 5-7 пикселей.

В каком направлении двигаться? 


Название: Re: умное выделение графика
Отправлено: _OLEGator_ от Февраль 03, 2012, 09:23
4. Переопределить функцию
Код
C++ (Qt)
bool QGraphicsItem::collidesWithPath ( const QPainterPath & path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const


Название: Re: умное выделение графика
Отправлено: GreatSnake от Февраль 03, 2012, 09:50
Добавь на выбранный элемент размытый контур через QGraphicsItem::setGraphicsEffect( new QGraphicsDropShadowEffect( ... )  ).
Ну и подними его выше всех других через QGraphicsItem::setZValue().


Название: Re: умное выделение графика
Отправлено: _Vitaliy_ от Февраль 03, 2012, 11:00
вывод на сцену производится следующим образом:
Код:
QGraphicsItem *item = new Grafik(model, Qt::red, 3, min, max);
        item->setPos(QPointF(0, 200));
        item->setFlag(QGraphicsItem::ItemIsMovable, true);

это все в цикле.

для _OLEGator_
Пока не пойму как использовать
Код:
bool QGraphicsItem::collidesWithPath ( const QPainterPath & path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const

ассистент смотрел, но примера использования не нашел, а так пока образования не хватает, отправте хоть к примеру какому-нибудь, буду разбираться. 

для GreatSnake
В том то и дело что нормально выбрать элемент не могу, очень часто выделяются другие, а не тот, который хочешь.


Название: Re: умное выделение графика
Отправлено: _OLEGator_ от Февраль 03, 2012, 11:10
Это функция для самостоятельной реализации проверки пересечения с произвольным путем.
В случае нажатия мышкой в path будет содержаться всего один элемент (path.elementCount() == 1), можно извлечь координаты из пути (path.elementAt(0)), дальше уже самому реализовать проверку на попадание в нужное отклонение от ломанной.


Название: Re: умное выделение графика
Отправлено: _Vitaliy_ от Февраль 06, 2012, 20:14
После некоторых размышлений практически решил проблему в лоб.
само рисование:
Код:
void Grafik::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    ...
    const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());

    QVarLengthArray<QPointF> pointsss;
    QList<double> x;

    for (int i = 0; i <= size_x-2; i++)
    {
        x.append(my_model->item(i, stolbec)->text().toFloat());
        pointsss.append(QPointF(i,x[i]));
   
        path1.moveTo(i,x[i]);
        path1.lineTo(i+1, my_model->item(i+1, stolbec)->text().toFloat());
    }

    painter->setPen(QPen(color, 2));
    painter->drawPoints(pointsss.data(), pointsss.size());
    painter->setPen(QPen(Qt::blue,15));
    //painter->drawPath(path1);
}

т.е. добавил траекторию path1, соответственно в
Код:
QPainterPath Grafik::shape() const
{
    //QPainterPath path1;
 //   path.addRect(1, 1, size_x, size_y/2);
    return path1;
}
прописал эту траекторию.

Однако выбор графика необходимо осуществлять "очень точно", малейшее отклонение и выбора не происходит, а хотелось бы чтобы по оси игрек можно было бы иметь запас +-10 пикселей. Установка painter->setPen(QPen(Qt::blue,15)) желаемого результата не принесла.

переопределил нажатия мышью
Код:
void Grafik::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::mousePressEvent(event);
    QGraphicsItem::setCursor(Qt::ClosedHandCursor);
    update();
}

Код:
void Grafik::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::setCursor(Qt::ClosedHandCursor);
    QGraphicsItem::mouseMoveEvent(event);

    if (QGraphicsItem::pos().x()!=0) // zapret peremesheniya po X
    {
        QGraphicsItem::setX(0);
    }
    update();   
}


Код:
void Grafik::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
   QGraphicsItem::setCursor(Qt::OpenHandCursor);
   QGraphicsItem::mouseReleaseEvent(event);
   update();
}

но теперь это начинает немного подтормаживать на одном тестовом графике, а их должно быть несколько десятков.
В какую сторону искать оптимизацию, потому что скорее всего изобрел велосипед.
З.Ы. думал может collidesWithPath но не смог пока осилить.


Название: Re: умное выделение графика
Отправлено: _Vitaliy_ от Февраль 06, 2012, 20:19
ответ GreatSnake
Цитировать
Добавь на выбранный элемент размытый контур через QGraphicsItem::setGraphicsEffect( new QGraphicsDropShadowEffect( ... )  ).
Ну и подними его выше всех других через QGraphicsItem::setZValue().
вроде пока не то,
а _OLEGator_
Цитировать
В случае нажатия мышкой в path будет содержаться всего один элемент (path.elementCount() == 1), можно извлечь координаты из пути (path.elementAt(0)), дальше уже самому реализовать проверку на попадание в нужное отклонение от ломанной.
пока не понял как реализовать.


Название: Re: умное выделение графика
Отправлено: _OLEGator_ от Февраль 06, 2012, 21:24
Ну вы и велосипедист, _Vitaliy_ =)
очень просто ее реализовать, переопределяешь
Код
C++ (Qt)
bool QGraphicsItem::collidesWithPath ( const QPainterPath & path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const [virtual]

проверяешь
Код
C++ (Qt)
//это проверка на клик будет
if (path.elementCount() == 1)
{
 QPointF pos = path.elementAt(0);
 //дальше определяешь попадает ли в область (элементарная геометрия)
 if ( ... ) return true;
 return false;
}
 
//возвращаешь пересечение со свои путем
return path.intersects( ... );
Соответственно через эту функцию будет и проверка на смену курсора, тебе просто надо будет установить курсор на элемент стандартным способом
Код
C++ (Qt)
void QGraphicsItem::setCursor ( const QCursor & cursor )

Помоему разжевал уже совсем.
Пробуй.

UPD
С запретом перемещения тоже намудрил. Используй
Код
C++ (Qt)
void QGraphicsItem::setTransform ( const QTransform & matrix, bool combine = false )
и
Код
C++ (Qt)
void QGraphicsItem::prepareGeometryChange () [protected]
в случае изменения геометрии для перерисовки, а если тупо двигаешь - то перерисовка вызовется автоматически!


Название: Re: умное выделение графика
Отправлено: _Vitaliy_ от Февраль 07, 2012, 23:48
Для _OLEGator_