示例#1
0
class ObjectItem(Item):

    """The class to visualize (detected) object.

    It currently supports only rotation around z-axis.

    """

    def __init__(self, scene, object_id, object_type, x,
                 y, yaw, sel_cb=None, selected=False):

        self.object_id = object_id
        self.selected = selected
        self.sel_cb = sel_cb
        # TODO check bbox type and use rectangle (used now) / ellipse, consider
        # other angles
        self.object_type = object_type
        self.inflate = 1.2
        self.hover_ratio = 1.1
        self.def_color = QtCore.Qt.gray

        self.desc = None

        super(ObjectItem, self).__init__(scene, x, y)

        self.desc = DescItem(scene, 0, 0, self)
        self.desc.setFlag(QtGui.QGraphicsItem.ItemIgnoresTransformations)

        self.update_text()

        self.setRotation(yaw)

        if selected:
            self.set_selected()

        self._update_desc_pos()
        self.setZValue(50)

    def set_color(self, color=QtCore.Qt.gray):

        self.def_color = color
        self.update()

    def _update_desc_pos(self):

        if self.desc is not None:

            # make upper left corner of description aligned with left extent of
            # the (possibly rotated) object bounding box (highlight area)
            self.desc.setPos(
                self.mapFromScene(
                    self.x() -
                    self.sceneBoundingRect().width() /
                    2,
                    self.y() +
                    self.sceneBoundingRect().height() /
                    2 +
                    self.m2pix(0.01)))

    def set_pos(self, x, y, parent_coords=False, yaw=None):

        super(ObjectItem, self).set_pos(x, y, parent_coords, yaw)
        self._update_desc_pos()
        self.update_text()

    def update_text(self):

        if self.desc is None:
            return

        desc = ""
        desc += translate("ObjectItem", "ID: ") + self.object_id

        if self.hover:

            desc += "\n" + translate("ObjectItem",
                                     "TYPE: ") + self.object_type.name
            desc += "\n" + self.get_pos_str()

        self.desc.set_content(desc)

    def hover_changed(self):

        self.update_text()
        self.update()

    def boundingRect(self):

        if not self.scene():

            return QtCore.QRectF()

        lx = self.hover_ratio * self.inflate * \
            self.m2pix(self.object_type.bbox.dimensions[0])
        ly = self.hover_ratio * self.inflate * \
            self.m2pix(self.object_type.bbox.dimensions[1])
        p = 1.0
        return QtCore.QRectF(-lx / 2 - p, -ly / 2 - p, lx + 2 * p, ly + 2 * p)

    def paint(self, painter, option, widget):

        if not self.scene():
            return

        painter.setClipRect(option.exposedRect)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        lx = self.inflate * self.m2pix(self.object_type.bbox.dimensions[0])
        ly = self.inflate * self.m2pix(self.object_type.bbox.dimensions[1])

        rr = 10

        if self.selected:

            painter.setBrush(QtCore.Qt.green)
            painter.setPen(QtCore.Qt.green)

            painter.drawRoundedRect(-lx / 2 * self.hover_ratio, -ly / 2 * self.hover_ratio,
                                    lx * self.hover_ratio, ly * self.hover_ratio, rr, rr, QtCore.Qt.RelativeSize)

        elif self.hover:

            painter.setBrush(QtCore.Qt.gray)
            painter.setPen(QtCore.Qt.gray)

            painter.drawRoundedRect(-lx / 2 * self.hover_ratio, -ly / 2 * self.hover_ratio,
                                    lx * self.hover_ratio, ly * self.hover_ratio, rr, rr, QtCore.Qt.RelativeSize)

        painter.setBrush(self.def_color)
        painter.setPen(self.def_color)

        painter.drawRoundedRect(-lx / 2, -ly / 2, lx,
                                ly, rr, rr, QtCore.Qt.RelativeSize)

        fr = 1.0 - (self.hover_ratio - 1.0)  # fill ratio

        painter.setBrush(QtCore.Qt.black)
        painter.setPen(QtCore.Qt.black)
        painter.drawRoundedRect(-lx / 2 * fr, -ly / 2 * fr,
                                lx * fr, ly * fr, rr, rr, QtCore.Qt.RelativeSize)

    def cursor_press(self):

        # TODO call base class method

        if self.sel_cb is not None:
            # callback should handle object selection
            self.sel_cb(self.object_id, self.selected)

        else:
            # no callback - object will handle its selection
            if not self.selected:
                self.set_selected()
            else:
                self.set_selected(False)

    def set_selected(self, selected=True):

        if selected:

            self.selected = True
            # rospy.logdebug('Object ID ' + self.object_id + ' selected')

        else:

            self.selected = False
            # rospy.logdebug('Object ID ' + self.object_id + ' unselected')

        self.update()
示例#2
0
class ObjectItem(Item):

    """The class to visualize (detected) object.

    It currently supports only rotation around z-axis.

    """

    def __init__(self, scene, object_id, object_type, x,
                 y, z, quaternion=(0, 0, 0, 1), sel_cb=None, selected=False, parent=None, horizontal=False):

        self.object_id = object_id
        self.selected = selected
        self.sel_cb = sel_cb
        self.horizontal = horizontal
        # TODO check bbox type and use rectangle (used now) / ellipse, consider
        # other angles
        self.object_type = object_type
        self.inflate = 0.01
        self.def_color = QtCore.Qt.gray
        self.lx = 0
        self.ly = 0

        self.desc = None
        self.quaternion = (0, 0, 0, 1)
        self.on_table = False

        super(ObjectItem, self).__init__(scene, x, y, z, parent=parent)

        self.desc = DescItem(scene, 0, 0, parent=self)
        self.desc.setFlag(QtGui.QGraphicsItem.ItemIgnoresTransformations)

        self.update_text()

        self.set_orientation(quaternion)

        if selected:
            self.set_selected()

        self._update_desc_pos()
        self.setZValue(50)

    def set_color(self, color=QtCore.Qt.gray):

        self.def_color = color
        self.update()

    def _update_desc_pos(self):

        if self.desc is not None:

            # make upper left corner of description aligned with left extent of
            # the (possibly rotated) object bounding box (highlight area)
            self.desc.setPos(
                self.mapFromScene(
                    self.x() -
                    self.sceneBoundingRect().width() /
                    2,
                    self.y() +
                    self.sceneBoundingRect().height() /
                    2 +
                    self.m2pix(0.01)))

    def set_pos(self, x, y, z=None, parent_coords=False):

        super(ObjectItem, self).set_pos(x, y, z, parent_coords)
        self._update_desc_pos()
        self.update_text()

    def get_yaw_axis(self):

        ax = ((1, 0, 0), (0, 1, 0), (0, 0, 1))

        for idx in range(0, len(ax)):

            res = conversions.qv_mult(self.quaternion, ax[idx])

            # TODO euclid dist
            if conversions.is_close(res[0], 0, abs_tol=0.1) and conversions.is_close(res[1], 0, abs_tol=0.1):

                return idx

        return -1

    def set_orientation(self, q):

        self.quaternion = q

        ax = self.get_yaw_axis()

        if ax == ObjectItem.Z:

            self.lx = self.m2pix(self.inflate + self.object_type.bbox.dimensions[0])
            self.ly = self.m2pix(self.inflate + self.object_type.bbox.dimensions[1])

            sres = conversions.qv_mult(self.quaternion, (1, 0, 0))
            angle = math.atan2(sres[1], sres[0])

            self.on_table = self.position[2] < self.object_type.bbox.dimensions[2] + 0.05

        elif ax in [ObjectItem.X, ObjectItem.Y]:

            res = conversions.qv_mult(self.quaternion, (0, 0, 1))

            self.lx = self.m2pix(self.inflate + self.object_type.bbox.dimensions[2])

            # TODO use correct dimension (x/y) - now let's assume that x and y dimensions are same
            self.ly = self.m2pix(self.inflate + self.object_type.bbox.dimensions[1])

            angle = math.atan2(res[1], res[0])

            self.on_table = self.position[2] < self.object_type.bbox.dimensions[0] + 0.05

        else:

            self.set_enabled(False, True)
            return

        self.setRotation(-angle / (math.pi * 2) * 360)

        # TODO if not on table - display somewhere list of detected objects or what?
        self.set_enabled(self.on_table, True)

        self.update()

    def update_text(self):

        if self.desc is None:
            return

        desc = ""
        desc += translate("ObjectItem", "ID: ") + self.object_id

        if self.hover:

            desc += "\n" + translate("ObjectItem",
                                     "TYPE: ") + self.object_type.name
            desc += "\n" + self.get_pos_str()

        self.desc.set_content(desc)

    def hover_changed(self):

        self.update_text()
        self.update()

    def boundingRect(self):

        if not self.scene():

            return QtCore.QRectF()

        self.lx = self.hover_ratio * self.inflate * \
            self.m2pix(self.object_type.bbox.dimensions[0])
        if not self.horizontal:
            self.ly = self.hover_ratio * self.inflate * \
                self.m2pix(self.object_type.bbox.dimensions[1])
        else:
            self.ly = self.hover_ratio * self.inflate * \
                 self.m2pix(self.object_type.bbox.dimensions[2])
        p = 10.0
        return QtCore.QRectF(-self.lx / 2 - p, -self.ly / 2 - p, self.lx + 2 * p, self.ly + 2 * p)

    def paint(self, painter, option, widget):

        if not self.scene():
            return

        if not self.on_table:
            return

        painter.setClipRect(option.exposedRect)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        # if not self.horizontal:
        #     self.ly = self.m2pix(self.inflate + self.object_type.bbox.dimensions[1])
        # else:
        #     self.ly = self.m2pix(self.inflate + self.object_type.bbox.dimensions[2])

        rr = 10

        painter.setBrush(QtCore.Qt.NoBrush)
        pen = QtGui.QPen(self.def_color, 5, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap)

        if self.selected:

            pen.setColor(QtCore.Qt.green)
            pen.setWidth(10)

        elif self.hover:

            pen.setWidth(10)

        painter.setPen(pen)

        painter.drawRoundedRect(-self.lx / 2, -self.ly / 2, self.lx,
                                self.ly, rr, rr, QtCore.Qt.RelativeSize)

    def cursor_press(self):

        # TODO call base class method

        if self.sel_cb is not None:
            # callback should handle object selection
            self.sel_cb(self.object_id, self.selected)

        else:
            # no callback - object will handle its selection
            if not self.selected:
                self.set_selected()
            else:
                self.set_selected(False)

    def set_selected(self, selected=True):

        if selected:

            self.selected = True
            # rospy.logdebug('Object ID ' + self.object_id + ' selected')

        else:

            self.selected = False
            # rospy.logdebug('Object ID ' + self.object_id + ' unselected')

        self.update()
示例#3
0
class SquareItem(Item):

    # Class constructor
    def __init__(
            self,
            scene,
            caption,
            min_x,
            min_y,
            square_width,
            square_height,
            object_type,
            poses,
            grid_points,
            scene_items,
            square_changed=None,
            fixed=False):

        self.scn = scene
        self.caption = caption
        self.object_type = object_type

        self.scene_items = scene_items
        self.square_changed = square_changed
        self.space = 0.05   # is added to bbox of an object

        self.object_side_length_x = self.object_type.bbox.dimensions[0] + self.space
        self.object_side_length_y = self.object_type.bbox.dimensions[1] + self.space
        self.poses = poses

        self.square = QtGui.QPolygon()

        self.previous_width = 0
        self.previous_height = 0

        self.items = []
        self.last_corner = "BR"

        if len(grid_points) == 0:
            self.min = [min_x, min_y]
            self.max = [min_x + square_width, min_y + square_height]
            self.pom_min = self.min     # to save original value because y is changed in update_bound
            self.pom_max = self.max
        else:
            self.min = list(min(grid_points))
            self.max = list(max(grid_points))

        self.orig_x = []
        self.orig_y = []

        self.desc = None
        self.dialog = None
        self.horizontal = False

        super(SquareItem, self).__init__(scene, min_x, min_y)
        self.fixed = fixed

        self.desc = DescItem(scene, self.min[0] - 0.01, self.min[1] - 0.015, self)
        self.desc.setFlag(QtGui.QGraphicsItem.ItemIgnoresTransformations)
        self.update_text()

        self.plus_btn = ButtonItem(scene, self.min[0] - 0.01, self.min[1] - 0.04, "+", self, self.plus_clicked, 1.25,
                                   QtCore.Qt.darkCyan, width=0.04)
        self.minus_btn = ButtonItem(scene, self.min[0] + 0.035, self.min[1] - 0.04, "-", self, self.minus_clicked, 1.25,
                                    QtCore.Qt.darkCyan, width=0.04)
        self.plus_btn.setEnabled(False)
        self.minus_btn.setEnabled(False)

        self.pts = []
        self.pts.append(SquarePointItem(scene, self.min[0], self.min[1], self, "BL", self.fixed))  # bottom-left corner
        self.pts.append(SquarePointItem(scene, self.max[0], self.min[1], self, "BR", self.fixed))  # bottom-right corner
        self.pts.append(SquarePointItem(scene, self.max[0], self.max[1], self, "TR", self.fixed))  # top-right corner
        self.pts.append(SquarePointItem(scene, self.min[0], self.max[1], self, "TL", self.fixed))  # top-left corner

        if len(poses) > 0 and self.fixed:
            # depicts fixed objects
            for pose in poses:
                it = PlaceItem(
                    self.scn,
                    "Object",
                    pose.pose.position.x,
                    pose.pose.position.y,
                    pose.pose.position.z,
                    conversions.q2a(pose.pose.orientation),
                    self.object_type,
                    None,
                    place_pose_changed=None,
                    fixed=True,
                    txt=False,
                    parent=self,
                    horizontal=self.horizontal
                )
                self.items.append(it)
        else:
            # depicts editable objects
            for i, pose in enumerate(poses):
                rot_point = None  # to save xy coordinates for rotation point (for rotating objects in a grid)
                if i == 0:
                    rot = True
                    pom = self.find_corner("TL").get_pos()
                    rot_point = [pom[0] - 0.025, pom[1] + 0.025]
                else:
                    rot = False
                it = PlaceItem(
                    self.scn,
                    "Object",
                    pose.pose.position.x,
                    pose.pose.position.y,
                    pose.pose.position.z,
                    conversions.q2a(pose.pose.orientation),
                    self.object_type,
                    None,
                    place_pose_changed=None,
                    fixed=False,
                    txt=False,
                    rot=rot,
                    rot_point=rot_point,
                    rotation_changed=self.items_rotation_changed,
                    parent=self,
                    horizontal=self.horizontal
                )
                it.update_point()
                self.items.append(it)

            if self.items:
                # rotation of all objects in a grid (list of all objects is handed over to
                # the first object. When the first object is rotated, all objects are
                # rotated)
                self.items[0].set_other_items(self.items[1:])
                self.plus_btn.setEnabled(True)
                self.minus_btn.setEnabled(True)
                self.dialog = DialogItem(self.scene(),
                                         self.pix2m(self.scene().width() / 2),
                                         0.1,
                                         translate(
                    "Place item",
                    "Object place pose options"),
                    [
                    translate(
                        "Place item", "Rotate |"),
                    translate(
                        "Place item", "Rotate --")
                ],
                    self.dialog_cb)

        if self.square_changed is not None:
            # to save grid points and objects into the ProgramItem message
            self.square_changed(self.get_square_points(), self.items)

        self.update_bound()
        self.update()

    '''
        Method updates text below the grid.
    '''

    def update_text(self):

        if self.desc is None:
            return

        desc = self.caption
        self.desc.set_content(desc)

    '''
        Method is called when "+" button is pushed. It increase spacing between objects and walls of the box,
        and between objects.
    '''

    def plus_clicked(self, btn):
        self.space += 0.01
        self.point_changed(True, self.last_corner)

    '''
        Method is called when "-" button is pushed. It decrease spacing between objects and walls of the box,
        and between objects.
    '''

    def minus_clicked(self, btn):
        if self.space > 0.02:
            self.space -= 0.01
            self.point_changed(True, self.last_corner)

    '''
        Method updates the bounding rectangle.
    '''

    def update_bound(self):

        self.min[0] = self.pix2m(self.pts[0].x())
        self.min[1] = self.pix2m(self.pts[0].y())
        self.max[0] = self.pix2m(self.pts[0].x())
        self.max[1] = self.pix2m(self.pts[0].y())

        self.orig_x = []
        self.orig_y = []
        for pt in self.pts:

            p = (self.pix2m(pt.x()), self.pix2m(pt.y()))

            if p[0] < self.min[0]:
                self.min[0] = p[0]
            if p[1] < self.min[1]:
                self.min[1] = p[1]

            if p[0] > self.max[0]:
                self.max[0] = p[0]
            if p[1] > self.max[1]:
                self.max[1] = p[1]

            point = pt.get_pos()
            self.orig_x.append(point[0])
            self.orig_y.append(point[1])

        self.pom_min = [min(self.orig_x), min(self.orig_y)]
        self.pom_max = [max(self.orig_x), max(self.orig_y)]

    '''
        Method returns a required corner.
    '''

    def find_corner(self, corner):
        for pt in self.pts:
            if pt.get_corner() == corner:
                return pt
        return None

    '''
        Metoda pre vykreslovanie gridu a objektov v nom. Je volana vzdy, ked sa pohne s niektorym rohom.
        Rovnomerne rozmiestnuje objekty v gride, kontroluje ci nie su v kolizii.
        Zaistuje ukladanie poloh bodov a gridu do spravy ProgramItem.

        Method depics the gird and objects in it. It is called, when some corner is moved.
        It secures even distribution of objects in the grid, checks collisions.
        Also secures saving grid points and positions of objects into the ProgramItem message.
    '''

    def point_changed(self, finished=False, corner=""):

        if self.fixed:
            return

        self.prepareGeometryChange()

        corner = corner

        # update of bounding rect
        self.update_bound()
        for pt in self.pts:
            if (pt.get_corner() == "BR") and pt.get_changed():
                self.find_corner("TR").setPos(pt.x(), self.find_corner("TR").y())
                self.find_corner("BL").setPos(self.find_corner("BL").x(), pt.y())
                self.desc.setPos(self.m2pix(self.min[0] - 0.01), self.m2pix(self.max[1] + 0.015))
                corner = "BR"
                pt.set_changed(False)
            elif (pt.get_corner() == "BL") and pt.get_changed():
                self.find_corner("TL").setPos(pt.x(), self.find_corner("TL").y())
                self.find_corner("BR").setPos(self.find_corner("BR").x(), pt.y())
                self.desc.setPos(self.m2pix(self.min[0] - 0.01), self.m2pix(self.max[1] + 0.015))
                corner = "BL"
                pt.set_changed(False)
            elif (pt.get_corner() == "TL") and pt.get_changed():
                self.find_corner("BL").setPos(pt.x(), self.find_corner("BL").y())
                self.find_corner("TR").setPos(self.find_corner("TR").x(), pt.y())
                self.desc.setPos(self.m2pix(self.min[0] - 0.01), self.m2pix(self.max[1] + 0.015))
                corner = "TL"
                pt.set_changed(False)
            elif (pt.get_corner() == "TR") and pt.get_changed():
                self.find_corner("BR").setPos(pt.x(), self.find_corner("BR").y())
                self.find_corner("TL").setPos(self.find_corner("TL").x(), pt.y())
                self.desc.setPos(self.m2pix(self.min[0] - 0.01), self.m2pix(self.max[1] + 0.015))
                corner = "TR"
                pt.set_changed(False)
            self.plus_btn.setPos(self.m2pix(self.min[0] - 0.01), self.m2pix(self.max[1] + 0.04))
            self.minus_btn.setPos(self.m2pix(self.min[0] + 0.035), self.m2pix(self.max[1] + 0.04))

        if corner != "":

            self.object_side_length_x = self.object_type.bbox.dimensions[0] + self.space
            if not self.horizontal:
                self.object_side_length_y = self.object_type.bbox.dimensions[1] + self.space
            else:
                self.object_side_length_y = self.object_type.bbox.dimensions[2] + self.space

            width_count = int(
                modf(
                    round(
                        (((self.max[0] - self.min[0]) - self.space) / self.object_side_length_x),
                        5))[1])
            height_count = int(
                modf(
                    round(
                        (((self.max[1] - self.min[1]) - self.space) / self.object_side_length_y),
                        5))[1])
            if self.previous_width != width_count or self.previous_height != height_count:
                ps = PoseStamped()
                if corner == "BR" or corner == "TR":
                    ps.pose.position.x = self.pom_min[0] + self.space / 2 + self.object_side_length_x / 2
                else:
                    ps.pose.position.x = self.pom_max[0] - self.space / 2 - self.object_side_length_x / 2

                if corner == "BR" or corner == "BL":
                    ps.pose.position.y = self.pom_max[1] - self.space / 2 - self.object_side_length_y / 2
                else:
                    ps.pose.position.y = self.pom_min[1] + self.space / 2 + self.object_side_length_y / 2
                ps.pose.orientation.w = 1.0

                if self.items:
                    rotation = self.items[0].rotation()
                else:
                    rotation = 0.0

                for it in self.items:
                    self.scn.removeItem(it)
                del self.items[:]

                for i in range(0, height_count):
                    for j in range(0, width_count):
                        rot_point = None    # to save xy coordinates for rotation point (for rotating objects in a grid)
                        if corner == "BR" and i == 0 and j == 0:
                            rot = True
                            pom = self.find_corner("TL").get_pos()
                            rot_point = [pom[0] - 0.025, pom[1] + 0.025]
                        elif corner == "BL" and i == 0 and j == 0:
                            rot = True
                            pom = self.find_corner("TR").get_pos()
                            rot_point = [pom[0] + 0.025, pom[1] + 0.025]
                        elif corner == "TR" and i == 0 and j == 0:
                            rot = True
                            pom = self.find_corner("BL").get_pos()
                            rot_point = [pom[0] - 0.025, pom[1] - 0.025]
                        elif corner == "TL" and i == 0 and j == 0:
                            rot = True
                            pom = self.find_corner("BR").get_pos()
                            rot_point = [pom[0] + 0.025, pom[1] - 0.025]
                        else:
                            rot = False
                        it = PlaceItem(
                            self.scn,
                            "Object",
                            ps.pose.position.x,
                            ps.pose.position.y,
                            ps.pose.position.z,
                            conversions.q2a(ps.pose.orientation),
                            self.object_type,
                            None,
                            place_pose_changed=None,
                            fixed=False,
                            txt=False,
                            rot=rot,
                            rot_point=rot_point,
                            rotation_changed=self.items_rotation_changed,
                            parent=self,
                            horizontal=self.horizontal
                        )
                        it.setRotation(rotation)
                        it.update_point()
                        self.items.append(it)

                        if corner == "BR" or corner == "TR":
                            ps.pose.position.x += self.object_side_length_x  # BR TR
                        else:
                            ps.pose.position.x -= self.object_side_length_x  # TL BL
                    if corner == "BR" or corner == "TR":
                        ps.pose.position.x = self.pom_min[0] + self.space / 2 + self.object_side_length_x / 2  # BR a TR
                    else:
                        ps.pose.position.x = self.pom_max[0] - self.space / 2 - self.object_side_length_x / 2  # TL BL

                    if corner == "BR" or corner == "BL":
                        ps.pose.position.y -= self.object_side_length_y + self.space / 2   # BR BL
                    else:
                        ps.pose.position.y += self.object_side_length_y + self.space / 2  # TL TR
                self.previous_width = width_count
                self.previous_height = height_count

            self.last_corner = corner

            # rotation of all objects in a grid (list of all objects is handed over to
            # the first object. When the first object is rotated, all objects are
            # rotated)
            if self.items:
                self.items[0].set_other_items(self.items[1:])

        if finished and self.square_changed is not None:

            # even distribution of objects in the grid
            if self.items:
                new_object_length_x = ((self.pom_max[0] - self.pom_min[0]) - self.space) / self.previous_width
                new_object_length_y = ((self.pom_max[1] - self.pom_min[1]) - self.space) / self.previous_height

                for i, it in enumerate(self.items):
                    if self.last_corner == "BR" or self.last_corner == "TR":
                        new_x = self.pom_min[0] + self.space / 2 + new_object_length_x / \
                            2 + new_object_length_x * (i % self.previous_width)
                    else:
                        new_x = self.pom_max[0] - self.space / 2 - new_object_length_x / \
                            2 - new_object_length_x * (i % self.previous_width)

                    if self.last_corner == "BR" or self.last_corner == "BL":
                        new_y = self.pom_max[1] - self.space / 2 - new_object_length_y / 2 - new_object_length_y * (
                            i / self.previous_width)
                    else:
                        new_y = self.pom_min[1] + self.space / 2 + new_object_length_y / 2 + new_object_length_y * (
                            i / self.previous_width)
                    it.set_pos(new_x, new_y)
                    it.update_point()

                for it in self.items:   # to check if there are still some collisions
                    it.item_moved()

            self.plus_btn.setEnabled(True)
            self.minus_btn.setEnabled(True)

            if self.last_corner == "BR" or self.last_corner == "BL":  # TODO: skontrolovat ci nema byt TR a TL!!
                self.items.reverse()    # we want robot always to place object from furthest line

            in_collision = False
            for it in self.items:   # to check collisions
                if it.in_collision:
                    in_collision = True
                    break
            if in_collision:
                # to save only grid points into the ProgramItem message
                self.square_changed(self.get_square_points(), [])
            else:
                # to save grid points and objects into the ProgramItem message
                self.square_changed(self.get_square_points(), self.items)

        self.update()

    def dialog_cb(self, idx):
        if idx == 0:
            self.horizontal = False
            self.point_changed(True, "BR")
        else:
            self.horizontal = True
            self.point_changed(True, "BR")

    '''
        Method returns grid points.
    '''

    def get_square_points(self):

        pts = []

        for pt in self.pts:

            pts.append(pt.get_pos())

        return pts

    '''
        Method which is called after releasing the grid.
        It saves new positions of grid points and objects into the ProgramItem message.
        It checks collisions between grid objects and scene objects.
    '''

    def cursor_release(self):

        if self.fixed:
            return
        for it in self.items:
            it.item_moved()

        if self.square_changed is not None:
            in_collision = False
            for it in self.items:  # to check collisions
                if it.in_collision:
                    in_collision = True
                    break
            if in_collision:
                # to save only grid points into the ProgramItem message
                self.square_changed(self.get_square_points(), [])
            else:
                # to save grid points and objects into the ProgramItem message
                self.square_changed(self.get_square_points(), self.items)

    def cursor_press(self):

        pass

    '''
        Method saves new rotations after rotating objects.
    '''

    def items_rotation_changed(self, items):

        # to save grid points and objects into the ProgramItem message
        self.square_changed(self.get_square_points(), items)

    '''
        Method returns the shape of this grid as a QPainterPath in local coordinates.
    '''

    def shape(self):

        path = QtGui.QPainterPath()
        path.addPolygon(QtGui.QPolygonF(self.square))
        return path

    '''
        Method defines the outer bounds of the item as a rectangle.
    '''

    def boundingRect(self):

        return QtCore.QRectF(
            self.m2pix(
                self.min[0]) -
            2.5,
            self.m2pix(
                self.min[1]) -
            2.5,
            self.m2pix(
                self.max[0] -
                self.min[0]) +
            5,
            self.m2pix(
                self.max[1] -
                self.min[1]) +
            5)

    '''
        Method paints the grid.
    '''

    def paint(self, painter, option, widget):

        painter.setClipRect(option.exposedRect)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        pen = QtGui.QPen()
        pen.setStyle(QtCore.Qt.DotLine)
        pen.setWidth(5)
        pen.setBrush(QtCore.Qt.white)
        pen.setCapStyle(QtCore.Qt.RoundCap)
        pen.setJoinStyle(QtCore.Qt.RoundJoin)

        painter.setPen(pen)

        self.square = QtGui.QPolygon()

        for i in range(0, len(self.pts)):

            self.square.append(self.pts[i].pos().toPoint())

        painter.drawPolygon(self.square)
示例#4
0
class PolygonItem(Item):
    def __init__(self,
                 scene,
                 caption,
                 obj_coords=[],
                 poly_points=[],
                 polygon_changed=None,
                 fixed=False):

        self.caption = caption
        self.polygon_changed = polygon_changed

        self.poly = QtGui.QPolygon()
        self.desc = None

        super(PolygonItem, self).__init__(scene, 0.5,
                                          0.5)  # TODO what should be here?
        self.fixed = fixed
        self.convex = True

        if not self.fixed:
            self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
            self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)

        self.pts = []

        if len(obj_coords) > 0:

            min = [obj_coords[0][0], obj_coords[0][1]]
            max = [obj_coords[0][0], obj_coords[0][1]]

            for pt in obj_coords:

                pt = [pt[0], pt[1]]

                if pt[0] < min[0]:
                    min[0] = pt[0]
                if pt[1] < min[1]:
                    min[1] = pt[1]
                if pt[0] > max[0]:
                    max[0] = pt[0]
                if pt[1] > max[1]:
                    max[1] = pt[1]

            pad = 0.02

            min[0] -= pad
            min[1] -= pad
            max[0] += pad
            max[1] += pad

            self.pts.append(
                PointItem(scene, min[0], min[1], self, self.point_changed))
            self.pts.append(
                PointItem(scene, max[0], min[1], self, self.point_changed))
            self.pts.append(
                PointItem(scene, max[0], max[1], self, self.point_changed))
            self.pts.append(
                PointItem(scene, min[0], max[1], self, self.point_changed))

            if self.polygon_changed is not None:
                self.polygon_changed(self.get_poly_points())

        elif len(poly_points) > 0:

            for pt in poly_points:

                self.pts.append(
                    PointItem(scene, pt[0], pt[1], self, self.point_changed,
                              fixed))

        else:

            pass  # TODO chyba

        for pt in self.pts:
            self.poly.append(pt.pos().toPoint())

        self.desc = DescItem(scene, 0, 0, self)
        self.desc.setFlag(QtGui.QGraphicsItem.ItemIgnoresTransformations)
        self.__update_desc_pos()
        self.__update_text()

        self.update()

    def __update_desc_pos(self):

        if self.desc is not None:

            p = self.poly.boundingRect().bottomLeft() + QtCore.QPointF(
                0, self.m2pix(0.02))
            self.desc.setPos(p)

    def __update_text(self):

        if self.desc is None:
            return

        desc = self.caption
        # TODO number of objects in polygon?
        self.desc.set_content(desc)

    def point_changed(self, pt, finished):

        self.poly.setPoint(self.pts.index(pt), pt.pos().toPoint())

        ps = self.poly.size()

        # TODO what to do with shuffled points? should be fixed...
        for i in range(2, ps + 2):

            line1 = QtCore.QLineF(self.poly.point(i - 2),
                                  self.poly.point((i - 1) % ps))
            line2 = QtCore.QLineF(self.poly.point((i - 1) % ps),
                                  self.poly.point(i % ps))

            a1 = line1.angle()
            a2 = line2.angle()

            d = a2 - a1

            if d < 0:
                d = 360 + d

            if d < 315 and d > 180:

                self.convex = False
                break
        else:
            self.convex = True

        self.__update_desc_pos()
        self.update()

        if finished and self.convex and self.polygon_changed is not None:
            self.polygon_changed(self.get_poly_points())

    def mouseReleaseEvent(self, event):

        self.cursor_release()
        super(Item, self).mouseReleaseEvent(event)

    def cursor_release(self):

        # TODO call base class method

        if self.convex and self.polygon_changed is not None:
            self.polygon_changed(self.get_poly_points())

    def get_poly_points(self):

        pts = []

        try:
            for pt in self.pts:

                pts.append(pt.get_pos())
        except AttributeError:
            return None

        return pts

    def shape(self):

        path = QtGui.QPainterPath()
        path.addPolygon(QtGui.QPolygonF(self.poly))
        return path

    def boundingRect(self):

        return QtCore.QRectF(self.poly.boundingRect())

    def paint(self, painter, option, widget):
        # TODO detekovat ze je polygon "divny" (prekrouceny) a zcervenat
        # TODO vypsat kolik obsahuje objektu

        if not self.scene():
            return

        painter.setClipRect(option.exposedRect)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        # painter.setPen(QtCore.Qt.white)
        # painter.drawRect(self.boundingRect())

        pen = QtGui.QPen()
        pen.setStyle(QtCore.Qt.DotLine)
        pen.setWidth(5)

        if self.convex:
            pen.setBrush(QtCore.Qt.white)
        else:
            pen.setBrush(QtCore.Qt.red)

        pen.setCapStyle(QtCore.Qt.RoundCap)
        pen.setJoinStyle(QtCore.Qt.RoundJoin)
        painter.setPen(pen)

        painter.drawPolygon(self.poly)