[PyQt] selecting a bezier curve

Tom Brown nextstate at gmail.com
Sun Apr 14 02:57:11 BST 2013


Sure! My solution is below.

<code>
from math import sqrt
from sys import argv

from PyQt4.Qt import QApplication
from PyQt4.QtCore import QPoint, QPointF, Qt
from PyQt4.QtGui import (
    QColor,
    QGraphicsItem,
    QGraphicsView,
    QGraphicsScene,
    QPainterPath,
)


class View(QGraphicsView):
    def __init__(self, parent=None):
        super(View, self).__init__(parent)
        self.epsilon = 11.0
        self.graphics_scene = QGraphicsScene(self)
        self.setScene(self.graphics_scene)
        self.add_curve()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.select_item_at(event.x(), event.y())

    def select_item_at(self, x, y):
        point = self.mapToScene(QPoint(x, y))
        self.unselect_items()
        for item in self.items():
            if item.contains_point(point.x(), point.y(), self.epsilon):
                item.set_selected(True)
                item.update()

    def unselect_items(self):
        for item in self.items():
            item.set_selected(False)
            item.update()

    def add_curve(self):
        color = QColor(255, 0, 0)
        x0 = 600.0
        y0 = 400.0
        x1 = 800.0
        y1 = 500.0
        x2 = 1000.0
        y2 = 500.0
        x3 = 1200.0
        y3 = 400.0
        control_points = (QPointF(x0, y0), QPointF(x1, y1),
            QPointF(x2, y2), QPointF(x3, y3))
        curve = Curve(color, control_points)
        self.graphics_scene.addItem(curve)


class Curve(QGraphicsItem):
    def __init__(self, color, control_points, parent=None, scene=None):
        super(Curve, self).__init__(parent, scene)
        self.selected = False
        self.color = color
        self.path = QPainterPath()
        self.path.moveTo(control_points[0])
        self.path.cubicTo(*control_points[1:])

    def set_selected(self, selected):
        self.selected = selected

    def contains_point(self, x, y, epsilon):
        p = (x, y)
        min_distance = float(0x7fffffff)
        t = 0.0
        while t < 1.0:
            point = self.path.pointAtPercent(t)
            spline_point = (point.x(), point.y())
            print p, spline_point
            distance = self.distance(p, spline_point)
            if distance < min_distance:
                min_distance = distance
            t += 0.1
        print min_distance, epsilon
        return (min_distance <= epsilon)

    def boundingRect(self):
        return self.path.boundingRect()

    def paint(self, painter, option, widget):
        painter.setPen(self.color)
        painter.setBrush(self.color)
        painter.strokePath(self.path, painter.pen())

    def distance(self, p0, p1):
        a = p1[0] - p0[0]
        b = p1[1] - p0[1]
        return sqrt(a * a + b * b)


if __name__ == '__main__':
    app = QApplication(argv)
    view = View()
    view.setGeometry(100, 100, 1600, 900)
    view.setWindowTitle('MainWindow')
    view.show()
    app.exec_()

</code>


On Sat, Apr 13, 2013 at 6:52 PM, Christophe BAL <projetmbc at gmail.com> wrote:

> Hello,
> can you give your solution ?
>
> Christophe.
>
>
> 2013/4/14 Tom Brown <nextstate at gmail.com>
>
>> Ah! I found the QGraphicsView.mapToScene() method. This solved my problem.
>>
>> Thanks!
>> Tom
>>
>>
>> On Sat, Apr 13, 2013 at 6:06 PM, Tom Brown <nextstate at gmail.com> wrote:
>>
>>> I've found that this problem isn't specific to a bezier curve. If I
>>> change the curve to a straight line, I observe the same problem. So, I
>>> would think that it has something to do with the way I'm setting up the
>>> view.
>>>
>>> Any ideas would be appreciated.
>>>
>>> Thanks!
>>> Tom
>>>
>>>
>>>
>>> On Sat, Apr 13, 2013 at 5:15 PM, Tom Brown <nextstate at gmail.com> wrote:
>>>
>>>> I've created a simple application (see below) that draws a bezier
>>>> curve. I want to give the user the ability to select the curve so they can
>>>> move it around. However, I'm having trouble selecting the curve in an
>>>> intuitive fashion. When I click on the curve, the point I click on is
>>>> actually far away from the curve.
>>>>
>>>> For example, when I click on the left end of the curve, the
>>>> x-coordinate of the point where I clicked is about 100 pixels away from the
>>>> x-coordinate of the point of the curve I clicked on.
>>>>
>>>> The code below demonstrates this problem.
>>>>
>>>> Any ideas why this is happening or what I'm doing wrong?
>>>>
>>>> Thanks!
>>>> Tom
>>>>
>>>> <code>
>>>> from math import sqrt
>>>> from sys import argv
>>>>
>>>> from PyQt4.Qt import QApplication
>>>> from PyQt4.QtCore import QPointF, Qt
>>>> from PyQt4.QtGui import (
>>>>     QColor,
>>>>     QGraphicsItem,
>>>>     QGraphicsView,
>>>>     QGraphicsScene,
>>>>     QPainterPath,
>>>> )
>>>>
>>>>
>>>> class View(QGraphicsView):
>>>>     def __init__(self, parent=None):
>>>>         super(View, self).__init__(parent)
>>>>         self.epsilon = 11.0
>>>>         self.graphics_scene = QGraphicsScene(self)
>>>>         self.setScene(self.graphics_scene)
>>>>         self.add_curve()
>>>>
>>>>     def mousePressEvent(self, event):
>>>>         if event.button() == Qt.LeftButton:
>>>>             self.select_item_at(event.x(), event.y())
>>>>
>>>>     def select_item_at(self, x, y):
>>>>         self.unselect_items()
>>>>         for item in self.items():
>>>>             if item.contains_point(x, y, self.epsilon):
>>>>                 item.set_selected(True)
>>>>                 item.update()
>>>>
>>>>     def unselect_items(self):
>>>>         for item in self.items():
>>>>             item.set_selected(False)
>>>>             item.update()
>>>>
>>>>     def add_curve(self):
>>>>         color = QColor(255, 0, 0)
>>>>         x0 = 600.0
>>>>         y0 = 400.0
>>>>         x1 = 800.0
>>>>         y1 = 500.0
>>>>         x2 = 1000.0
>>>>         y2 = 500.0
>>>>         x3 = 1200.0
>>>>         y3 = 400.0
>>>>         control_points = (QPointF(x0, y0), QPointF(x1, y1),
>>>>             QPointF(x2, y2), QPointF(x3, y3))
>>>>         curve = Curve(color, control_points)
>>>>         self.graphics_scene.addItem(curve)
>>>>
>>>>
>>>> class Curve(QGraphicsItem):
>>>>     def __init__(self, color, control_points, parent=None, scene=None):
>>>>         super(Curve, self).__init__(parent, scene)
>>>>         self.selected = False
>>>>         self.color = color
>>>>         self.path = QPainterPath()
>>>>         self.path.moveTo(control_points[0])
>>>>         self.path.cubicTo(*control_points[1:])
>>>>
>>>>     def set_selected(self, selected):
>>>>         self.selected = selected
>>>>
>>>>     def contains_point(self, x, y, epsilon):
>>>>         p = (x, y)
>>>>         min_distance = float(0x7fffffff)
>>>>         t = 0.0
>>>>         while t < 1.0:
>>>>             point = self.path.pointAtPercent(t)
>>>>             spline_point = (point.x(), point.y())
>>>>             print p, spline_point
>>>>             distance = self.distance(p, spline_point)
>>>>             if distance < min_distance:
>>>>                 min_distance = distance
>>>>             t += 0.1
>>>>         print min_distance, epsilon
>>>>         return (min_distance <= epsilon)
>>>>
>>>>     def boundingRect(self):
>>>>         return self.path.boundingRect()
>>>>
>>>>     def paint(self, painter, option, widget):
>>>>         painter.setPen(self.color)
>>>>         painter.setBrush(self.color)
>>>>         painter.strokePath(self.path, painter.pen())
>>>>
>>>>     def distance(self, p0, p1):
>>>>         a = p1[0] - p0[0]
>>>>         b = p1[1] - p0[1]
>>>>         return sqrt(a * a + b * b)
>>>>
>>>>
>>>> if __name__ == '__main__':
>>>>     app = QApplication(argv)
>>>>     view = View()
>>>>     view.setGeometry(100, 100, 1600, 900)
>>>>     view.setWindowTitle('MainWindow')
>>>>     view.show()
>>>>     app.exec_()
>>>>
>>>> </code>
>>>>
>>>
>>>
>>
>> _______________________________________________
>> PyQt mailing list    PyQt at riverbankcomputing.com
>> http://www.riverbankcomputing.com/mailman/listinfo/pyqt
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20130413/055c35c1/attachment-0001.html>


More information about the PyQt mailing list