Визуальное выделение с точностью до пикселя в QGraphicsScene

У меня есть QGraphicsScene, где я хочу, чтобы пользователь рисовал/перемещал объекты. В настоящее время я могу рисовать все фигуры, которые хочу (а именно [не]закрашенные прямоугольники и эллипсы, линии и кубические кривые Безье), получая QGraphics*Item.

Я также могу выделить эти элементы, но мне нужен какой-то пиксельный выбор. Например, кривую можно выбрать, щелкнув рядом с ней в том месте, где она изогнута, даже если мышь находится не на реальной линии. То же самое касается пустых прямоугольников или эллипсов, щелкнув по отверстию в середине их, чтобы выбрать их.

Это из-за того, как работает contains, и он не соответствует моим потребностям: он в основном проверяет, находится ли точка в ограничивающем прямоугольнике. setBoundingRegionGranularity(1) ничего не решает (упомянул на всякий случай).

Я также пытался напрямую проверить, содержится ли точка в QPainterPath моих элементов, но это дает мне те же результаты.

Как сделать визуальный выбор фигур?

Единственным решением, которое я вижу на данный момент, было бы переопределение моей собственной функции contains для каждой формы, которая у меня есть, но это может быть довольно сложно, и я бы очень хотел, чтобы это было сделано Qt, если это возможно.

Я использую Python 3.3 и PyQt 5 (.1.1 IIRC), но это больше связано с инфраструктурой Qt, чем с языком/привязкой, и ответы на C++ тоже подходят.


person teh internets is made of catz    schedule 14.02.2014    source источник


Ответы (1)


QGraphicsPathItem::shape содержит отображаемый путь, включая его внутреннюю область. Ожидаемое поведение больше похоже на QGraphicsPixmapItem. Если его shapeMode равно QGraphicsPixmapItem::MaskShape (по умолчанию), его форма будет содержать только непрозрачные точки. Вы можете нарисовать свои кривые на пиксельных изображениях и показать эти пиксельные изображения на сцене и наслаждаться поведением по умолчанию. Вы также можете переопределить QGraphicsPathItem::shape и реализовать это поведение. Рабочий пример, C++ (можно легко адаптировать для Python):

class MyGraphicsPathItem : public QGraphicsPathItem {
public:
  MyGraphicsPathItem() {}
  QPainterPath shape() const {
    QRectF rect = path().boundingRect();
    QImage canvas(rect.size().toSize(), QImage::Format_ARGB32);
    canvas.fill(qRgba(0, 0, 0, 0));
    QPainter painter(&canvas);
    painter.setPen(pen());
    painter.drawPath(path().translated(-rect.topLeft()));
    painter.end();
    QPainterPath result;
    result.addRegion(QPixmap::fromImage(canvas).mask());
    return result.translated(rect.topLeft());
  }
};

Использование:

MyGraphicsPathItem* item = new MyGraphicsPathItem();
item->setPath(path);
item->setFlag(QGraphicsItem::ItemIsSelectable);
item->setPen(QPen(Qt::green, 3));
scene->addItem(item);

Обратите внимание, что эта реализация довольно медленная. Используйте с осторожностью.

person Pavel Strakhov    schedule 14.02.2014
comment
Спасибо за Ваш ответ. Однако использование Pixmaps сбрасывает сглаживание фигур, я что-то пропустил? - person teh internets is made of catz; 15.02.2014
comment
Вам нужно включить подсказку QPainter::Antialiasing в рисовании, которое вы используете для создания растрового изображения. Если вы используете масштабирование растрового изображения, вы также должны установить QGraphicsPixmapItem::setTransformationMode(Qt::SmoothTransformation). - person Pavel Strakhov; 15.02.2014