Пример #1
0
 def create_fromDB(self,pos_list=[],obj_type=""):      
     if obj_type =="rois":
         num_rois = len(pos_list)
         for i in range(num_rois):
             points = pos_list[i]
             rect=QGraphicsRectItem()
             rect.setPen(QPen(Qt.green))
             rect.setRect(points[0],points[1],points[2],points[3])
             self.rectgroup.addToGroup(rect)
         self.scene.addItem(self.rectgroup)
     elif obj_type =="vector1":
         num_vec = len(pos_list)
         for i in range(num_vec):
             points = pos_list[i]
             vec=QGraphicsLineItem()
             vec.setPen(QPen(Qt.green))
             vec.setLine(points[0],points[1],points[2],points[3])
             self.linegroup.addToGroup(vec)
         self.scene.addItem(self.linegroup)
     elif obj_type =="vector2":
         num_vec = len(pos_list)
         for i in range(num_vec):
             points = pos_list[i]
             vec=QGraphicsLineItem()
             vec.setPen(QPen(Qt.green))
             vec.setLine(points[0],points[1],points[2],points[3])
             self.linegroup2.addToGroup(vec)
         self.scene.addItem(self.linegroup2)
Пример #2
0
    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton:

            downPos = event.buttonDownPos(Qt.LeftButton)
            if not self.__tmpLine and self.__dragStartItem and \
                    (downPos - event.pos()).manhattanLength() > \
                        QApplication.instance().startDragDistance():
                # Start a line drag
                line = QGraphicsLineItem(self)
                start = self.__dragStartItem.boundingRect().center()
                start = self.mapFromItem(self.__dragStartItem, start)
                line.setLine(start.x(), start.y(),
                             event.pos().x(),
                             event.pos().y())

                pen = QPen(Qt.black, 4)
                pen.setCapStyle(Qt.RoundCap)
                line.setPen(pen)
                line.show()

                self.__tmpLine = line

            if self.__tmpLine:
                # Update the temp line
                line = self.__tmpLine.line()
                line.setP2(event.pos())
                self.__tmpLine.setLine(line)

        QGraphicsWidget.mouseMoveEvent(self, event)
Пример #3
0
class VertexFixSign(QGraphicsItem):
	def __init__(self, vertex):
		super().__init__(vertex.circle)
		self.topLeft = QPointF(vertex.x() - 8, vertex.y() - 8)
		self.topRight = QPointF(vertex.x() + 8, vertex.y() - 8)
		self.bottomLeft = QPointF(vertex.x() - 8, vertex.y() + 8)
		self.bottomRight = QPointF(vertex.x() + 8, vertex.y() + 8)
		self.line1 = QGraphicsLineItem(QLineF(self.topLeft, self.bottomRight), self)
		self.line2 = QGraphicsLineItem(QLineF(self.topRight, self.bottomLeft), self)
		self.vertex = vertex
		self.setVisible(False)

	def paint(self, painter, option, widget):
		painter.setPen(Qt.black)
		painter.drawLine(self.topLeft, self.bottomRight)
		painter.drawLine(self.topRight, self.bottomLeft)

	def boundingRect(self):
		return QRectF(self.topLeft, self.bottomRight)

	def move(self):
		self.topLeft = QPointF(self.vertex.x() - 8, self.vertex.y() - 8)
		self.topRight = QPointF(self.vertex.x() + 8, self.vertex.y() - 8)
		self.bottomLeft = QPointF(self.vertex.x() - 8, self.vertex.y() + 8)
		self.bottomRight = QPointF(self.vertex.x() + 8, self.vertex.y() + 8)
		self.line1.setLine(QLineF(self.topLeft, self.bottomRight))
		self.line2.setLine(QLineF(self.topRight, self.bottomLeft))
Пример #4
0
class RSegment(QObject):
    def __init__(self, x1, y1, x2, y2, color, line_width):
        self._x1 = x1
        self._y1 = y1
        self._x2 = x2
        self._y2 = y2
        self._pos = QPointF(x1, y1)
        super().__init__()
        self.line = QGraphicsLineItem()
        self.line.setLine(x1, y1, x2, y2)
        pen = QPen()
        pen.setWidthF(line_width)
        pen.setColor(color)
        self.line.setPen(pen)

    def x(self):
        return self._pos.x()

    def y(self):
        return self._pos.y()

    @pyqtProperty(QPointF)
    def pos(self):
        return self._pos

    @pos.setter
    def pos(self, value):
        delta_x = value.x() - self._pos.x()
        delta_y = value.y() - self._pos.y()
        self._x1 = self._x1 + delta_x
        self._y1 = self._y1 + delta_y
        self._x2 = self._x2 + delta_x
        self._y2 = self._y2 + delta_y
        self.line.setLine(self._x1, self._y1, self._x2, self._y2)
        self._pos = value
Пример #5
0
class WayPoint:
    def __init__(self, **kwargs):
        super().__init__()
        self.location = MapPoint()
        self.__dict__.update(kwargs)

        self.pixmap = QGraphicsPixmapItem(
            QPixmap('HOME_DIR + /nparse/data/maps/waypoint.png'))
        self.pixmap.setOffset(-10, -20)

        self.line = QGraphicsLineItem(0.0, 0.0, self.location.x,
                                      self.location.y)
        self.line.setPen(QPen(Qt.green, 1, Qt.DashLine))
        self.line.setVisible(False)

        self.pixmap.setZValue(5)
        self.line.setZValue(4)

        self.pixmap.setPos(self.location.x, self.location.y)

    def update_(self, scale, location=None):
        self.pixmap.setScale(scale)
        if location:
            line = self.line.line()
            line.setP1(QPointF(location.x, location.y))
            self.line.setLine(line)

            pen = self.line.pen()
            pen.setWidth(1 / scale)
            self.line.setPen(pen)

            self.line.setVisible(True)
Пример #6
0
    def __drawPoint(self, x, y, index):
        #横线
        line1 = QGraphicsLineItem()
        line1.setPen(self.pen)
        line1.setLine(x - self.lineRadius, y, x + self.lineRadius, y)

        #竖线
        line2 = QGraphicsLineItem()
        line2.setPen(self.pen)
        line2.setLine(x, y - self.lineRadius, x, y + self.lineRadius)

        #文字说明
        text = QGraphicsTextItem()
        text.setDefaultTextColor(Qt.blue)
        text.setFont(self.font)

        text.setPlainText(self.pointsName[index])
        text.setPos(x, y)

        #放到组中
        pointGroup = QGraphicsItemGroup()
        pointGroup.addToGroup(line1)
        pointGroup.addToGroup(line2)
        pointGroup.addToGroup(text)

        #显示
        if self.pointsItem[index] is not None:
            self.scene.removeItem(self.pointsItem[index])

        #保存到字典
        self.pointsItem[index] = pointGroup
        #显示该点
        self.scene.addItem(self.pointsItem[index])
Пример #7
0
 def updateGraphicsLineItem(line: QGraphicsLineItem, lineF: QLineF = None, pen: QPen = None):
     """
     更新线 QGraphicsLineItem属性
     :param line: QGraphicsLineItem
     :param lineF: 线的坐标大小
     :param pen: 画笔
     """
     if lineF:
         line.setLine(lineF)
     if pen:
         line.setPen(pen)
Пример #8
0
class CrosshairGraphicsItem(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.horizontal_line = QGraphicsLineItem()
        self.vertical_line = QGraphicsLineItem()

        self.horizontal_line.setLine(0, 1080 / 2, 1920, 1080 / 2)
        self.vertical_line.setLine(1920 / 2, 0, 1920 / 2, 1080)

        self.pen = QPen(Qt.white)
        self.horizontal_line.setPen(self.pen)
        self.vertical_line.setPen(self.pen)
Пример #9
0
class ParticipantItem(QGraphicsItem):
    def __init__(self, model_item: Participant, parent=None):
        super().__init__(parent)

        self.model_item = model_item

        self.text = QGraphicsTextItem(self)

        self.line = QGraphicsLineItem(self)
        self.line.setPen(
            QPen(Qt.darkGray, 1, Qt.DashLine, Qt.RoundCap, Qt.RoundJoin))

        self.refresh()

    def update_position(self, x_pos=-1, y_pos=-1):
        if x_pos == -1:
            x_pos = self.x_pos()

        if y_pos == -1:
            y_pos = self.line.line().y2()

        self.text.setPos(x_pos - (self.text.boundingRect().width() / 2), 0)
        self.line.setLine(x_pos, 30, x_pos, y_pos)

    def x_pos(self):
        return self.line.line().x1()

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

    def refresh(self):
        self.text.setPlainText(
            "?" if not self.model_item else self.model_item.shortname)
        if hasattr(self.model_item, "simulate") and self.model_item.simulate:
            font = QFont()
            font.setBold(True)
            self.text.setFont(font)
            self.text.setDefaultTextColor(Qt.darkGreen)
            self.line.setPen(
                QPen(Qt.darkGreen, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        else:
            self.text.setFont(QFont())
            self.text.setDefaultTextColor(constants.LINECOLOR)
            self.line.setPen(
                QPen(Qt.darkGray, 1, Qt.DashLine, Qt.RoundCap, Qt.RoundJoin))

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

    def paint(self, painter, option, widget):
        pass
Пример #10
0
 def generateData(self, canvas, lines, params):
     scene = QGraphicsScene()
     scene.setSceneRect(canvas)
     group = scene.createItemGroup([])
     for line in lines:
         clone = QGraphicsLineItem(line)
         clone.setLine(line.line())
         clone.setPen(line.pen())
         scene.addItem(clone)
         group.addToGroup(clone)
     pixmaps = []
     for i in xrange(params.count):
         pixmaps.append(self.generateRandom(scene, group, canvas, params))
     return pixmaps
Пример #11
0
    def __init__(self, parent, color):
        super().__init__(None)

        self.color = QColor(color)
        self.pen = QPen()
        self.pen.setColor(self.color)
        self.lines = []
        self.offset_x = 10

        for x in range(self.offset_x, 500 + self.offset_x):
            line = QGraphicsLineItem(self)
            line.setLine(x, 20 + 200, x + 1, 20 + 200)
            line.setPen(self.pen)
            self.lines.append(line)
Пример #12
0
class RSegment(QObject):
    def __init__(self, x1, y1, l, a, color, line_width):
        self._point_radius = 3
        self._angle = a
        self._length = l
        self._x1 = x1
        self._y1 = y1
        self._x2 = x1 + self._length * math.cos(self._angle)
        self._y2 = y1 + self._length * math.sin(self._angle)
        self._pos = QVector3D(x1, y1, self._angle)
        super().__init__()
        self.line = QGraphicsLineItem()
        self.rect = QRectF(x1 - self._point_radius, y1 - self._point_radius,
                           2 * self._point_radius, 2 * self._point_radius)
        self.point = QGraphicsEllipseItem()
        self.point.setRect(self.rect)
        self.line.setLine(self._x1, self._y1, self._x2, self._y2)
        pen = QPen()
        pen.setWidthF(line_width)
        pen.setColor(color)
        self.line.setPen(pen)
        self.point.setPen(pen)

    def x(self):
        return self._x1

    def y(self):
        return self._y1

    def angle(self):
        return self._angle

    @pyqtProperty(QVector3D)
    def pos(self):
        return self._pos

    @pos.setter
    def pos(self, value):
        self._pos = value
        self._x1 = self._pos.x()
        self._y1 = self._pos.y()
        self._angle = value.z()
        self._x2 = self._x1 + self._length * math.cos(self._angle)
        self._y2 = self._y1 + self._length * math.sin(self._angle)
        self.line.setLine(self._x1, self._y1, self._x2, self._y2)
        self.rect = QRectF(self._x1 - self._point_radius,
                           self._y1 - self._point_radius,
                           2 * self._point_radius, 2 * self._point_radius)
        self.point.setRect(self.rect)
    def connect_relation(self, x, y, type):
        ver_line = QGraphicsLineItem(self.en_shape)
        horz_line = QGraphicsLineItem(self.en_shape)
        ver_line.setLine(
            QLineF(self.rel_orgn.x(), self.rel_orgn.y(), self.rel_orgn.x(),
                   y - self.en_shape.y()))
        horz_line.setLine(
            QLineF(ver_line.line().x2(),
                   ver_line.line().y2(), x - self.en_shape.x(),
                   y - self.en_shape.y()))

        self.rel_orgn.setX(self.rel_orgn.x() + self.orgn_step)
        if (type == 'p'):
            ver_line.setPen(QPen(Qt.black, 3, Qt.DashLine))
            horz_line.setPen(QPen(Qt.black, 3, Qt.DashLine))
Пример #14
0
class ParticipantItem(QGraphicsItem):
    def __init__(self, model_item: Participant, parent=None):
        super().__init__(parent)

        self.model_item = model_item

        self.text = QGraphicsTextItem(self)

        self.line = QGraphicsLineItem(self)
        self.line.setPen(QPen(Qt.darkGray, 1, Qt.DashLine, Qt.RoundCap, Qt.RoundJoin))

        self.refresh()

    def update_position(self, x_pos=-1, y_pos=-1):
        if x_pos == -1:
            x_pos = self.x_pos()

        if y_pos == -1:
            y_pos = self.line.line().y2()

        self.text.setPos(x_pos - (self.text.boundingRect().width() / 2), 0)
        self.line.setLine(x_pos, 30, x_pos, y_pos)

    def x_pos(self):
        return self.line.line().x1()

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

    def refresh(self):
        self.text.setPlainText("?" if not self.model_item else self.model_item.shortname)
        if hasattr(self.model_item, "simulate") and self.model_item.simulate:
            font = QFont()
            font.setBold(True)
            self.text.setFont(font)
            self.text.setDefaultTextColor(Qt.darkGreen)
            self.line.setPen(QPen(Qt.darkGreen, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        else:
            self.text.setFont(QFont())
            self.text.setDefaultTextColor(constants.LINECOLOR)
            self.line.setPen(QPen(Qt.darkGray, 1, Qt.DashLine, Qt.RoundCap, Qt.RoundJoin))

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

    def paint(self, painter, option, widget):
        pass
Пример #15
0
class CTChartView(QChartView):
    def __init__(self, parent):
        super().__init__(parent)
        self.setRenderHint(QPainter.Antialiasing)
        self.chart = self.chart()
        self.chart.legend().setVisible(False)
        self._chart_loaded = False

        self._chart_horizontal_line = QGraphicsLineItem(0, 0, 0, 0)
        pen = self._chart_horizontal_line.pen()
        pen.setStyle(Qt.DashLine)
        self._chart_horizontal_line.setPen(pen)
        self.scene().addItem(self._chart_horizontal_line)
        self._chart_vertical_line = QGraphicsLineItem(0, 0, 0, 0)
        self._chart_vertical_line.setPen(pen)
        self.scene().addItem(self._chart_vertical_line)

        self._chart_tooltip = QGraphicsTextItem("")
        self._chart_tooltip.setPos(100, 20)
        self.scene().addItem(self._chart_tooltip)

        self._chart_crosshair = QGraphicsTextItem("")
        self._chart_crosshair.setPos(600, 20)
        self.scene().addItem(self._chart_crosshair)

        margins = self.chart.margins()
        margins.setTop(margins.top() + 80)
        self.chart.setMargins(margins)

    def mouseMoveEvent(self, event):
        self._chart_horizontal_line.setLine(0,
                                            event.pos().y(), self.width(),
                                            event.pos().y())
        self._chart_vertical_line.setLine(event.pos().x(), 0,
                                          event.pos().x(), self.height())

        crosshair_coordinates = self.chart.mapToValue(event.pos(),
                                                      self.chart.series()[0])

        self._chart_crosshair.setPlainText(
            " time:\t{0}\n level:\t{1:.8f}".format(
                datetime.fromtimestamp(
                    int(crosshair_coordinates.x() /
                        1000)).strftime('%Y-%m-%d %H:%M:%S'),
                crosshair_coordinates.y()))

        return QChartView.mouseMoveEvent(self, event)
Пример #16
0
    def mouseMoveEvent(self, e):

        self.oldPoint = self.newPoint
        self.newPoint = e.pos()
        if self.firstMove:
            #zamaz kropke
            self.del_point(self.oldPoint.x(), self.oldPoint.y())

        self.firstMove = False
        #print(self.oldPoint.x(), self.oldPoint.y(), self.newPoint.x(), self.newPoint.y())
        line = QGraphicsLineItem()

        line.setLine(
            QLineF(self.oldPoint.x(), self.oldPoint.y(), self.newPoint.x(),
                   self.newPoint.y()))
        line.setPen(
            QPen(self.colors[self.color], self.size, Qt.SolidLine, Qt.RoundCap,
                 Qt.RoundJoin))
        self.scene.addItem(line)
Пример #17
0
    def approve_obj(self):
        self.scene.removeItem(self.rect)
        self.scene.removeItem(self.line)

        viewBBox = self.zoomStack[-1] if len(
            self.zoomStack) else self.sceneRect()
        selectionBBox = self.scene.selectionArea().boundingRect().intersected(
            viewBBox)
        rect = QGraphicsRectItem()
        rect.setRect(selectionBBox)
        rect.setPen(QPen(Qt.green))
        self.rectgroup.addToGroup(rect)
        self.scene.addItem(self.rectgroup)

        line = QGraphicsLineItem()
        line.setLine(QLineF(self.start, self.current))
        line.setPen(QPen(Qt.green))
        self.linegroup.addToGroup(line)
        self.scene.addItem(self.linegroup)

        return (selectionBBox, self.start, self.current)
Пример #18
0
    def __init__(self, position, angle, arrow_size, value, unit):
        super(StressRepresentation, self).__init__()
        arrow_line = QLineF()
        arrow_line.setP1(position)
        arrow_line.setLength(arrow_size)
        arrow_line.setAngle(angle)
        arrow = QGraphicsLineItem()
        arrow.setLine(arrow_line)
        self.addToGroup(arrow)

        arrow_head1_line = QLineF()
        arrow_head1_line.setP1(arrow_line.p1())
        arrow_head1_line.setLength(arrow_size / 4)
        arrow_head1_line.setAngle(arrow_line.angle() + 45)
        arrow_head1 = QGraphicsLineItem()
        arrow_head1.setLine(arrow_head1_line)
        self.addToGroup(arrow_head1)

        arrow_head2_line = QLineF()
        arrow_head2_line.setP1(arrow_line.p1())
        arrow_head2_line.setLength(arrow_size / 4)
        arrow_head2_line.setAngle(arrow_line.angle() - 45)
        arrow_head2 = QGraphicsLineItem()
        arrow_head2.setLine(arrow_head2_line)
        self.addToGroup(arrow_head2)

        text = QGraphicsTextItem()
        text.setPlainText(f'{str(value)}{unit}')
        text.setPos(arrow_line.p2())
        self.addToGroup(text)

        self._set_color()
Пример #19
0
    def addLink(self, output, input):
        """
        Add a link between `output` (:class:`OutputSignal`) and `input`
        (:class:`InputSignal`).

        """
        if not compatible_channels(output, input):
            return

        if output not in self.source.output_channels():
            raise ValueError("%r is not an output channel of %r" % \
                             (output, self.source))

        if input not in self.sink.input_channels():
            raise ValueError("%r is not an input channel of %r" % \
                             (input, self.sink))

        if input.single:
            # Remove existing link if it exists.
            for s1, s2, _ in self.__links:
                if s2 == input:
                    self.removeLink(s1, s2)

        line = QGraphicsLineItem(self)

        source_anchor = self.sourceNodeWidget.anchor(output)
        sink_anchor = self.sinkNodeWidget.anchor(input)

        source_pos = source_anchor.boundingRect().center()
        source_pos = self.mapFromItem(source_anchor, source_pos)

        sink_pos = sink_anchor.boundingRect().center()
        sink_pos = self.mapFromItem(sink_anchor, sink_pos)
        line.setLine(source_pos.x(), source_pos.y(), sink_pos.x(),
                     sink_pos.y())
        pen = QPen(Qt.black, 4)
        pen.setCapStyle(Qt.RoundCap)
        line.setPen(pen)

        self.__links.append(_Link(output, input, line))
Пример #20
0
class ParticipantItem(QGraphicsItem):
    def __init__(self, model_item: Participant, parent=None):
        super().__init__(parent)

        self.model_item = model_item

        self.text = QGraphicsTextItem(self)

        self.line = QGraphicsLineItem(self)
        self.line.setPen(QPen(Qt.darkGray, 1, Qt.DashLine, Qt.RoundCap, Qt.RoundJoin))

        self.refresh()

    def update_position(self, x_pos=-1, y_pos=-1):
        if x_pos == -1:
            x_pos = self.x_pos()

        if y_pos == -1:
            y_pos = self.line.line().y2()

        self.text.setPos(x_pos - (self.text.boundingRect().width() / 2), 0)
        self.line.setLine(x_pos, 30, x_pos, y_pos)

    def x_pos(self):
        return self.line.line().x1()

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

    def refresh(self):
        self.text.setPlainText("?" if not self.model_item else self.model_item.shortname)

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

    def paint(self, painter, option, widget):
        pass
Пример #21
0
    def addCircle(self, index, points, r):
        (x, y) = points

        circleItem = QGraphicsEllipseItem(x - r, y - r, 2 * r, 2 * r)
        circleItem.setPen(self.pen)

        # self.scene.addItem(circleItem)

        #横线
        line1 = QGraphicsLineItem()
        line1.setPen(self.pen)
        line1.setLine(x - self.lineRadius, y, x + self.lineRadius, y)

        #竖线
        line2 = QGraphicsLineItem()
        line2.setPen(self.pen)
        line2.setLine(x, y - self.lineRadius, x, y + self.lineRadius)

        #文字说明
        text = QGraphicsTextItem()
        text.setDefaultTextColor(Qt.blue)
        text.setFont(self.font)

        text.setPlainText(str(self.names[index]))
        text.setPos(x, y)

        #放到组中
        pointGroup = QGraphicsItemGroup()
        pointGroup.addToGroup(line1)
        pointGroup.addToGroup(line2)
        pointGroup.addToGroup(text)
        pointGroup.addToGroup(circleItem)

        #显示该点
        self.scene.addItem(pointGroup)

        self.pointsItem.append(pointGroup)
Пример #22
0
class DoublePipeSegmentItem(SegmentItemBase):
    def __init__(self, startNode, endNode, parent: "ConnectionBase"):
        super().__init__(startNode, endNode, parent)

        self.blueLine = QGraphicsLineItem(self)
        self.redLine = QGraphicsLineItem(self)

    def _createSegment(self, startNode, endNode) -> "SegmentItemBase":
        return DoublePipeSegmentItem(startNode, endNode, self.connection)

    def _setLineImpl(self, x1, y1, x2, y2):
        self.blueLine.setPen(QtGui.QPen(QtCore.Qt.blue, 3))
        self.redLine.setPen(QtGui.QPen(QtCore.Qt.red, 3))
        offset = 3

        if abs(y1 - y2) < 1:
            self.blueLine.setLine(x1, y1 + offset, x2, y2 + offset)
            self.redLine.setLine(x1, y1 - offset, x2, y2 - offset)
        elif abs(x1 - x2) < 1:
            self.blueLine.setLine(x1 + offset, y1, x2 + offset, y2)
            self.redLine.setLine(x1 - offset, y1, x2 - offset, y2)
        else:
            # Initially, connections go directly between its two ports: only
            # afterwards are they broken up into horizontal and vertical segments.
            # This is probably an artifact of back when connections didn't have to
            # be straight. Once, these artifacts have been removed, we should throw here.
            pass
        self.linePoints = QLineF(x1, y1, x2, y2)

    def updateGrad(self):
        self.blueLine.setPen(QtGui.QPen(QtCore.Qt.blue, 3))
        self.redLine.setPen(QtGui.QPen(QtCore.Qt.red, 3))

    def setSelect(self, isSelected: bool) -> None:
        if isSelected:
            selectPen = self._createSelectPen()
            self.blueLine.setPen(selectPen)
            self.redLine.setPen(selectPen)
        else:
            self.updateGrad()

    def setColorAndWidthAccordingToMassflow(self, color, width):
        pass
Пример #23
0
class ChartView(QChartView):

    def __init__(self, *args, **kwargs):
        super(ChartView, self).__init__(*args, **kwargs)
        self.resize(800, 600)
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self.initChart()

        self.toolTipWidget = GraphicsProxyWidget(self._chart)

        # line
        self.lineItem = QGraphicsLineItem(self._chart)
        self.lineItem.setZValue(998)
        self.lineItem.hide()
        
        # 一些固定计算,减少mouseMoveEvent中的计算量
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.min_x, self.max_x = axisX.min(), axisX.max()
        self.min_y, self.max_y = axisY.min(), axisY.max()
        # 坐标系中左上角顶点
        self.point_top = self._chart.mapToPosition(QPointF(self.min_x, self.max_y))
        # 坐标原点坐标
        self.point_bottom = self._chart.mapToPosition(QPointF(self.min_x, self.min_y))
        self.step_x = (self.max_x - self.min_x) / (axisX.tickCount() - 1)
#         self.step_y = (self.max_y - self.min_y) / (axisY.tickCount() - 1)

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(event.pos()).x()
        y = self._chart.mapToValue(event.pos()).y()
        index = round((x - self.min_x) / self.step_x)
        pos_x = self._chart.mapToPosition(
            QPointF(index * self.step_x + self.min_x, self.min_y))
#         print(x, pos_x, index, index * self.step_x + self.min_x)
        # 得到在坐标系中的所有series的类型和点
        points = [(serie, serie.at(index))
                  for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
        if points:
            # 永远在轴上的黑线条
            self.lineItem.setLine(pos_x.x(), self.point_top.y(),
                                  pos_x.x(), self.point_bottom.y())
            self.lineItem.show()
            self.toolTipWidget.show("", points, event.pos() + QPoint(20, 20))
        else:
            self.toolTipWidget.hide()
            self.lineItem.hide()

    def onSeriesHoverd(self, point, state):
        if state:
            try:
                name = self.sender().name()
            except:
                name = ""
            QToolTip.showText(QCursor.pos(), "%s\nx: %s\ny: %s" % 
                              (name, point.x(), point.y()))

    def initChart(self):
        self._chart = QChart(title="Line Chart")
        self._chart.setAcceptHoverEvents(True)
        dataTable = [
            [120, 132, 101, 134, 90, 230, 210],
            [220, 182, 191, 234, 290, 330, 310],
            [150, 232, 201, 154, 190, 330, 410],
            [320, 332, 301, 334, 390, 330, 320],
            [820, 932, 901, 934, 1290, 1330, 1320]
        ]
        for i, data_list in enumerate(dataTable):
            series = QLineSeries(self._chart)
            for j, v in enumerate(data_list):
                series.append(j, v)
            series.setName("Series " + str(i))
            series.setPointsVisible(True)  # 显示原点
            series.hovered.connect(self.onSeriesHoverd)
            self._chart.addSeries(series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        self._chart.axisX().setTickCount(7)  # x轴设置7个刻度
        self._chart.axisY().setTickCount(7)  # y轴设置7个刻度
        self._chart.axisY().setRange(0, 1500)  # 设置y轴范围
        self.setChart(self._chart)
Пример #24
0
class ChartView(QChartView):
    def __init__(self, *args, **kwargs):
        super(ChartView, self).__init__(*args, **kwargs)
        self.resize(800, 600)
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        # 自定义x轴label
        self.category = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
        self.initChart()

        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)

        # line
        self.lineItem = QGraphicsLineItem(self._chart)
        pen = QPen(Qt.gray)
        pen.setWidth(1)

        self.lineItem.setPen(pen)
        self.lineItem.setZValue(996)
        self.lineItem.hide()

        self.lineItemX = QGraphicsLineItem(self._chart)
        penx = QPen(Qt.gray)
        penx.setWidth(1)
        self.lineItemX.setPen(penx)
        self.lineItemX.setZValue(997)
        self.lineItemX.hide()

        # 一些固定计算,减少mouseMoveEvent中的计算量
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.min_x, self.max_x = axisX.min(), axisX.max()
        self.min_y, self.max_y = axisY.min(), axisY.max()

    def resizeEvent(self, event):
        super(ChartView, self).resizeEvent(event)
        # 当窗口大小改变时需要重新计算
        # 坐标系中左上角顶点
        self.point_top = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))
        # 坐标右上点
        self.point_right_top = self._chart.mapToPosition(
            QPointF(self.max_x, self.max_y))
        # 坐标右下点
        self.point_right_bottom = self._chart.mapToPosition(
            QPointF(self.max_x, self.min_y))
        # 坐标原点坐标
        self.point_bottom = self._chart.mapToPosition(
            QPointF(self.min_x, self.min_y))
        self.step_x = (self.max_x - self.min_x) / \
            (self._chart.axisX().tickCount() - 1)

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(pos).x()
        y = self._chart.mapToValue(pos).y()

        # 根据间隔来确定鼠标当前所在的索引
        index = round((x - self.min_x) / self.step_x)
        # 得到在坐标系中的所有正常显示的series的类型和点
        points = [
            (serie, serie.at(index)) for serie in self._chart.series()
            if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y
        ]
        if points:
            # 根据鼠标的点获取对应曲线的坐标点,
            # pos_x = self._chart.mapToPosition(
            #     QPointF(index * self.step_x + self.min_x, self.min_y))
            # 设置鼠标的垂直线
            self.lineItem.setLine(pos.x(), self.point_top.y(), pos.x(),
                                  self.point_bottom.y())
            self.lineItem.show()

            # 设置鼠标的水平线
            self.lineItemX.setLine(self.point_top.x(), pos.y(),
                                   self.point_right_top.x(), pos.y())
            self.lineItemX.show()

            try:
                title = self.category[index]
            except:
                title = ""
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(title, points, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
            self.lineItem.hide()
            self.lineItemX.hide()

    def handleMarkerClicked(self):
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        visible = not marker.series().isVisible()
        # 隐藏或显示series
        marker.series().setVisible(visible)
        marker.setVisible(True)  # 要保证marker一直显示
        # 透明度
        alpha = 1.0 if visible else 0.4
        # 设置label的透明度
        brush = marker.labelBrush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setLabelBrush(brush)
        # 设置marker的透明度
        brush = marker.brush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setBrush(brush)
        # 设置画笔透明度
        pen = marker.pen()
        color = pen.color()
        color.setAlphaF(alpha)
        pen.setColor(color)
        marker.setPen(pen)

    def handleMarkerHovered(self, status):
        # 设置series的画笔宽度
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        series = marker.series()
        if not series:
            return
        pen = series.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if status else -1))
        series.setPen(pen)

    def handleSeriesHoverd(self, point, state):
        # 设置series的画笔宽度
        series = self.sender()  # 信号发送者
        pen = series.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if state else -1))
        series.setPen(pen)

    def initChart(self):
        self._chart = QChart(title="折线图堆叠")
        self._chart.setAcceptHoverEvents(True)
        # Series动画
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        dataTable = [["邮件营销", [120, 132, 101, 134, 90, 230, 210]],
                     ["联盟广告", [220, 182, 191, 234, 290, 330, 310]],
                     ["视频广告", [150, 232, 201, 154, 190, 330, 410]],
                     ["直接访问", [320, 332, 301, 334, 390, 330, 320]],
                     ["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]]]
        for series_name, data_list in dataTable:
            series = QLineSeries(self._chart)
            for j, v in enumerate(data_list):
                series.append(j, v)
            series.setName(series_name)
            series.setPointsVisible(True)  # 显示圆点
            series.hovered.connect(self.handleSeriesHoverd)  # 鼠标悬停
            self._chart.addSeries(series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        axisX = self._chart.axisX()  # x轴
        axisX.setTickCount(7)  # x轴设置7个刻度
        axisX.setGridLineVisible(False)  # 隐藏从x轴往上的线条
        axisY = self._chart.axisY()
        axisY.setTickCount(7)  # y轴设置7个刻度
        axisY.setRange(0, 1500)  # 设置y轴范围
        # 自定义x轴
        axis_x = QCategoryAxis(
            self._chart,
            labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
        axis_x.setTickCount(7)
        axis_x.setGridLineVisible(False)
        min_x = axisX.min()
        max_x = axisX.max()
        step = (max_x - min_x) / (7 - 1)  # 7个tick
        for i in range(0, 7):
            axis_x.append(self.category[i], min_x + i * step)
        self._chart.setAxisX(axis_x, self._chart.series()[-1])
        # self._chart.addAxis(axis_x, Qt.AlignBottom)
        # chart的图例
        legend = self._chart.legend()
        # 设置图例由Series来决定样式
        legend.setMarkerShape(QLegend.MarkerShapeFromSeries)
        # 遍历图例上的标记并绑定信号
        for marker in legend.markers():
            # 点击事件
            marker.clicked.connect(self.handleMarkerClicked)
            # 鼠标悬停事件
            marker.hovered.connect(self.handleMarkerHovered)
        self.setChart(self._chart)
Пример #25
0
class DiagramScene(QGraphicsScene):
    insert_item, insert_line, move_item = range(3)

    item_inserted = pyqtSignal(DiagramItem)

    def __init__(self, context_menu: QMenu, parent: QGraphicsItem = None):
        super(DiagramScene, self).__init__(parent)

        self.context_menu = context_menu
        self.framework_name = frameworks_utils.get_sorted_frameworks_list()[0]
        self.mode = self.move_item
        self.item_type = None
        self.line = None

        # Could be used later to support items & lines coloring.
        self.item_color = Qt.white
        self.line_color = Qt.black

    def set_framework_name(self, framework_name: str):
        self.framework_name = framework_name

    def set_mode(self, mode: int):
        self.mode = mode

    def set_item_type(self, item_type: int):
        self.item_type = item_type

    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        if event.button() != Qt.LeftButton:
            return

        if self.mode == self.insert_item:
            layers = frameworks_utils.get_framework_layers(self.framework_name)
            item = DiagramItem(layers[self.item_type](), self.context_menu)
            item.setBrush(self.item_color)
            item.setPos(event.scenePos())
            self.addItem(item)
            self.item_inserted.emit(item)
        elif self.mode == self.insert_line:
            self.line = QGraphicsLineItem(
                QLineF(event.scenePos(), event.scenePos()))
            self.line.setPen(QPen(self.line_color, 2))
            self.addItem(self.line)

        super(DiagramScene, self).mousePressEvent(event)

    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):
        if self.mode == self.insert_line and self.line:
            new_line = QLineF(self.line.line().p1(), event.scenePos())
            self.line.setLine(new_line)
        elif self.mode == self.move_item:
            super(DiagramScene, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent):
        if self.mode == self.insert_line and self.line:
            start_items = self.items(self.line.line().p1())
            if len(start_items) and start_items[0] == self.line:
                start_items.pop(0)

            end_items = self.items(self.line.line().p2())
            if len(end_items) and end_items[0] == self.line:
                end_items.pop(0)

            self.removeItem(self.line)
            self.line = None

            if len(start_items) and isinstance(start_items[0],
                                               QGraphicsSimpleTextItem):
                start_items[0] = start_items[0].parentItem()

            if len(end_items) and isinstance(end_items[0],
                                             QGraphicsSimpleTextItem):
                end_items[0] = end_items[0].parentItem()

            if (len(start_items) and len(end_items)
                    and isinstance(start_items[0], DiagramItem)
                    and isinstance(end_items[0], DiagramItem)
                    and start_items[0] != end_items[0]):
                start_item = start_items[0]
                end_item = end_items[0]
                arrow = Arrow(start_item, end_item)
                start_item.add_arrow(arrow)
                end_item.add_arrow(arrow)
                arrow.setZValue(-1000.0)
                self.addItem(arrow)
                arrow.updatePosition()

        self.line = None
        super(DiagramScene, self).mouseReleaseEvent(event)

    def isItemChange(self, type_: DiagramItem) -> bool:
        for item in self.selectedItems():
            if isinstance(item, type_):
                return True
        return False
Пример #26
0
class ImageViewer(QGraphicsView, QObject):
    points_selection_sgn = pyqtSignal(list)
    key_press_sgn = pyqtSignal(QtGui.QKeyEvent)

    def __init__(self, parent=None):
        super(ImageViewer, self).__init__(parent)
        self.setDragMode(QGraphicsView.ScrollHandDrag)
        self.setRenderHints(QPainter.Antialiasing
                            | QPainter.SmoothPixmapTransform)
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)

        self._scene = ImageViewerScene(self)
        self.setScene(self._scene)
        self._image = None
        self._image_original = None
        self._pixmap = None
        self._img_contrast = 1.0
        self._img_brightness = 50.0
        self._img_gamma = 1.0
        self._create_grid()
        self._channels = []
        self._current_tool = SELECTION_TOOL.POINTER
        self._dataset = None

        # create grid lines
        pen_color = QColor(255, 255, 255, 255)
        pen = QPen(pen_color)
        pen.setWidth(2)
        pen.setStyle(QtCore.Qt.DotLine)
        self.vline = QGraphicsLineItem()
        self.vline.setVisible(False)
        self.vline.setPen(pen)
        self.hline = QGraphicsLineItem()
        self.hline.setVisible(False)
        self.hline.setPen(pen)
        self._scene.addItem(self.vline)
        self._scene.addItem(self.hline)
        self._current_label = None

        # rectangle selection tool
        self._rectangle_tool_origin = QPoint()
        self._rectangle_tool_picker = QRubberBand(QRubberBand.Rectangle, self)

        # polygon selection tool
        app = QApplication.instance()
        color = app.palette().color(QPalette.Highlight)
        self._polygon_guide_line_pen = QPen(color)
        self._polygon_guide_line_pen.setWidth(3)
        self._polygon_guide_line_pen.setStyle(QtCore.Qt.DotLine)
        self._polygon_guide_line = QGraphicsLineItem()
        self._polygon_guide_line.setVisible(False)
        self._polygon_guide_line.setPen(self._polygon_guide_line_pen)
        self._scene.addItem(self._polygon_guide_line)
        self._current_polygon = None

        # circle
        self._current_ellipse = None

        # free selection tool
        self._current_free_path = None
        self._is_drawing = False
        self._last_point_drawn = QPoint()
        self._last_click_point = None
        self._free_Path_pen = QPen(color)
        self._free_Path_pen.setWidth(10)

        self._extreme_points = Queue(maxsize=4)

    @property
    def current_label(self):
        return self._current_label

    @current_label.setter
    def current_label(self, value):
        self._current_label = value
        if self._current_label:
            color = QColor(self._current_label.color)
            self._free_Path_pen.setColor(color)
            self._polygon_guide_line_pen.setColor(color)
            self._polygon_guide_line.setPen(self._polygon_guide_line_pen)

    @property
    def dataset(self):
        return self._dataset

    @dataset.setter
    def dataset(self, value):
        self._dataset = value

    @property
    def img_contrast(self):
        return self._img_contrast

    @img_contrast.setter
    def img_contrast(self, value):
        self._img_contrast = value

    @property
    def img_gamma(self):
        return self._img_gamma

    @img_gamma.setter
    def img_gamma(self, value):
        self._img_gamma = value

    @property
    def img_brightness(self):
        return self._img_brightness

    @img_brightness.setter
    def img_brightness(self, value):
        self._img_brightness = value

    @property
    def image(self):
        return self._image

    @image.setter
    def image(self, value):
        self._image = value
        self._image_original = value.copy()
        self.update_viewer()

    @property
    def pixmap(self) -> ImagePixmap:
        return self._pixmap

    @gui_exception
    def update_viewer(self, fit_image=True):
        rgb = cv2.cvtColor(self._image, cv2.COLOR_BGR2RGB)
        rgb = ImageUtilities.adjust_image(rgb, self._img_contrast,
                                          self._img_brightness)
        rgb = ImageUtilities.adjust_gamma(rgb, self._img_gamma)
        pil_image = Image.fromarray(rgb)
        qppixmap_image = pil_image.toqpixmap()
        x, y = -qppixmap_image.width() / 2, -qppixmap_image.height() / 2

        if self._pixmap:
            self._pixmap.resetTransform()
            self._pixmap.setPixmap(qppixmap_image)
            self._pixmap.setOffset(x, y)
        else:
            self._pixmap = ImagePixmap()
            self._pixmap.setPixmap(qppixmap_image)
            self._pixmap.setOffset(x, y)
            self._scene.addItem(self._pixmap)
            self._pixmap.signals.hoverEnterEventSgn.connect(
                self.pixmap_hoverEnterEvent_slot)
            self._pixmap.signals.hoverLeaveEventSgn.connect(
                self.pixmap_hoverLeaveEvent_slot)
            self._pixmap.signals.hoverMoveEventSgn.connect(
                self.pixmap_hoverMoveEvent_slot)
        self._hide_guide_lines()
        if fit_image:
            self.fit_to_window()

    @gui_exception
    def reset_viewer(self):
        self._img_contrast = 1.0
        self._img_brightness = 50.0
        self._img_gamma = 1.0
        self._image = self._image_original.copy()

    @gui_exception
    def equalize_histogram(self):
        self._image = ImageUtilities.histogram_equalization(self._image)

    @gui_exception
    def correct_lightness(self):
        self._image = ImageUtilities.correct_lightness(self._image)

    def clusterize(self, k):
        self._image = ImageUtilities.kmeans(self._image.copy(), k)

    @property
    def current_tool(self):
        return self._current_tool

    @current_tool.setter
    def current_tool(self, value):
        self._polygon_guide_line.hide()
        self._current_polygon = None
        self._current_free_path = None
        self._current_ellipse = None
        self._is_drawing = value == SELECTION_TOOL.FREE
        self._current_tool = value
        self.clear_extreme_points()
        if value == SELECTION_TOOL.POINTER:
            self.enable_items(True)
        else:
            self.enable_items(False)

    def fit_to_window(self):
        if not self._pixmap or not self._pixmap.pixmap():
            return
        self.resetTransform()
        self.setTransform(QtGui.QTransform())
        self.fitInView(self._pixmap, QtCore.Qt.KeepAspectRatio)

    def _create_grid(self, gridSize=15):
        app: QApplication = QApplication.instance()
        curr_theme = "dark"
        if app:
            curr_theme = app.property("theme")
        if curr_theme == "light":
            color1 = QtGui.QColor("white")
            color2 = QtGui.QColor(237, 237, 237)
        else:
            color1 = QtGui.QColor(20, 20, 20)
            color2 = QtGui.QColor(0, 0, 0)
        backgroundPixmap = QtGui.QPixmap(gridSize * 2, gridSize * 2)
        backgroundPixmap.fill(color1)
        painter = QtGui.QPainter(backgroundPixmap)
        painter.fillRect(0, 0, gridSize, gridSize, color2)
        painter.fillRect(gridSize, gridSize, gridSize, gridSize, color2)
        painter.end()
        self._scene.setBackgroundBrush(QtGui.QBrush(backgroundPixmap))

    def wheelEvent(self, event: QWheelEvent):
        adj = (event.angleDelta().y() / 120) * 0.1
        self.scale(1 + adj, 1 + adj)

    @gui_exception
    def keyPressEvent(self, event: QKeyEvent):
        if event.key() == QtCore.Qt.Key_Space:
            image_rect: QRectF = self._pixmap.sceneBoundingRect()
            if self.current_tool == SELECTION_TOOL.POLYGON and self._current_polygon:
                points = self._current_polygon.points
                self._polygon_guide_line.hide()
                self.setDragMode(QGraphicsView.ScrollHandDrag)
                if len(points) <= 2:
                    self._current_polygon.delete_item()
                self.current_tool = SELECTION_TOOL.POINTER
            elif self.current_tool == SELECTION_TOOL.EXTREME_POINTS and \
                    self._extreme_points.full():
                points = []
                image_offset = QPointF(image_rect.width() / 2,
                                       image_rect.height() / 2)
                for pt in self._extreme_points.queue:
                    pt: EditablePolygonPoint
                    center = pt.sceneBoundingRect().center()
                    x = math.floor(center.x() + image_offset.x())
                    y = math.floor(center.y() + image_offset.y())
                    points.append([x, y])
                self.points_selection_sgn.emit(points)
                self.current_tool = SELECTION_TOOL.POINTER
        else:
            event.ignore()

    # guide lines events
    def _show_guide_lines(self):
        if self.hline and self.vline:
            self.hline.show()
            self.vline.show()

    def _hide_guide_lines(self):
        if self.hline and self.vline:
            self.hline.hide()
            self.vline.hide()

    def _update_guide_lines(self, x, y):
        bbox: QRect = self._pixmap.boundingRect()
        offset = QPointF(bbox.width() / 2, bbox.height() / 2)
        self.vline.setLine(x, -offset.y(), x, bbox.height() - offset.y())
        self.vline.setZValue(1)
        self.hline.setLine(-offset.x(), y, bbox.width() - offset.x(), y)
        self.hline.setZValue(1)

    def pixmap_hoverMoveEvent_slot(self, evt: QGraphicsSceneHoverEvent, x, y):
        self._update_guide_lines(x, y)

    def pixmap_hoverEnterEvent_slot(self):
        self._show_guide_lines()

    def pixmap_hoverLeaveEvent_slot(self):
        self._hide_guide_lines()

    def delete_polygon_slot(self, polygon: EditablePolygon):
        self._current_polygon = None
        self.current_tool = SELECTION_TOOL.POINTER
        self._polygon_guide_line.hide()

    @gui_exception
    def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None:
        image_rect: QRectF = self._pixmap.boundingRect()
        mouse_pos = self.mapToScene(evt.pos())
        if evt.buttons() == QtCore.Qt.LeftButton:
            if self.current_tool == SELECTION_TOOL.BOX:
                # create rectangle
                self.setDragMode(QGraphicsView.NoDrag)
                self._rectangle_tool_origin = evt.pos()
                geometry = QRect(self._rectangle_tool_origin, QSize())
                self._rectangle_tool_picker.setGeometry(geometry)
                self._rectangle_tool_picker.show()
            elif self.current_tool == SELECTION_TOOL.POLYGON:
                if image_rect.contains(mouse_pos):
                    if self._current_polygon is None:
                        self._current_polygon = EditablePolygon()
                        self._current_polygon.label = self._current_label
                        self._current_polygon.tag = self._dataset
                        self._current_polygon.signals.deleted.connect(
                            self.delete_polygon_slot)
                        self._scene.addItem(self._current_polygon)
                        self._current_polygon.addPoint(mouse_pos)
                    else:
                        self._current_polygon.addPoint(mouse_pos)
            elif self.current_tool == SELECTION_TOOL.ELLIPSE:
                if image_rect.contains(mouse_pos):
                    self.setDragMode(QGraphicsView.NoDrag)
                    ellipse_rec = QtCore.QRectF(mouse_pos.x(), mouse_pos.y(),
                                                0, 0)
                    self._current_ellipse = EditableEllipse()
                    self._current_ellipse.tag = self.dataset
                    self._current_ellipse.label = self._current_label
                    self._current_ellipse.setRect(ellipse_rec)
                    self._scene.addItem(self._current_ellipse)

            elif self.current_tool == SELECTION_TOOL.FREE:
                # consider only the points into the image
                if image_rect.contains(mouse_pos):
                    self.setDragMode(QGraphicsView.NoDrag)
                    self._last_point_drawn = mouse_pos
                    self._current_free_path = QGraphicsPathItem()
                    self._current_free_path.setOpacity(0.6)
                    self._current_free_path.setPen(self._free_Path_pen)
                    painter = QPainterPath()
                    painter.moveTo(self._last_point_drawn)
                    self._current_free_path.setPath(painter)
                    self._scene.addItem(self._current_free_path)

            elif self.current_tool == SELECTION_TOOL.EXTREME_POINTS:
                if image_rect.contains(mouse_pos):
                    if not self._extreme_points.full():

                        def delete_point(idx):
                            del self._extreme_points.queue[idx]

                        idx = self._extreme_points.qsize()
                        editable_pt = EditablePolygonPoint(idx)
                        editable_pt.signals.deleted.connect(delete_point)
                        editable_pt.setPos(mouse_pos)
                        self._scene.addItem(editable_pt)
                        self._extreme_points.put(editable_pt)

        else:
            self.setDragMode(QGraphicsView.ScrollHandDrag)
        super(ImageViewer, self).mousePressEvent(evt)

    @gui_exception
    def mouseMoveEvent(self, evt: QtGui.QMouseEvent) -> None:
        mouse_pos = self.mapToScene(evt.pos())
        image_rect: QRectF = self._pixmap.boundingRect()
        if self.current_tool == SELECTION_TOOL.BOX:
            if not self._rectangle_tool_origin.isNull():
                geometry = QRect(self._rectangle_tool_origin,
                                 evt.pos()).normalized()
                self._rectangle_tool_picker.setGeometry(geometry)
        elif self.current_tool == SELECTION_TOOL.POLYGON:
            if self._current_polygon and image_rect.contains(mouse_pos):
                if self._current_polygon.count > 0:
                    last_point: QPointF = self._current_polygon.last_point
                    self._polygon_guide_line.setZValue(1)
                    self._polygon_guide_line.show()
                    mouse_pos = self.mapToScene(evt.pos())
                    self._polygon_guide_line.setLine(last_point.x(),
                                                     last_point.y(),
                                                     mouse_pos.x(),
                                                     mouse_pos.y())
            else:
                self._polygon_guide_line.hide()
        elif self.current_tool == SELECTION_TOOL.ELLIPSE:
            if self._current_ellipse and image_rect.contains(mouse_pos):
                ellipse_rect = self._current_ellipse.rect()
                ellipse_pos = QPointF(ellipse_rect.x(), ellipse_rect.y())
                distance = math.hypot(mouse_pos.x() - ellipse_pos.x(),
                                      mouse_pos.y() - ellipse_pos.y())
                ellipse_rect.setWidth(distance)
                ellipse_rect.setHeight(distance)
                self._current_ellipse.setRect(ellipse_rect)
        elif self.current_tool == SELECTION_TOOL.FREE and evt.buttons(
        ) and QtCore.Qt.LeftButton:
            if self._current_free_path and image_rect.contains(mouse_pos):
                painter: QPainterPath = self._current_free_path.path()
                self._last_point_drawn = self.mapToScene(evt.pos())
                painter.lineTo(self._last_point_drawn)
                self._current_free_path.setPath(painter)
        super(ImageViewer, self).mouseMoveEvent(evt)

    @gui_exception
    def mouseReleaseEvent(self, evt: QtGui.QMouseEvent) -> None:
        image_rect: QRectF = self._pixmap.boundingRect()
        if self.current_tool == SELECTION_TOOL.BOX:
            roi: QRect = self._rectangle_tool_picker.geometry()
            roi: QRectF = self.mapToScene(roi).boundingRect()
            self._rectangle_tool_picker.hide()
            if image_rect == roi.united(image_rect):
                rect = EditableBox(roi)
                rect.label = self.current_label
                rect.tag = self._dataset
                self._scene.addItem(rect)
                self.current_tool = SELECTION_TOOL.POINTER
                self.setDragMode(QGraphicsView.ScrollHandDrag)

        elif self.current_tool == SELECTION_TOOL.ELLIPSE and self._current_ellipse:
            roi: QRect = self._current_ellipse.boundingRect()
            if image_rect == roi.united(image_rect):
                self.current_tool = SELECTION_TOOL.POINTER
                self.setDragMode(QGraphicsView.ScrollHandDrag)
            else:
                self._current_ellipse.delete_item()
        elif self.current_tool == SELECTION_TOOL.FREE and self._current_free_path:
            # create polygon
            self._current_free_path: QGraphicsPathItem
            path_rect = self._current_free_path.boundingRect()
            if image_rect == path_rect.united(image_rect):
                path = self._current_free_path.path()
                path_polygon = EditablePolygon()
                path_polygon.tag = self.dataset
                path_polygon.label = self.current_label
                self._scene.addItem(path_polygon)
                for i in range(0, path.elementCount(), 10):
                    x, y = path.elementAt(i).x, path.elementAt(i).y
                    path_polygon.addPoint(QPointF(x, y))
            self._scene.removeItem(self._current_free_path)
            self.current_tool = SELECTION_TOOL.POINTER
            self.setDragMode(QGraphicsView.ScrollHandDrag)
        super(ImageViewer, self).mouseReleaseEvent(evt)

    def remove_annotations(self):
        for item in self._scene.items():
            if isinstance(item, EditableItem):
                item.delete_item()

    def remove_annotations_by_label(self, label_name):
        for item in self._scene.items():
            if isinstance(item, EditableItem):
                if item.label and item.label.name == label_name:
                    item.delete_item()

    def enable_items(self, value):
        for item in self._scene.items():
            if isinstance(item, EditableItem):
                item.setEnabled(value)

    def clear_extreme_points(self):
        if self._extreme_points.qsize() > 0:
            for pt in self._extreme_points.queue:
                self._scene.removeItem(pt)
            self._extreme_points.queue.clear()
Пример #27
0
class PreXoverItemGroup(QGraphicsEllipseItem):
    """Summary

    Attributes:
        active_wedge_gizmo (TYPE): Description
        fwd_prexover_items (dict): Description
        HUE_FACTOR (float): Description
        id_num (TYPE): Description
        is_active (TYPE): Description
        model_part (Part): The model part
        rev_prexover_items (dict): Description
        SPIRAL_FACTOR (float): Description
        virtual_helix_item (VirtualHelixItem): Description
    """
    HUE_FACTOR = 1.6
    SPIRAL_FACTOR = 0.4

    def __init__(self, radius, rect, virtual_helix_item, is_active):
        """Summary

        Args:
            radius (TYPE): Description
            rect (TYPE): Description
            virtual_helix_item (VirtualHelixItem): Description
            is_active (TYPE): Description
        """
        super(PreXoverItemGroup, self).__init__(rect, virtual_helix_item)

        self._radius = radius
        self._rect = rect
        self.virtual_helix_item = virtual_helix_item
        self.model_part = virtual_helix_item.part()
        self.id_num = virtual_helix_item.idNum()
        self.is_active = is_active
        self.active_wedge_gizmo = WedgeGizmo(radius, rect, self)
        self.fwd_prexover_items = fwd_pxis = {}
        self.rev_prexover_items = rev_pxis = {}
        self._colors = self._getColors()
        self.addItems()
        self.setPen(getNoPen())
        z = styles.ZPXIGROUP + 10 if is_active else styles.ZPXIGROUP
        self.setZValue(z)
        self.setTransformOriginPoint(rect.center())

        bpr, tpr, eulerZ = virtual_helix_item.getProperty(['bases_per_repeat',
                                                           'turns_per_repeat',
                                                           'eulerZ'])

        self.setRotation(-eulerZ)  # add 180

        # for baseNearestPoint
        fwd_pos, rev_pos = [], []
        step_size = self.virtual_helix_item.getProperty('bases_per_repeat')
        for i in range(int(step_size)):
            fwd_pos.append((fwd_pxis[i].scenePos().x(),
                            fwd_pxis[i].scenePos().y()))
            rev_pos.append((rev_pxis[i].scenePos().x(),
                            rev_pxis[i].scenePos().y()))
        self.fwd_pos_array = np.asarray(fwd_pos)
        self.rev_pos_array = np.asarray(rev_pos)
        self.baseNearLine = QGraphicsLineItem(self)
        self.baseNearLine.setPen(getPenObj("#000000", 0.25, capstyle=Qt.RoundCap))
    # end def

    def mousePressEvent(self, event):
        print("PreXoverGroup press")

    def mouseMoveEvent(self, event):
        print("PreXoverGroup move")

    def mouseReleaseEvent(self, event):
        print("PreXoverGroup release")

    ### ACCESSORS ###
    def partItem(self):
        """Summary

        Returns:
            TYPE: Description
        """
        return self.virtual_helix_item.partItem()
    # end def

    def getItem(self, is_fwd, step_idx):
        """Summary

        Args:
            is_fwd (TYPE): Description
            step_idx (int): the base index within the virtual helix

        Returns:
            TYPE: Description
        """
        items = self.fwd_prexover_items if is_fwd else self.rev_prexover_items
        if step_idx in items:
            return items[step_idx]
        else:
            return None
    # end def

    def getItemIdx(self, is_fwd, idx):
        """Summary

        Args:
            is_fwd (TYPE): Description
            idx (int): the base index within the virtual helix

        Returns:
            TYPE: Description
        """
        step_size = self.virtual_helix_item.getProperty('bases_per_repeat')
        return self.getItem(is_fwd, idx % step_size)
    # end def

    ### EVENT HANDLERS ###

    ### PRIVATE SUPPORT METHODS ###
    def _getColors(self):
        """Summary

        Returns:
            TYPE: Description
        """
        step_size = int(self.virtual_helix_item.getProperty('bases_per_repeat'))
        hue_scale = step_size*self.HUE_FACTOR
        return [QColor.fromHsvF(i / hue_scale, 0.75, 0.8).name() for i in range(step_size)]
    # end def

    ### PUBLIC SUPPORT METHODS ###
    def addItems(self):
        """Summary
        """
        radius = self._radius
        step_size, bases_per_turn, tpb, mgroove = self.virtual_helix_item.getAngularProperties()
        # print("TPB", tpb, step_size)
        iw = PXI_PP_ITEM_WIDTH
        spiral_factor = self.SPIRAL_FACTOR
        colors = self._colors
        ctr = self.mapToParent(self._rect).boundingRect().center()
        x = ctr.x() + radius - PXI_PP_ITEM_WIDTH
        y = ctr.y()
        # tpb = -tpb
        # Qt +angle is Clockwise (CW), model +angle is CCW
        mgroove = -mgroove
        fwd_pxis = self.fwd_prexover_items
        rev_pxis = self.rev_prexover_items
        for i in range(int(step_size)):
            inset = i*spiral_factor  # spiral layout
            fwd = PreXoverItem(i, tpb, step_size, colors[i], self, is_fwd=True)
            rev = PreXoverItem(i, tpb, step_size, colors[-1 - i], self, is_fwd=False)
            fwd.setPos(x - inset, y)
            rev.setPos(x - inset, y)
            fwd.setTransformOriginPoint((-radius + iw + inset), 0)
            rev.setTransformOriginPoint((-radius + iw + inset), 0)
            fwd.setRotation(round(i*tpb % 360, 3))
            rev.setRotation(round((i*tpb + mgroove) % 360, 3))
            fwd.setBondLineLength(inset + iw)
            rev.setBondLineLength(inset + iw)
            fwd_pxis[i] = fwd
            rev_pxis[i] = rev

        for i in range(int(step_size) - 1):
            fwd, next_fwd = fwd_pxis[i], fwd_pxis[i + 1]
            j = (step_size - 1) - i
            rev, next_rev = rev_pxis[j], rev_pxis[j - 1]
            fwd.set3pItem(next_fwd)
            rev.set3pItem(next_rev)
            next_fwd.set5pItem(fwd)
            next_rev.set5pItem(rev)
    # end def

    def baseNearestPoint(self, is_fwd, scene_pos):
        """Summary

        Args:
            is_fwd (bool): used to check fwd or rev lists.
            scene_pos (QPointF): scene coordinate position

        Returns:
            PreXoverItem: base nearest to position
        """
        pos_array = self.fwd_pos_array if is_fwd else self.rev_pos_array
        dist_2 = np.sum((pos_array - (scene_pos.x(), scene_pos.y()))**2, axis=1)
        near_idx = np.argmin(dist_2)
        near_pxi = self.fwd_prexover_items[near_idx] if is_fwd else self.rev_prexover_items[near_idx]
        # Draw a line
        p1 = self.mapFromScene(scene_pos.x(), scene_pos.y())
        p2 = self.mapFromScene(near_pxi.scenePos())
        line = QLineF(p1, p2)
        self.baseNearLine.setLine(line)

    def destroyItem(self):
        """Summary
        """
        fpxis = self.fwd_prexover_items
        rpxis = self.rev_prexover_items
        scene = self.scene()
        for i in range(len(fpxis)):
            x = fpxis.pop(i)
            x.destroyItem()
            x = rpxis.pop(i)
            x.destroyItem()
        self.virtual_helix_item = None
        self.model_part = None
        scene.removeItem(self.active_wedge_gizmo)
        self.active_wedge_gizmo = None
        scene.removeItem(self)
    # end def

    def updateTurnsPerRepeat(self):
        """Summary
        """
        step_size, bases_per_turn, tpb, mgroove = self.virtual_helix_item.getAngularProperties()
        mgroove = -mgroove
        fpxis = self.fwd_prexover_items
        rpxis = self.rev_prexover_items
        for i in range(int(step_size)):
            fwd = self.fwd_prexover_items[i]
            rev = self.rev_prexover_items[i]
            fwd.setRotation(round((i*tpb) % 360, 3))
            rev.setRotation(round((i*tpb + mgroove) % 360, 3))
        for i in range(int(step_size) - 1):
            fwd, next_fwd = fpxis[i], fpxis[i + 1]
            j = (step_size - 1) - i
            rev, next_rev = rpxis[j], rpxis[j - 1]
            fwd.set3pItem(next_fwd)
            rev.set3pItem(next_rev)
            next_fwd.set5pItem(fwd)
            next_rev.set5pItem(rev)
    # end def

    def partCrossoverSpanAngle(self):
        """
        Returns:
            int: Crossover span angle from Part.
        """
        return self.virtual_helix_item.partCrossoverSpanAngle()

    def updateModelActiveBaseInfo(self, pre_xover_info):
        """Notify model of pre_xover_item hover state.
        """
        self.model_part.setActiveBaseInfo(pre_xover_info)
Пример #28
0
class MonoScene(QGraphicsScene):
    def __init__(self):
        super().__init__()

        self.language = settings.LANG
        self.csv_separator = settings.CSV_SEPARATOR
        self.fmt_float = settings.FMT_FLOAT

        self.nodes = {}
        self.nb_nodes = 0
        self.adj_list = {}
        self.current_line = None
        self.current_port = None
        self.project_path = ''

        self.setSceneRect(
            QRectF(0, 0, settings.SCENE_SIZE[0], settings.SCENE_SIZE[1]))
        self.transform = QTransform()
        self.selectionChanged.connect(self.selection_changed)

        self._init_with_default_node()

    def reinit(self):
        self.clear()
        self._init_with_default_node()
        self.update()

    def _init_without_node(self):
        self.nodes = {}
        self.nb_nodes = 0
        self.adj_list = {}

    def _init_with_default_node(self):
        self._init_without_node()
        self.add_node(LoadSerafin2DNode(0), 50, 50)
        self._add_current_line()

    def _add_current_line(self):
        self.current_line = QGraphicsLineItem()
        self.addItem(self.current_line)
        self.current_line.setVisible(False)
        pen = QPen(QColor(0, 0, 0))
        pen.setWidth(2)
        self.current_line.setPen(pen)
        self.current_port = None

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        for node_index, node in enumerate(self.nodes.values()):
            for port_index, port in enumerate(node.ports):
                if port.isSelected():
                    self.current_port = (node_index, port_index)

    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        if self.current_port is not None:
            port = self.nodes[self.current_port[0]].ports[self.current_port[1]]
            self.current_line.setLine(
                QLineF(port.mapToScene(port.rect().center()),
                       event.scenePos()))
            self.current_line.setVisible(True)

    def mouseReleaseEvent(self, event):
        if self.current_port is not None:
            target_item = self.itemAt(event.scenePos(), self.transform)
            if isinstance(target_item, Port):
                self._handle_add_link(target_item)
            self.current_line.setVisible(False)
            self.current_port = None
        super().mouseReleaseEvent(event)

    def mouseDoubleClickEvent(self, event):
        super().mouseDoubleClickEvent(event)
        target_item = self.itemAt(event.scenePos(), self.transform)
        if isinstance(target_item, Link):
            self._handle_remove_link(target_item)
        elif isinstance(target_item, Box):
            node = target_item.parentItem()
            node.configure()
            self.selection_changed()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Delete:
            selected = self.selectedItems()
            if selected:
                link = selected[0]
                self._handle_remove_link(link)

    def selection_changed(self):
        view = self.views()[0]
        selected = self.selectedItems()
        if not selected:
            view.deselect_node()
            return
        selected = selected[0]
        if isinstance(selected, Node):
            view.select_node(selected)
        else:
            view.deselect_node()

    def add_node(self, node, x, y):
        logger.debug('Add node #%i %s' %
                     (node.index(), node.label.replace('\n', ' ')))
        self.addItem(node)
        self.nodes[node.index()] = node
        self.adj_list[node.index()] = set()
        self.nb_nodes += 1
        node.moveBy(x, y)

    def save(self):
        links = []
        for item in self.items():
            if isinstance(item, Link):
                links.append(item.save())

        yield '.'.join([self.language, self.csv_separator])
        yield '%d %d' % (self.nb_nodes, len(links))
        for node in self.nodes.values():
            yield node.save()

        for link in links:
            yield link

    def load(self, filename):
        logger.debug('Loading project in MONO: %s' % filename)
        self.project_path = filename
        self.clear()
        self._add_current_line()
        self._init_without_node()
        try:
            with open(filename, 'r') as f:
                self.language, self.csv_separator = f.readline().rstrip(
                ).split('.')
                nb_nodes, nb_links = map(int, f.readline().split())
                for _ in range(nb_nodes):
                    line = f.readline().rstrip().split('|')
                    category, name, index, x, y = line[:5]
                    node = NODES[category][name](int(index))
                    node.load(line[5:])
                    self.add_node(node, float(x), float(y))

                for _ in range(nb_links):
                    from_node_index, from_port_index, \
                                     to_node_index, to_port_index = map(int, f.readline().rstrip().split('|'))
                    from_node = self.nodes[from_node_index]
                    to_node = self.nodes[to_node_index]
                    from_port = from_node.ports[from_port_index]
                    to_port = to_node.ports[to_port_index]
                    _, _ = add_link(from_port, to_port)
                    link = Link(from_port, to_port)
                    from_node.add_link(link)
                    to_node.add_link(link)
                    self.addItem(link)
                    link.setZValue(-1)
                    self.adj_list[from_node_index].add(to_node_index)
                    self.adj_list[to_node_index].add(from_node_index)

            self.update()
            return True
        except (IndexError, ValueError, KeyError) as e:
            logger.exception(e)
            logger.error("An exception occured while loading project in MONO.")
            self.reinit()
            return False

    def run_all(self):
        roots = self._to_sources()

        for root in roots:
            self.nodes[root].run_downward()

    def _to_sources(self):
        roots = []
        visited = {node: False for node in self.nodes}

        def forward(node):
            for port in node.ports:
                if port.type == Port.OUTPUT:
                    if port.has_children():
                        for child in port.children:
                            child_node = child.parentItem()
                            if not visited[child_node.index()]:
                                visited[child_node.index()] = False
                                forward(child_node)

        def backward(node_index):
            if not visited[node_index]:
                visited[node_index] = True
                node = self.nodes[node_index]
                if node.ports[0].type == Port.INPUT:
                    backward(node.ports[0].parentItem().index())
                    if len(node.ports
                           ) > 1 and node.ports[1].type == Port.INPUT:
                        backward(node.ports[1].parentItem().index())
                else:
                    roots.append(node_index)
                    forward(node)

        for node in self.nodes:
            backward(node)

        return roots

    def _handle_add_link(self, target_item):
        port_index = target_item.index()
        node_index = target_item.parentItem().index()
        if node_index == self.current_port[0]:
            return

        from_node, to_node, from_port, to_port = self._permute(
            self.current_port[0], self.current_port[1], node_index, port_index)
        if from_port is None:
            QMessageBox.critical(
                None, 'Error',
                'Connections can only be established between Output and Input ports!',
                QMessageBox.Ok)
            return
        success, cause = add_link(from_port, to_port)
        if success:
            link = Link(from_port, to_port)
            from_node.add_link(link)
            to_node.add_link(link)
            self.addItem(link)
            link.setZValue(-1)
            self.update()
            self.adj_list[from_node.index()].add(to_node.index())
            self.adj_list[to_node.index()].add(from_node.index())
        else:
            if cause == 'already':
                QMessageBox.critical(None, 'Error',
                                     'These two ports are already connected.',
                                     QMessageBox.Ok)
            elif cause == 'type':
                QMessageBox.critical(
                    None, 'Error',
                    'These two ports cannot be connected: incompatible data types.',
                    QMessageBox.Ok)
            else:
                QMessageBox.critical(
                    None, 'Error',
                    'The input port is already connected to another port.',
                    QMessageBox.Ok)

    def _handle_remove_link(self, target_item):
        msg = QMessageBox.warning(None, 'Confirm delete',
                                  'Do you want to delete this link?',
                                  QMessageBox.Ok | QMessageBox.Cancel,
                                  QMessageBox.Ok)
        if msg == QMessageBox.Cancel:
            return
        from_index, to_index = target_item.from_port.parentItem().index(
        ), target_item.to_port.parentItem().index()
        self.adj_list[from_index].remove(to_index)
        self.adj_list[to_index].remove(from_index)
        target_item.remove()
        self.removeItem(target_item)

    def handle_remove_node(self, node):
        msg = QMessageBox.warning(None, 'Confirm delete',
                                  'Do you want to delete this node?',
                                  QMessageBox.Ok | QMessageBox.Cancel,
                                  QMessageBox.Ok)
        if msg == QMessageBox.Cancel:
            return
        self.removeItem(node)
        for link in node.links.copy():
            link.remove()
            self.removeItem(link)
        del self.nodes[node.index()]

        new_nodes = {}
        self.nb_nodes -= 1

        for index, node in zip(range(self.nb_nodes), self.nodes.values()):
            new_nodes[index] = node
            node.set_index(index)
        self.nodes = new_nodes

        self.adj_list = {node: set() for node in self.nodes}
        for item in self.items():
            if isinstance(item, Link):
                from_index, to_index = item.from_port.parentItem().index(
                ), item.to_port.parentItem().index()
                self.adj_list[from_index].add(to_index)
                self.adj_list[to_index].add(from_index)

    def _permute(self, first_node_index, first_port_index, second_node_index,
                 second_port_index):
        first_node = self.nodes[first_node_index]
        second_node = self.nodes[second_node_index]
        p1 = first_node.ports[first_port_index]
        p2 = second_node.ports[second_port_index]
        if p1.type == Port.OUTPUT and p2.type == Port.INPUT:
            return first_node, second_node, p1, p2
        elif p1.type == Port.INPUT and p2.type == Port.OUTPUT:
            return second_node, first_node, p2, p1
        return None, None, None, None

    def suffix_pool(self):
        suffix = []
        for node in self.nodes.values():
            if hasattr(node, 'suffix'):
                suffix.append(node.suffix)
        return suffix

    def not_connected(self):
        nb_cc, _ = count_cc(list(self.adj_list.keys()), self.adj_list)
        return nb_cc > 1
Пример #29
0
class DiagramScene(QGraphicsScene):
    InsertItem, InsertLine, InsertText, MoveItem  = range(4)

    itemInserted = pyqtSignal(DiagramItem)

    textInserted = pyqtSignal(QGraphicsTextItem)

    itemSelected = pyqtSignal(QGraphicsItem)

    def __init__(self, itemMenu, parent=None):
        super(DiagramScene, self).__init__(parent)

        self.myItemMenu = itemMenu
        self.myMode = self.MoveItem
        self.myItemType = DiagramItem.Step
        self.line = None
        self.textItem = None
        self.myItemColor = Qt.white
        self.myTextColor = Qt.black
        self.myLineColor = Qt.black
        self.myFont = QFont()

    def setLineColor(self, color):
        self.myLineColor = color
        if self.isItemChange(Arrow):
            item = self.selectedItems()[0]
            item.setColor(self.myLineColor)
            self.update()

    def setTextColor(self, color):
        self.myTextColor = color
        if self.isItemChange(DiagramTextItem):
            item = self.selectedItems()[0]
            item.setDefaultTextColor(self.myTextColor)

    def setItemColor(self, color):
        self.myItemColor = color
        if self.isItemChange(DiagramItem):
            item = self.selectedItems()[0]
            item.setBrush(self.myItemColor)

    def setFont(self, font):
        self.myFont = font
        if self.isItemChange(DiagramTextItem):
            item = self.selectedItems()[0]
            item.setFont(self.myFont)

    def setMode(self, mode):
        self.myMode = mode

    def setItemType(self, type):
        self.myItemType = type

    def editorLostFocus(self, item):
        cursor = item.textCursor()
        cursor.clearSelection()
        item.setTextCursor(cursor)

        if item.toPlainText():
            self.removeItem(item)
            item.deleteLater()

    def mousePressEvent(self, mouseEvent):
        if (mouseEvent.button() != Qt.LeftButton):
            return

        if self.myMode == self.InsertItem:
            item = DiagramItem(self.myItemType, self.myItemMenu)
            item.setBrush(self.myItemColor)
            self.addItem(item)
            item.setPos(mouseEvent.scenePos())
            self.itemInserted.emit(item)
        elif self.myMode == self.InsertLine:
            self.line = QGraphicsLineItem(QLineF(mouseEvent.scenePos(),
                    mouseEvent.scenePos()))
            self.line.setPen(QPen(self.myLineColor, 2))
            self.addItem(self.line)
        elif self.myMode == self.InsertText:
            textItem = DiagramTextItem()
            textItem.setFont(self.myFont)
            textItem.setTextInteractionFlags(Qt.TextEditorInteraction)
            textItem.setZValue(1000.0)
            textItem.lostFocus.connect(self.editorLostFocus)
            textItem.selectedChange.connect(self.itemSelected)
            self.addItem(textItem)
            textItem.setDefaultTextColor(self.myTextColor)
            textItem.setPos(mouseEvent.scenePos())
            self.textInserted.emit(textItem)

        super(DiagramScene, self).mousePressEvent(mouseEvent)

    def mouseMoveEvent(self, mouseEvent):
        if self.myMode == self.InsertLine and self.line:
            newLine = QLineF(self.line.line().p1(), mouseEvent.scenePos())
            self.line.setLine(newLine)
        elif self.myMode == self.MoveItem:
            super(DiagramScene, self).mouseMoveEvent(mouseEvent)

    def mouseReleaseEvent(self, mouseEvent):
        if self.line and self.myMode == self.InsertLine:
            startItems = self.items(self.line.line().p1())
            if len(startItems) and startItems[0] == self.line:
                startItems.pop(0)
            endItems = self.items(self.line.line().p2())
            if len(endItems) and endItems[0] == self.line:
                endItems.pop(0)

            self.removeItem(self.line)
            self.line = None

            if len(startItems) and len(endItems) and \
                    isinstance(startItems[0], DiagramItem) and \
                    isinstance(endItems[0], DiagramItem) and \
                    startItems[0] != endItems[0]:
                startItem = startItems[0]
                endItem = endItems[0]
                arrow = Arrow(startItem, endItem)
                arrow.setColor(self.myLineColor)
                startItem.addArrow(arrow)
                endItem.addArrow(arrow)
                arrow.setZValue(-1000.0)
                self.addItem(arrow)
                arrow.updatePosition()

        self.line = None
        super(DiagramScene, self).mouseReleaseEvent(mouseEvent)

    def isItemChange(self, type):
        for item in self.selectedItems():
            if isinstance(item, type):
                return True
        return False
class DiagramScene(QGraphicsScene):
    InsertItem, InsertLine, InsertText, MoveItem, DefaultMode = range(5)

    itemInserted = pyqtSignal(int)

    textInserted = pyqtSignal(QGraphicsTextItem)

    itemSelected = pyqtSignal(QGraphicsItem)

    def __init__(self, item_menu, parent=None):
        super(DiagramScene, self).__init__(parent)

        self.my_item_menu = item_menu
        self.my_mode = self.DefaultMode
        self.my_item_type = "channel"
        self.line = None
        self.text_item = None
        self.my_item_color = Qt.white
        self.my_text_color = Qt.black
        self.my_line_color = Qt.black
        self.my_font = QFont()
        self.m_drag_offset = 0
        self.m_dragged = None

    def set_line_color(self, color):
        self.my_line_color = color
        if self.is_item_changed(Arrow):
            item = self.selectedItems()[0]
            item.set_color(self.my_line_color)
            self.update()

    def set_text_color(self, color):
        self.my_text_color = color
        if self.is_item_changed(DiagramTextItem):
            item = self.selectedItems()[0]
            item.setDefaultTextColor(self.my_text_color)

    def set_item_color(self, color):
        self.my_item_color = color
        if self.is_item_changed(DiagramTextItem):
            item = self.selectedItems()[0]
            item.setBrush(self.my_item_color)

    def setFont(self, font):
        self.my_font = font
        if self.is_item_changed(DiagramTextItem):
            item = self.selectedItems()[0]
            item.setFont(self.my_font)

    def set_mode(self, mode):
        self.my_mode = mode

    def set_item_type(self, new_type):
        self.my_item_type = new_type

    def editor_lost_focus(self, item):
        if item:
            cursor = item.text_cursor()
            cursor.clearSelection()
            item.set_text_cursor(cursor)

            if item.to_plain_text():
                self.removeItem(item)
                item.delete_later()

    # noinspection PyArgumentList
    def insert_item(self, item_type=None, x=None, y=None, text=None):
        if not text:
            text, ok = QInputDialog.getText(QInputDialog(), 'Insert name', 'Enter new object name:')
            if not ok:  # TODO
                return
        item = FlumeObject(item_type, text).pictogram
        item.setBrush(self.my_item_color)
        self.addItem(item)
        item.setPos(x, y)
        return item

    def mousePressEvent(self, mouse_event):

        if mouse_event.button() != Qt.LeftButton:
            return
        if self.my_mode == self.InsertItem:
            x = mouse_event.scenePos().x()  # // 50 * 50
            y = mouse_event.scenePos().y()  # // 50 * 50
            item = self.insert_item(self.my_item_type, x, y)
            self.itemInserted.emit(item.flume_component)
        elif self.my_mode == self.InsertLine:
            self.line = QGraphicsLineItem(QLineF(mouse_event.scenePos(),
                                                 mouse_event.scenePos()))
            self.line.setPen(QPen(self.my_line_color, 2))
            self.addItem(self.line)
        elif self.my_mode == self.InsertText:
            text_item = DiagramTextItem()
            text_item.setFont(self.my_font)
            text_item.setTextInteractionFlags(Qt.TextEditorInteraction)
            text_item.setZValue(1000.0)
            text_item.lostFocus.connect(self.editor_lost_focus)
            # text_item.selectedChange.connect(self.itemSelected)
            self.addItem(text_item)
            text_item.setDefaultTextColor(self.my_text_color)
            text_item.setPos(mouse_event.scenePos())
            self.textInserted.emit(text_item)
        else:
            self.m_dragged = QGraphicsScene.itemAt(self, mouse_event.scenePos(), QTransform())
            if self.m_dragged:
                self.my_mode = self.MoveItem
                self.m_drag_offset = mouse_event.scenePos() - self.m_dragged.pos()

        super(DiagramScene, self).mousePressEvent(mouse_event)

    def mouseMoveEvent(self, mouse_event):
        if self.my_mode == self.InsertLine and self.line:
            new_line = QLineF(self.line.line().p1(), mouse_event.scenePos())
            self.line.setLine(new_line)
        elif self.my_mode == self.MoveItem:
            if self.m_dragged:
                self.m_dragged.setPos(mouse_event.scenePos() - self.m_drag_offset)
            super(DiagramScene, self).mouseMoveEvent(mouse_event)

    def mouseReleaseEvent(self, mouse_event):
        if self.line and self.my_mode == self.InsertLine:
            start_items = self.items(self.line.line().p1())
            if len(start_items) and start_items[0] == self.line:
                start_items.pop(0)
            end_items = self.items(self.line.line().p2())
            if len(end_items) and end_items[0] == self.line:
                end_items.pop(0)

            self.removeItem(self.line)
            self.line = None

            if len(start_items) and len(end_items) and isinstance(start_items[0], FlumeDiagramItem) and \
                    isinstance(end_items[0], FlumeDiagramItem) and start_items[0] != end_items[0]:
                start_item = start_items[0]
                end_item = end_items[0]

                self.add_arrow(start_item, end_item)

        self.line = None

        if self.m_dragged:
            x = mouse_event.scenePos().x()  # // 50 * 50
            y = mouse_event.scenePos().y()  # // 50 * 50
            self.m_dragged.setPos(x, y)
            self.m_dragged = None
            self.my_mode = self.DefaultMode

        super(DiagramScene, self).mouseReleaseEvent(mouse_event)

    def add_arrow(self, start_item, end_item):
        arrow = Arrow(start_item, end_item)
        arrow.set_color(self.my_line_color)
        start_item.add_arrow(arrow)
        end_item.add_arrow(arrow)
        arrow.setZValue(-1000.0)
        self.addItem(arrow)
        arrow.update_position()

    def is_item_changed(self, new_type):
        for item in self.selectedItems():
            if isinstance(item, new_type):
                return True
        return False
class BaseGraphic(object):
    def __init__(self, *args):
        super(BaseGraphic, self).__init__()
        self.label = args[3]
        self.parent = args[0]
        self.section_analyzer = self.parent.section_analyzer
        self.width = None
        self.height = None
        self.margin = None
        self.position = args[1], args[2]
        self.content_width = None
        self.content_height = None
        self.distance_pointer = None
        self.distance_label = None
        self.section_num = len(self.section_analyzer.section_list)
        self.section_distance = self.section_analyzer.section_distance
        self.length_multiplier = 100.0
        self.height_multiplier = 100.0
        self.line_extend = 20
        self.margin = 50
        self.material_legend = MaterialLegend(self)
        self._inited = False
        self.items = []
        self.add_title()

    def add_title(self):
        if self.label:
            title = QGraphicsSimpleTextItem(self.label)
            title.setPos(self.position[0] + self.margin, self.position[1] + self.line_extend)
            self.addToGroup(title)

    def addToGroup(self, item):
        self.items.append(item)

    def create_distance_pointer(self):
        self.distance_pointer = QGraphicsLineItem()
        pen = QPen()
        pen.setWidthF(1.0)
        pen.setStyle(Qt.DashDotLine)
        color = Color.create_qcolor_from_rgb_tuple_f((1,0,0))
        pen.setColor(color)
        self.distance_pointer.setPen(pen)
        self.distance_pointer.setZValue(1.0)
        self.addToGroup(self.distance_pointer)
        self.distance_label = QGraphicsSimpleTextItem()
        self.distance_label.setZValue(1.0)
        self.addToGroup(self.distance_label)


    def init_dimension(self):
        section_num = len(self.section_analyzer.section_list)
        section_distance = self.section_analyzer.section_distance
        self.content_width = section_num * section_distance * self.length_multiplier
        self.create_distance_pointer()
        self._inited = True

    def update_graph_size(self):
        if self.content_height and self.content_width:
            self.width = self.content_width + self.margin * 2
            self.height = self.content_height + self.margin * 2
        # bounding_rect.setWidth(self.width)
        # bounding_rect.setHeight(self.height)

    def set_distance_pointer(self, distance):
        if self._inited:
            x1 = self.position[0] + self.margin + distance * self.length_multiplier
            y1 = self.position[1]
            x2 = x1
            y2 = y1 + self.height
            self.distance_pointer.setLine(x1, y1, x2, y2)
            self.distance_label.setText("%.2f" % distance)
            self.distance_label.setPos(x2,y2)
        pass

    @staticmethod
    def set_rect_fill(*args):
        if args[0] == 0: #surface color mode
            rect = args[1]
            color = args[2]
            qcolor = Color.create_qcolor_from_rgb_tuple_f(color)
            brush = QBrush(qcolor)
            rect.setBrush(brush)

    def create_legend(self):
        x = self.position[0] + self.width
        y = self.position[1]
        self.material_legend.create_material_legend(x, y)
        for item in self.material_legend.graphic_items:
            self.addToGroup(item)
Пример #32
0
class ChartView(QChartView):

    def __init__(self, *args, **kwargs):
        super(ChartView, self).__init__(*args, **kwargs)
        self.resize(800, 600)
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self.initChart()

        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)

        # line 宽度需要调整
        self.lineItem = QGraphicsLineItem(self._chart)
        pen = QPen(Qt.gray)
        self.lineItem.setPen(pen)
        self.lineItem.setZValue(998)
        self.lineItem.hide()

        # 一些固定计算,减少mouseMoveEvent中的计算量
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.category_len = len(axisX.categories())
        self.min_x, self.max_x = -0.5, self.category_len - 0.5
        self.min_y, self.max_y = axisY.min(), axisY.max()
        # 坐标系中左上角顶点
        self.point_top = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(pos).x()
        y = self._chart.mapToValue(pos).y()
        index = round(x)
        # 得到在坐标系中的所有bar的类型和点
        serie = self._chart.series()[0]
        bars = [(bar, bar.at(index))
                for bar in serie.barSets() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
        if bars:
            right_top = self._chart.mapToPosition(
                QPointF(self.max_x, self.max_y))
            # 等分距离比例
            step_x = round(
                (right_top.x() - self.point_top.x()) / self.category_len)
            posx = self._chart.mapToPosition(QPointF(x, self.min_y))
            self.lineItem.setLine(posx.x(), self.point_top.y(),
                                  posx.x(), posx.y())
            self.lineItem.show()
            try:
                title = self.categories[index]
            except:
                title = ""
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(
                title, bars, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
            self.lineItem.hide()

    def handleMarkerClicked(self):
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        bar = marker.barset()
        if not bar:
            return
        # bar透明度
        brush = bar.brush()
        color = brush.color()
        alpha = 0.0 if color.alphaF() == 1.0 else 1.0
        color.setAlphaF(alpha)
        brush.setColor(color)
        bar.setBrush(brush)
        # marker
        brush = marker.labelBrush()
        color = brush.color()
        alpha = 0.4 if color.alphaF() == 1.0 else 1.0
        # 设置label的透明度
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setLabelBrush(brush)
        # 设置marker的透明度
        brush = marker.brush()
        color = brush.color()
        color.setAlphaF(alpha)
        brush.setColor(color)
        marker.setBrush(brush)

    def handleMarkerHovered(self, status):
        # 设置bar的画笔宽度
        marker = self.sender()  # 信号发送者
        if not marker:
            return
        bar = marker.barset()
        if not bar:
            return
        pen = bar.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if status else -1))
        bar.setPen(pen)

    def handleBarHoverd(self, status, index):
        # 设置bar的画笔宽度
        bar = self.sender()  # 信号发送者
        pen = bar.pen()
        if not pen:
            return
        pen.setWidth(pen.width() + (1 if status else -1))
        bar.setPen(pen)

    def initChart(self):
        self._chart = QChart(title="柱状图堆叠")
        self._chart.setAcceptHoverEvents(True)
        # Series动画
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self.categories = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
        # names = ["邮件营销", "联盟广告", "视频广告", "直接访问", "搜索引擎"]
        names = ["邮件营销", ]
        series = QBarSeries(self._chart)
        for name in names:
            bar = QBarSet(name)
            # 随机数据
            for _ in range(7):
                bar.append(randint(0, 10))
                
            series.append(bar)
            bar.hovered.connect(self.handleBarHoverd)  # 鼠标悬停
        self._chart.addSeries(series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        # x轴
        axis_x = QBarCategoryAxis(self._chart)
        axis_x.append(self.categories)
        self._chart.setAxisX(axis_x, series)
        # chart的图例
        legend = self._chart.legend()
        legend.setVisible(True)
        # 遍历图例上的标记并绑定信号
        for marker in legend.markers():
            # 点击事件
            marker.clicked.connect(self.handleMarkerClicked)
            # 鼠标悬停事件
            marker.hovered.connect(self.handleMarkerHovered)
        self.setChart(self._chart)
Пример #33
0
class CalendarDesklet(Desklet):

    def __init__(self):
        super().__init__()

        self.model = CalendarModel()

        self.cursor_pos = None

        self.cursor = QGraphicsRectItem(self.root)
        self.header = QGraphicsSimpleTextItem(self.root)

        self.weekdays = []
        days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
        for day in days:
            self.weekdays.append(QGraphicsSimpleTextItem(day, self.root))

        self.header_line = QGraphicsLineItem(self.root)

        self.days = []
        for _ in range(0, 6 * 7):
            self.days.append(QGraphicsSimpleTextItem(self.root))

    def next_month(self):
        self.model.next_month()
        self.layout()

    def previous_month(self):
        self.model.previous_month()
        self.layout()

    def set_rect(self, rect):
        super().set_rect(rect)
        self.layout()

    def set_style(self, style):
        super().set_style(style)

        font = QFont(style.font)
        font.setPixelSize(48)
        self.header.setBrush(style.midcolor)
        self.header.setFont(font)

        font = QFont(style.font)
        font.setPixelSize(32)

        self.header_line.setPen(style.foreground_color)

        self.cursor.setBrush(style.midcolor)
        self.cursor.setPen(QPen(Qt.NoPen))

        for widget in self.weekdays:
            widget.setFont(font)
            widget.setBrush(style.foreground_color)

        for widget in self.days:
            widget.setFont(font)
            widget.setBrush(self.style.foreground_color)

        self.layout()

    def layout(self):
        cell_width = (self.rect.width()) / 7.0
        cell_height = (self.rect.height() - 64) / 7.0

        x = self.rect.left()
        y = self.rect.top()

        fm = QFontMetrics(self.header.font())
        rect = fm.boundingRect(self.header.text())
        self.header.setPos(x + self.rect.width() / 2 - rect.width() / 2,
                           y)

        y += fm.height()

        for row, day in enumerate(self.weekdays):
            fm = QFontMetrics(day.font())
            rect = fm.boundingRect(day.text())
            day.setPos(x + row * cell_width + cell_width / 2 - rect.width() / 2,
                       y)

        y += fm.height()
        self.header_line.setLine(x, y,
                                 x + self.rect.width() - 3, y)

        y += 8

        for n, widget in enumerate(self.days):
            col = n % 7
            row = n // 7

            rect = fm.boundingRect(widget.text())
            widget.setPos(x + col * cell_width + cell_width / 2 - rect.width() / 2,
                          y + row * cell_height + cell_height / 2 - fm.height() / 2)

            # if day.month != self.now.month:
            #    widget.setBrush(self.style.midcolor)
            # else:

        if self.cursor_pos is not None:
            self.cursor.setRect(x + self.cursor_pos[0] * cell_width,
                                y + self.cursor_pos[1] * cell_height,
                                cell_width,
                                cell_height)
            self.cursor.show()
        else:
            self.cursor.hide()

    def update(self, now):
        self.model.update(now)

        # update header
        self.header.setText(
            date(self.model.year, self.model.month, 1).strftime("%B %Y"))

        # calculate the date of the top/left calendar entry
        current_date = date(self.model.year, self.model.month, 1)
        current_date = current_date - timedelta(current_date.weekday())

        self.cursor_pos = None
        for n, widget in enumerate(self.days):
            col = n % 7
            row = n // 7

            if current_date == self.model.today:
                self.cursor_pos = (col, row)

            widget.setText("%d" % current_date.day)
            self.days[n] = widget
            current_date += timedelta(days=1)

        self.layout()
Пример #34
0
class PreXoverItemGroup(QGraphicsEllipseItem):
    """Summary

    Attributes:
        active_wedge_gizmo (TYPE): Description
        fwd_prexover_items (dict): Description
        HUE_FACTOR (float): Description
        id_num (TYPE): Description
        is_active (TYPE): Description
        model_part (Part): The model part
        rev_prexover_items (dict): Description
        SPIRAL_FACTOR (float): Description
        virtual_helix_item (VirtualHelixItem): Description
    """
    HUE_FACTOR = 1.6
    SPIRAL_FACTOR = 0.4

    def __init__(self, radius, rect, virtual_helix_item, is_active):
        """Summary

        Args:
            radius (TYPE): Description
            rect (TYPE): Description
            virtual_helix_item (VirtualHelixItem): Description
            is_active (TYPE): Description
        """
        super(PreXoverItemGroup, self).__init__(rect, virtual_helix_item)

        self._radius = radius
        self._rect = rect
        self.virtual_helix_item = virtual_helix_item
        self.model_part = virtual_helix_item.part()
        self.id_num = virtual_helix_item.idNum()
        self.is_active = is_active
        self.active_wedge_gizmo = WedgeGizmo(radius, rect, self)
        self.fwd_prexover_items = fwd_pxis = {}
        self.rev_prexover_items = rev_pxis = {}
        self._colors = self._getColors()
        self.addItems()
        self.setPen(getNoPen())
        z = styles.ZPXIGROUP + 10 if is_active else styles.ZPXIGROUP
        self.setZValue(z)
        self.setTransformOriginPoint(rect.center())

        bpr, tpr, eulerZ = virtual_helix_item.getProperty(
            ['bases_per_repeat', 'turns_per_repeat', 'eulerZ'])

        self.setRotation(-eulerZ)  # add 180

        # for baseNearestPoint
        fwd_pos, rev_pos = [], []
        step_size = self.virtual_helix_item.getProperty('bases_per_repeat')
        for i in range(int(step_size)):
            fwd_pos.append(
                (fwd_pxis[i].scenePos().x(), fwd_pxis[i].scenePos().y()))
            rev_pos.append(
                (rev_pxis[i].scenePos().x(), rev_pxis[i].scenePos().y()))
        self.fwd_pos_array = np.asarray(fwd_pos)
        self.rev_pos_array = np.asarray(rev_pos)
        self.baseNearLine = QGraphicsLineItem(self)
        self.baseNearLine.setPen(
            getPenObj("#000000", 0.25, capstyle=Qt.RoundCap))

    # end def

    # def mousePressEvent(self, event):
    #     print("PreXoverGroup press")

    # def mouseMoveEvent(self, event):
    #     print("PreXoverGroup move")

    # def mouseReleaseEvent(self, event):
    #     print("PreXoverGroup release")

    ### ACCESSORS ###
    def partItem(self):
        """Summary

        Returns:
            TYPE: Description
        """
        return self.virtual_helix_item.partItem()

    # end def

    def getItem(self, is_fwd, step_idx):
        """Summary

        Args:
            is_fwd (TYPE): Description
            step_idx (int): the base index within the virtual helix

        Returns:
            TYPE: Description
        """
        items = self.fwd_prexover_items if is_fwd else self.rev_prexover_items
        if step_idx in items:
            return items[step_idx]
        else:
            return None

    # end def

    def getItemIdx(self, is_fwd, idx):
        """Summary

        Args:
            is_fwd (TYPE): Description
            idx (int): the base index within the virtual helix

        Returns:
            TYPE: Description
        """
        step_size = self.virtual_helix_item.getProperty('bases_per_repeat')
        return self.getItem(is_fwd, idx % step_size)

    # end def

    ### EVENT HANDLERS ###

    ### PRIVATE SUPPORT METHODS ###
    def _getColors(self):
        """Summary

        Returns:
            TYPE: Description
        """
        step_size = int(
            self.virtual_helix_item.getProperty('bases_per_repeat'))
        hue_scale = step_size * self.HUE_FACTOR
        return [
            QColor.fromHsvF(i / hue_scale, 0.75, 0.8).name()
            for i in range(step_size)
        ]

    # end def

    ### PUBLIC SUPPORT METHODS ###
    def addItems(self):
        """Summary
        """
        radius = self._radius
        step_size, bases_per_turn, tpb, mgroove = self.virtual_helix_item.getAngularProperties(
        )
        # print("TPB", tpb, step_size)
        iw = PXI_PP_ITEM_WIDTH
        spiral_factor = self.SPIRAL_FACTOR
        colors = self._colors
        ctr = self.mapToParent(self._rect).boundingRect().center()
        x = ctr.x() + radius - PXI_PP_ITEM_WIDTH
        y = ctr.y()
        # tpb = -tpb
        # Qt +angle is Clockwise (CW), model +angle is CCW
        mgroove = -mgroove
        fwd_pxis = self.fwd_prexover_items
        rev_pxis = self.rev_prexover_items
        for i in range(int(step_size)):
            inset = i * spiral_factor  # spiral layout
            fwd = PreXoverItem(i, tpb, step_size, colors[i], self, is_fwd=True)
            rev = PreXoverItem(i,
                               tpb,
                               step_size,
                               colors[-1 - i],
                               self,
                               is_fwd=False)
            fwd.setPos(x - inset, y)
            rev.setPos(x - inset, y)
            fwd.setTransformOriginPoint((-radius + iw + inset), 0)
            rev.setTransformOriginPoint((-radius + iw + inset), 0)
            fwd.setRotation(round(i * tpb % 360, 3))
            rev.setRotation(round((i * tpb + mgroove) % 360, 3))
            fwd.setBondLineLength(inset + iw)
            rev.setBondLineLength(inset + iw)
            fwd_pxis[i] = fwd
            rev_pxis[i] = rev

        for i in range(int(step_size) - 1):
            fwd, next_fwd = fwd_pxis[i], fwd_pxis[i + 1]
            j = (step_size - 1) - i
            rev, next_rev = rev_pxis[j], rev_pxis[j - 1]
            fwd.set3pItem(next_fwd)
            rev.set3pItem(next_rev)
            next_fwd.set5pItem(fwd)
            next_rev.set5pItem(rev)

    # end def

    def baseNearestPoint(self, is_fwd, scene_pos):
        """Summary

        Args:
            is_fwd (bool): used to check fwd or rev lists.
            scene_pos (QPointF): scene coordinate position

        Returns:
            PreXoverItem: base nearest to position
        """
        pos_array = self.fwd_pos_array if is_fwd else self.rev_pos_array
        dist_2 = np.sum((pos_array - (scene_pos.x(), scene_pos.y()))**2,
                        axis=1)
        near_idx = np.argmin(dist_2)
        near_pxi = self.fwd_prexover_items[
            near_idx] if is_fwd else self.rev_prexover_items[near_idx]
        # Draw a line
        p1 = self.mapFromScene(scene_pos.x(), scene_pos.y())
        p2 = self.mapFromScene(near_pxi.scenePos())
        line = QLineF(p1, p2)
        self.baseNearLine.setLine(line)

    def destroyItem(self):
        """Summary
        """
        fpxis = self.fwd_prexover_items
        rpxis = self.rev_prexover_items
        scene = self.scene()
        for i in range(len(fpxis)):
            x = fpxis.pop(i)
            x.destroyItem()
            x = rpxis.pop(i)
            x.destroyItem()
        self.virtual_helix_item = None
        self.model_part = None
        scene.removeItem(self.active_wedge_gizmo)
        self.active_wedge_gizmo = None
        scene.removeItem(self)

    # end def

    def updateTurnsPerRepeat(self):
        """Summary
        """
        step_size, bases_per_turn, tpb, mgroove = self.virtual_helix_item.getAngularProperties(
        )
        mgroove = -mgroove
        fpxis = self.fwd_prexover_items
        rpxis = self.rev_prexover_items
        for i in range(int(step_size)):
            fwd = self.fwd_prexover_items[i]
            rev = self.rev_prexover_items[i]
            fwd.setRotation(round((i * tpb) % 360, 3))
            rev.setRotation(round((i * tpb + mgroove) % 360, 3))
        for i in range(int(step_size) - 1):
            fwd, next_fwd = fpxis[i], fpxis[i + 1]
            j = (step_size - 1) - i
            rev, next_rev = rpxis[j], rpxis[j - 1]
            fwd.set3pItem(next_fwd)
            rev.set3pItem(next_rev)
            next_fwd.set5pItem(fwd)
            next_rev.set5pItem(rev)

    # end def

    def partCrossoverSpanAngle(self):
        """
        Returns:
            int: Crossover span angle from Part.
        """
        return self.virtual_helix_item.partCrossoverSpanAngle()

    def updateModelActiveBaseInfo(self, pre_xover_info):
        """Notify model of pre_xover_item hover state.
        """
        self.model_part.setActiveBaseInfo(pre_xover_info)
Пример #35
0
class ImageViewer(QGraphicsView, QObject):
    def __init__(self, parent=None):
        super(ImageViewer, self).__init__(parent)
        self.setRenderHints(QPainter.Antialiasing
                            | QPainter.SmoothPixmapTransform)
        #self.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setDragMode(QGraphicsView.ScrollHandDrag)
        #self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        #self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        self._scene = ImageViewerScene(self)
        self.setScene(self._scene)
        self._create_grid()
        self._create_grid_lines()
        self._pixmap = None
        self._selection_mode = SELECTION_MODE.NONE

        # polygon selection
        _polygon_guide_line_pen = QPen(QtGui.QColor(235, 72, 40))
        _polygon_guide_line_pen.setWidth(2)
        _polygon_guide_line_pen.setStyle(QtCore.Qt.DotLine)
        self._polygon_guide_line = QGraphicsLineItem()
        self._polygon_guide_line.setVisible(False)
        self._polygon_guide_line.setPen(_polygon_guide_line_pen)
        self._scene.addItem(self._polygon_guide_line)
        self._current_polygon = None
        # rectangle selection
        self._box_origin = QPoint()
        self._box_picker = QRubberBand(QRubberBand.Rectangle, self)

        # free selection
        self._current_free_path = None
        self._is_drawing = False
        self._last_point_drawn = QPoint()
        self._current_label = None

    @property
    def current_label(self):
        return self._current_label

    @current_label.setter
    def current_label(self, value):
        self._current_label = value

    @property
    def pixmap(self) -> ImagePixmap:
        return self._pixmap

    @pixmap.setter
    def pixmap(self, value: QPixmap):
        self.selection_mode = SELECTION_MODE.NONE
        self.resetTransform()
        if self.pixmap:
            self._scene.removeItem(self._pixmap)
        self.remove_annotations()
        self._pixmap = ImagePixmap()
        self._pixmap.setPixmap(value)
        self._pixmap.setOffset(-value.width() / 2, -value.height() / 2)
        self._pixmap.setTransformationMode(QtCore.Qt.SmoothTransformation)
        self._pixmap.signals.hoverEnterEventSgn.connect(
            self.pixmap_hoverEnterEvent_slot)
        self._pixmap.signals.hoverLeaveEventSgn.connect(
            self.pixmap_hoverLeaveEvent_slot)
        self._pixmap.signals.hoverMoveEventSgn.connect(
            self.pixmap_hoverMoveEvent_slot)
        self._scene.addItem(self._pixmap)
        # rect=self._scene.addRect(QtCore.QRectF(0,0,100,100), QtGui.QPen(QtGui.QColor("red")))
        # rect.setZValue(1.0)
        self.fit_to_window()

    @property
    def selection_mode(self):
        return self._selection_mode

    @selection_mode.setter
    def selection_mode(self, value):
        self._polygon_guide_line.hide()
        self._current_polygon = None
        self._current_free_path = None
        self._is_drawing = value == SELECTION_MODE.FREE
        if value == SELECTION_MODE.NONE:
            self.enable_items(True)
        else:
            self.enable_items(False)
        self._selection_mode = value

    def remove_annotations(self):
        for item in self._scene.items():
            if isinstance(item, EditableBox):
                self._scene.removeItem(item)
            elif isinstance(item, EditablePolygon):
                item.delete_polygon()

    def remove_annotations_by_label(self, label_name):
        for item in self._scene.items():
            if isinstance(item, EditableBox):
                if item.label and item.label.name == label_name:
                    self._scene.removeItem(item)
            elif isinstance(item, EditablePolygon):
                if item.label and item.label.name == label_name:
                    item.delete_polygon()

    def enable_items(self, value):
        for item in self._scene.items():
            if isinstance(item, EditablePolygon) or isinstance(
                    item, EditableBox):
                item.setEnabled(value)

    def _create_grid(self):
        gridSize = 15
        backgroundPixmap = QtGui.QPixmap(gridSize * 2, gridSize * 2)
        #backgroundPixmap.fill(QtGui.QColor("white"))
        backgroundPixmap.fill(QtGui.QColor(20, 20, 20))
        #backgroundPixmap.fill(QtGui.QColor("powderblue"))
        painter = QtGui.QPainter(backgroundPixmap)
        #backgroundColor=QtGui.QColor("palegoldenrod")
        #backgroundColor=QtGui.QColor(237,237,237)
        backgroundColor = QtGui.QColor(0, 0, 0)
        painter.fillRect(0, 0, gridSize, gridSize, backgroundColor)
        painter.fillRect(gridSize, gridSize, gridSize, gridSize,
                         backgroundColor)
        painter.end()
        self._scene.setBackgroundBrush(QtGui.QBrush(backgroundPixmap))

    def _create_grid_lines(self):
        pen_color = QColor(255, 255, 255, 255)
        pen = QPen(pen_color)
        pen.setWidth(2)
        pen.setStyle(QtCore.Qt.DotLine)
        self.vline = QGraphicsLineItem()
        self.vline.setVisible(False)
        self.vline.setPen(pen)
        self.hline = QGraphicsLineItem()
        self.hline.setVisible(False)
        self.hline.setPen(pen)
        self._scene.addItem(self.vline)
        self._scene.addItem(self.hline)

    def wheelEvent(self, event: QWheelEvent):
        adj = (event.angleDelta().y() / 120) * 0.1
        self.scale(1 + adj, 1 + adj)

    def fit_to_window(self):
        """Fit image within view."""
        if not self.pixmap or not self._pixmap.pixmap():
            return
        #self._pixmap.setTransformationMode(QtCore.Qt.SmoothTransformation)
        self.fitInView(self._pixmap, QtCore.Qt.KeepAspectRatio)

    def show_guide_lines(self):
        if self.hline and self.vline:
            self.hline.show()
            self.vline.show()

    def hide_guide_lines(self):
        if self.hline and self.vline:
            self.hline.hide()
            self.vline.hide()

    def pixmap_hoverEnterEvent_slot(self):
        self.show_guide_lines()

    def pixmap_hoverLeaveEvent_slot(self):
        self.hide_guide_lines()

    def pixmap_hoverMoveEvent_slot(self, evt: QGraphicsSceneHoverEvent, x, y):
        bbox: QRect = self._pixmap.boundingRect()
        offset = QPointF(bbox.width() / 2, bbox.height() / 2)
        self.vline.setLine(x, -offset.y(), x, bbox.height() - offset.y())
        self.vline.setZValue(1)
        self.hline.setLine(-offset.x(), y, bbox.width() - offset.x(), y)
        self.hline.setZValue(1)

    def mouseMoveEvent(self, evt: QtGui.QMouseEvent) -> None:
        if self.selection_mode == SELECTION_MODE.BOX:
            if not self._box_origin.isNull():
                self._box_picker.setGeometry(
                    QRect(self._box_origin, evt.pos()).normalized())
        elif self.selection_mode == SELECTION_MODE.POLYGON:
            if self._current_polygon:
                if self._current_polygon.count > 0:
                    last_point: QPointF = self._current_polygon.last_point
                    self._polygon_guide_line.setZValue(1)
                    self._polygon_guide_line.show()
                    mouse_pos = self.mapToScene(evt.pos())
                    self._polygon_guide_line.setLine(last_point.x(),
                                                     last_point.y(),
                                                     mouse_pos.x(),
                                                     mouse_pos.y())
            else:
                self._polygon_guide_line.hide()

        elif self.selection_mode == SELECTION_MODE.FREE and evt.buttons(
        ) and QtCore.Qt.LeftButton:
            if self._current_free_path:
                painter: QPainterPath = self._current_free_path.path()
                self._last_point_drawn = self.mapToScene(evt.pos())
                painter.lineTo(self._last_point_drawn)
                self._current_free_path.setPath(painter)

        super(ImageViewer, self).mouseMoveEvent(evt)

    def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None:

        if evt.buttons() == QtCore.Qt.LeftButton:
            if self.selection_mode == SELECTION_MODE.BOX:
                self.setDragMode(QGraphicsView.NoDrag)
                self._box_origin = evt.pos()
                self._box_picker.setGeometry(QRect(self._box_origin, QSize()))
                self._box_picker.show()

            elif self._selection_mode == SELECTION_MODE.POLYGON:
                pixmap_rect: QRectF = self._pixmap.boundingRect()
                new_point = self.mapToScene(evt.pos())
                # consider only the points intothe image
                if pixmap_rect.contains(new_point):
                    if self._current_polygon is None:
                        self._current_polygon = EditablePolygon()
                        self._current_polygon.signals.deleted.connect(
                            self.delete_polygon_slot)
                        self._scene.addItem(self._current_polygon)
                        self._current_polygon.addPoint(new_point)
                    else:
                        self._current_polygon.addPoint(new_point)

            elif self._selection_mode == SELECTION_MODE.FREE:
                # start drawing
                new_point = self.mapToScene(evt.pos())
                pixmap_rect: QRectF = self._pixmap.boundingRect()
                # consider only the points intothe image
                if pixmap_rect.contains(new_point):
                    self.setDragMode(QGraphicsView.NoDrag)
                    pen = QPen(QtGui.QColor(235, 72, 40))
                    pen.setWidth(10)
                    self._last_point_drawn = new_point
                    self._current_free_path = QGraphicsPathItem()
                    self._current_free_path.setOpacity(0.6)
                    self._current_free_path.setPen(pen)
                    painter = QPainterPath()
                    painter.moveTo(self._last_point_drawn)
                    self._current_free_path.setPath(painter)
                    self._scene.addItem(self._current_free_path)
        else:
            self.setDragMode(QGraphicsView.ScrollHandDrag)

        super(ImageViewer, self).mousePressEvent(evt)

    def mouseReleaseEvent(self, evt: QtGui.QMouseEvent) -> None:
        if evt.button() == QtCore.Qt.LeftButton:
            if self.selection_mode == SELECTION_MODE.BOX:
                roi: QRect = self._box_picker.geometry()
                roi: QRectF = self.mapToScene(roi).boundingRect()
                pixmap_rect = self._pixmap.boundingRect()
                self._box_picker.hide()
                if pixmap_rect == roi.united(pixmap_rect):
                    rect = EditableBox(roi)
                    rect.label = self.current_label
                    self._scene.addItem(rect)
                    self.selection_mode = SELECTION_MODE.NONE
                    self.setDragMode(QGraphicsView.ScrollHandDrag)

            elif self.selection_mode == SELECTION_MODE.FREE and self._current_free_path:
                # create polygon
                self._current_free_path: QGraphicsPathItem
                path_rect = self._current_free_path.boundingRect()
                pixmap_rect = self._pixmap.boundingRect()
                if pixmap_rect == path_rect.united(pixmap_rect):
                    path = self._current_free_path.path()
                    path_polygon = EditablePolygon()
                    path_polygon.label = self.current_label
                    self._scene.addItem(path_polygon)
                    for i in range(0, path.elementCount(), 10):
                        x, y = path.elementAt(i).x, path.elementAt(i).y
                        path_polygon.addPoint(QPointF(x, y))
                self._scene.removeItem(self._current_free_path)
                self.selection_mode = SELECTION_MODE.NONE
                self.setDragMode(QGraphicsView.ScrollHandDrag)

        super(ImageViewer, self).mouseReleaseEvent(evt)

    def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
        if self._current_polygon and event.key() == QtCore.Qt.Key_Space:
            points = self._current_polygon.points
            self._current_polygon.label = self.current_label
            self._current_polygon = None
            self.selection_mode = SELECTION_MODE.NONE
            self._polygon_guide_line.hide()
            self.setDragMode(QGraphicsView.ScrollHandDrag)
        super(ImageViewer, self).keyPressEvent(event)

    def delete_polygon_slot(self, polygon: EditablePolygon):
        self._current_polygon = None
        self.selection_mode = SELECTION_MODE.NONE
        self._polygon_guide_line.hide()
Пример #36
0
class DiagramScene(QGraphicsScene):
    InsertItem, InsertLine, InsertText, MoveItem  = range(4)

    itemInserted = pyqtSignal(DiagramItem)

    textInserted = pyqtSignal(QGraphicsTextItem)

    itemSelected = pyqtSignal(QGraphicsItem)

    def __init__(self, itemMenu, parent=None):
        super(DiagramScene, self).__init__(parent)

        self.myItemMenu = itemMenu
        self.myMode = self.MoveItem
        self.myItemType = DiagramItem.Step
        self.line = None
        self.textItem = None
        self.myItemColor = Qt.white
        self.myTextColor = Qt.black
        self.myLineColor = Qt.black
        self.myFont = QFont()

    def setLineColor(self, color):
        self.myLineColor = color
        if self.isItemChange(Arrow):
            item = self.selectedItems()[0]
            item.setColor(self.myLineColor)
            self.update()

    def setTextColor(self, color):
        self.myTextColor = color
        if self.isItemChange(DiagramTextItem):
            item = self.selectedItems()[0]
            item.setDefaultTextColor(self.myTextColor)

    def setItemColor(self, color):
        self.myItemColor = color
        if self.isItemChange(DiagramItem):
            item = self.selectedItems()[0]
            item.setBrush(self.myItemColor)

    def setFont(self, font):
        self.myFont = font
        if self.isItemChange(DiagramTextItem):
            item = self.selectedItems()[0]
            item.setFont(self.myFont)

    def setMode(self, mode):
        self.myMode = mode

    def setItemType(self, type):
        self.myItemType = type

    def editorLostFocus(self, item):
        cursor = item.textCursor()
        cursor.clearSelection()
        item.setTextCursor(cursor)

        if item.toPlainText()=='':
            self.removeItem(item)
            item.deleteLater()

    def mousePressEvent(self, mouseEvent):
        if (mouseEvent.button() != Qt.LeftButton):
            return

        if self.myMode == self.InsertItem:
            item = DiagramItem(self.myItemType, self.myItemMenu)
            item.setBrush(self.myItemColor)
            self.addItem(item)
            item.setPos(mouseEvent.scenePos())
            self.itemInserted.emit(item)
        elif self.myMode == self.InsertLine:
            self.line = QGraphicsLineItem(QLineF(mouseEvent.scenePos(),
                    mouseEvent.scenePos()))
            self.line.setPen(QPen(self.myLineColor, 2))
            self.addItem(self.line)
        elif self.myMode == self.InsertText:
            textItem = DiagramTextItem()
            textItem.setFont(self.myFont)
            textItem.setTextInteractionFlags(Qt.TextEditorInteraction)
            textItem.setZValue(1000.0)
            textItem.lostFocus.connect(self.editorLostFocus)
            textItem.selectedChange.connect(self.itemSelected)
            self.addItem(textItem)
            textItem.setDefaultTextColor(self.myTextColor)
            textItem.setPos(mouseEvent.scenePos())
            self.textInserted.emit(textItem)

        super(DiagramScene, self).mousePressEvent(mouseEvent)

    def mouseMoveEvent(self, mouseEvent):
        if self.myMode == self.InsertLine and self.line:
            newLine = QLineF(self.line.line().p1(), mouseEvent.scenePos())
            self.line.setLine(newLine)
        elif self.myMode == self.MoveItem:
            super(DiagramScene, self).mouseMoveEvent(mouseEvent)

    def mouseReleaseEvent(self, mouseEvent):
        if self.line and self.myMode == self.InsertLine:
            startItems = self.items(self.line.line().p1())
            if len(startItems) and startItems[0] == self.line:
                startItems.pop(0)
            endItems = self.items(self.line.line().p2())
            if len(endItems) and endItems[0] == self.line:
                endItems.pop(0)

            self.removeItem(self.line)
            self.line = None

            if len(startItems) and len(endItems) and \
                    isinstance(startItems[0], DiagramItem) and \
                    isinstance(endItems[0], DiagramItem) and \
                    startItems[0] != endItems[0]:
                startItem = startItems[0]
                endItem = endItems[0]
                arrow = Arrow(startItem, endItem)
                arrow.setColor(self.myLineColor)
                startItem.addArrow(arrow)
                endItem.addArrow(arrow)
                arrow.setZValue(-1000.0)
                self.addItem(arrow)
                arrow.updatePosition()

        self.line = None
        super(DiagramScene, self).mouseReleaseEvent(mouseEvent)

    def isItemChange(self, type):
        for item in self.selectedItems():
            if isinstance(item, type):
                return True
        return False
Пример #37
0
class StickWidget(QGraphicsObject):

    font: QFont = QFont("monospace", 32)

    delete_clicked = pyqtSignal(Stick)
    link_initiated = pyqtSignal('PyQt_PyObject') # Actually StickWidget
    link_accepted = pyqtSignal('PyQt_PyObject')
    hovered = pyqtSignal(['PyQt_PyObject', 'PyQt_PyObject'])
    stick_changed = pyqtSignal('PyQt_PyObject')
    sibling_changed = pyqtSignal(bool)
    right_clicked = pyqtSignal('PyQt_PyObject')

    handle_idle_brush = QBrush(QColor(0, 125, 125, 50))
    handle_hover_brush = QBrush(QColor(125, 125, 0, 50))
    handle_press_brush = QBrush(QColor(200, 200, 0, 0))
    handle_idle_pen = QPen(QColor(0, 0, 0, 255))
    handle_press_pen = QPen(QColor(200, 200, 0, 255))
    handle_size = 20

    normal_color = QColor(0, 200, 120)
    negative_color = QColor(200, 0, 0)
    positive_color = QColor(0, 200, 0)

    mismatched = pyqtSignal('PyQt_PyObject')
    misplaced = pyqtSignal('PyQt_PyObject')
    measurement_corrected = pyqtSignal('PyQt_PyObject')
    clearly_visible = pyqtSignal('PyQt_PyObject')
    zero_clicked = pyqtSignal('PyQt_PyObject')

    def __init__(self, stick: Stick, camera: Camera, parent: Optional[QGraphicsItem] = None):
        QGraphicsObject.__init__(self, parent)
        self.camera = camera
        self.stick = stick
        self.line = QLineF()
        self.gline = QGraphicsLineItem(self.line)

        self.stick_label_text = QGraphicsSimpleTextItem("0", self)
        self.stick_label_text.setFont(StickWidget.font)
        self.stick_label_text.setPos(self.line.p1() - QPoint(0, 24))
        self.stick_label_text.setBrush(QBrush(QColor(0, 255, 0)))
        self.stick_label_text.hide()
        self.setZValue(10)

        self.mode = StickMode.Display

        self.btn_delete = Button("delete", "x", parent=self)
        self.btn_delete.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
        self.btn_delete.set_base_color([ButtonColor.RED])
        self.btn_delete.setVisible(False)
        btn_size = max(int(np.linalg.norm(self.stick.top - self.stick.bottom) / 5.0), 15)
        self.btn_delete.set_height(12)
        self.btn_delete.clicked.connect(self.handle_btn_delete_clicked)
        self.btn_delete.setPos(self.line.p1() - QPointF(0.5 * self.btn_delete.boundingRect().width(), 1.1 * self.btn_delete.boundingRect().height()))
        self.btn_delete.set_opacity(0.7)

        self.top_handle = QGraphicsEllipseItem(0, 0, self.handle_size, self.handle_size, self)
        self.mid_handle = QGraphicsEllipseItem(0, 0, self.handle_size, self.handle_size, self)
        self.bottom_handle = QGraphicsEllipseItem(0, 0, self.handle_size, self.handle_size, self)

        self.top_handle.setAcceptedMouseButtons(Qt.NoButton)
        self.mid_handle.setAcceptedMouseButtons(Qt.NoButton)
        self.bottom_handle.setAcceptedMouseButtons(Qt.NoButton)
        self.top_handle.setBrush(self.handle_idle_brush)
        self.top_handle.setPen(self.handle_idle_pen)
        self.mid_handle.setBrush(self.handle_idle_brush)
        self.mid_handle.setPen(self.handle_idle_pen)
        self.bottom_handle.setBrush(self.handle_idle_brush)
        self.bottom_handle.setPen(self.handle_idle_pen)

        self.hovered_handle: Optional[QGraphicsRectItem] = None
        self.handles = [self.top_handle, self.mid_handle, self.bottom_handle]

        self.link_button = Button("link", "Link to...", parent=self)
        self.link_button.set_base_color([ButtonColor.GREEN])
        self.link_button.set_height(12)
        self.link_button.set_label("Link", direction="vertical")
        self.link_button.fit_to_contents()
        self.link_button.clicked.connect(lambda: self.link_initiated.emit(self))
        self.link_button.setVisible(False)
        self.link_button.setFlag(QGraphicsObject.ItemIgnoresTransformations, False)

        self.adjust_line()

        self.setAcceptHoverEvents(True)
        self.top_handle.setZValue(4)
        self.bottom_handle.setZValue(4)
        self.mid_handle.setZValue(4)

        self.top_handle.hide()
        self.mid_handle.hide()
        self.bottom_handle.hide()

        self.handle_mouse_offset = QPointF(0, 0)
        self.available_for_linking = False
        self.link_source = False
        self.current_highlight_color: QColor = StickWidget.normal_color
        self.highlighted = False
        self.frame_color: Optional[None] = self.normal_color
        self.is_linked = False

        self.is_master = True
        self.selected = False

        self.measured_height: int = -1
        self.current_color = self.normal_color

        self.show_label = False
        self.highlight_animation = QPropertyAnimation(self, b"highlight_color")
        self.highlight_animation.valueChanged.connect(self.handle_highlight_animation_value_changed)
        self.deleting = False
        self.update_tooltip()
        self.show_measurements: bool = False
        self.proposed_snow_height: int = -1

        self.zero_btn = Button("zero_btn", "0", parent=self)
        self.zero_btn.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
        self.zero_btn.setVisible(False)
        self.zero_btn.setPos(self.boundingRect().center() + QPointF(self.zero_btn.boundingRect().width() * -0.5,
                                                                    self.boundingRect().height() * 0.5))
        self.zero_btn.clicked.connect(self.handle_zero)

    @pyqtSlot()
    def handle_btn_delete_clicked(self):
        self.delete_clicked.emit(self.stick)

    def prepare_for_deleting(self):
        self.deleting = True
        self.highlight_animation.stop()
        self.btn_delete.setParentItem(None)
        self.scene().removeItem(self.btn_delete)
        self.btn_delete.deleteLater()

    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem,
              widget: Optional[PyQt5.QtWidgets.QWidget] = ...):
        painter.setPen(QPen(self.current_color, 1.0))

        brush = QBrush(self.current_highlight_color)
        pen = QPen(brush, 4)
        painter.setPen(pen)
        if self.highlighted:
            painter.fillRect(self.boundingRect(), QBrush(self.current_highlight_color))

        if self.frame_color is not None and self.mode != StickMode.Edit and self.mode != StickMode.EditDelete:
            painter.setPen(QPen(self.frame_color, 4))
            painter.drawRect(self.boundingRect())

        pen = QPen(QColor(0, 255, 0, 255))

        pen.setWidth(1.0)
        pen.setColor(QColor(255, 0, 255, 255))
        pen.setStyle(Qt.DotLine)
        painter.setPen(pen)
        off = 10
        painter.drawLine(self.line.p1() - QPointF(0, off), self.line.p1() + QPointF(0, off))
        painter.drawLine(self.line.p1() - QPointF(off, 0), self.line.p1() + QPointF(off, 0))
        painter.drawLine(self.line.p2() - QPointF(0, off), self.line.p2() + QPointF(0, off))
        painter.drawLine(self.line.p2() - QPointF(off, 0), self.line.p2() + QPointF(off, 0))
        pen.setStyle(Qt.SolidLine)
        pen.setColor(QColor(0, 255, 0, 255))
        painter.setPen(pen)

        if self.mode != StickMode.EditDelete:
            pen.setWidth(2.0)
            br = painter.brush()
            painter.setPen(pen)
            painter.drawEllipse(self.line.p1(), 10, 10)
            painter.drawEllipse(self.line.p2(), 10, 10)
            painter.setBrush(br)

            if self.mode == StickMode.Measurement and self.proposed_snow_height >= 0:
                point = QPointF(self.boundingRect().x(), -self.proposed_snow_height + self.line.p2().y())
                pen = QPen(QColor(200, 100, 0, 255), 3.0)
                painter.setPen(pen)
                painter.drawLine(point,
                                 point + QPointF(self.boundingRect().width(), 0.0))

            if self.measured_height >= 0:
                vec = (self.stick.top - self.stick.bottom) / np.linalg.norm(self.stick.top - self.stick.bottom)
                dist_along_stick = self.measured_height / np.dot(np.array([0.0, -1.0]), vec)
                point = self.line.p2() + dist_along_stick * QPointF(vec[0], vec[1])
                point = QPointF(self.boundingRect().x(), point.y())
                pen = QPen(QColor(0, 100, 200, 255), 3.0)
                painter.setPen(pen)
                painter.drawLine(point,
                                 point + QPointF(self.boundingRect().width(), 0.0))
        else:
            painter.drawLine(self.line.p1(), self.line.p2())

        if self.selected:
            pen.setColor(QColor(255, 125, 0, 255))
            pen.setStyle(Qt.DashLine)
            painter.setPen(pen)
            painter.drawRect(self.boundingRect().marginsAdded(QMarginsF(5, 5, 5, 5)))

        if self.show_measurements:
            painter.fillRect(self.stick_label_text.boundingRect().translated(self.stick_label_text.pos()),
                             QBrush(QColor(0, 0, 0, 120)))

    def boundingRect(self) -> PyQt5.QtCore.QRectF:
        return self.gline.boundingRect().united(self.top_handle.boundingRect()).\
            united(self.mid_handle.boundingRect()).united(self.bottom_handle.boundingRect())
    
    def set_edit_mode(self, value: bool):
        if value:
            self.set_mode(StickMode.EditDelete)
        else:
            self.set_mode(StickMode.Display)
    
    def set_mode(self, mode: StickMode):
        if mode == StickMode.Display:
            self.btn_delete.setVisible(False)
            self.top_handle.setVisible(False)
            self.mid_handle.setVisible(False)
            self.bottom_handle.setVisible(False)
            self.link_button.setVisible(False)
            self.available_for_linking = False
            self.link_source = False
            self.zero_btn.setVisible(False)
            self.setVisible(self.stick.is_visible)
        elif mode == StickMode.EditDelete:
            self.set_mode(StickMode.Display)
            self.top_handle.setVisible(True)
            self.mid_handle.setVisible(True)
            self.bottom_handle.setVisible(True)
            self.available_for_linking = False
            self.link_source = False
            self.btn_delete.setVisible(True)
        elif mode == StickMode.LinkSource:
            self.set_mode(StickMode.Display)
            self.link_source = True
            self.available_for_linking = False
            self.link_button.setPos(self.boundingRect().topLeft())
            self.link_button.set_width(int(self.boundingRect().width()))
            self.link_button.set_button_height(int(self.boundingRect().height()))
            self.link_button.adjust_text_to_button()
        elif mode == StickMode.LinkTarget:
            self.set_mode(StickMode.Display)
            self.link_source = False
            self.available_for_linking = True
        elif mode == StickMode.Edit:
            self.set_mode(StickMode.EditDelete)
            self.btn_delete.setVisible(False)
        elif mode == StickMode.Measurement:
            self.zero_btn.setVisible(True)
            self.setVisible(True)

        self.mode = mode
        self.update_tooltip()
        self.update()

    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        if self.mode != StickMode.EditDelete:
            return
        if self.hovered_handle is None:
            return

        self.hovered_handle.setBrush(self.handle_press_brush)
        if self.hovered_handle == self.mid_handle:
            self.bottom_handle.setBrush(self.handle_press_brush)
            self.bottom_handle.setPen(self.handle_press_pen)
            self.bottom_handle.setOpacity(0.5)
            self.top_handle.setBrush(self.handle_press_brush)
            self.top_handle.setPen(self.handle_press_pen)
            self.top_handle.setOpacity(0.5)
        self.hovered_handle.setPen(self.handle_press_pen)
        self.hovered_handle.setOpacity(0.5)
        self.handle_mouse_offset = self.hovered_handle.rect().center() - event.pos()
        self.btn_delete.setVisible(False)

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent):
        if self.available_for_linking:
            self.link_accepted.emit(self)
            return

        if self.mode == StickMode.Measurement:
            old_snow = self.stick.snow_height_px
            self.measured_height = self.proposed_snow_height
            self.stick.set_snow_height_px(self.proposed_snow_height)
            if abs(old_snow - self.proposed_snow_height) > 0:
                self.measurement_corrected.emit(self)
            self.proposed_snow_height = -1

        if self.mode != StickMode.EditDelete and self.mode != StickMode.Edit:
            return

        if self.hovered_handle is not None:
            self.hovered_handle.setBrush(self.handle_hover_brush)
            self.hovered_handle.setPen(self.handle_idle_pen)
            self.hovered_handle.setOpacity(1.0)
            if self.hovered_handle == self.mid_handle:
                self.bottom_handle.setBrush(self.handle_idle_brush)
                self.bottom_handle.setPen(self.handle_idle_pen)
                self.bottom_handle.setOpacity(1.0)
                self.top_handle.setBrush(self.handle_idle_brush)
                self.top_handle.setPen(self.handle_idle_pen)
                self.top_handle.setOpacity(1.0)
            self.stick_changed.emit(self)
        self.hovered_handle = None
        if self.mode == StickMode.EditDelete:
            self.btn_delete.setVisible(True)
    
    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):
        if self.hovered_handle is None:
            return
        if self.hovered_handle == self.top_handle:
            self.line.setP1((event.pos() + self.handle_mouse_offset).toPoint())
        elif self.hovered_handle == self.bottom_handle:
            self.line.setP2((event.pos() + self.handle_mouse_offset).toPoint())
        else:
            displacement = event.pos() - event.lastPos()
            self.setPos(self.pos() + displacement)
        self.adjust_handles()
        self.adjust_stick()
        self.scene().update()

    def set_top(self, pos: QPoint):
        self.line.setP1(pos)
        self.adjust_handles()
        self.adjust_stick()
        self.scene().update()

    def set_bottom(self, pos: QPoint):
        self.line.setP2(pos)
        self.adjust_handles()
        self.adjust_stick()
        self.scene().update()

    def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
        if self.available_for_linking:
            self.hovered.emit(True, self)
        elif self.link_source:
            self.link_button.setVisible(True)
        self.scene().update()

    def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
        for h in self.handles:
            h.setBrush(self.handle_idle_brush)
        self.hovered_handle = None
        if self.available_for_linking:
            self.hovered.emit(False, self)
        self.link_button.setVisible(False)
        self.proposed_snow_height = -1
        self.scene().update()
    
    def hoverMoveEvent(self, event: QGraphicsSceneHoverEvent):
        if self.mode != StickMode.EditDelete and self.mode != StickMode.Edit and self.mode != StickMode.Measurement:
            return
        if self.mode == StickMode.Measurement:
            self.proposed_snow_height = max(self.line.p2().y() - event.pos().y(), 0)
            self.update()
            return
        hovered_handle = list(filter(lambda h: h.rect().contains(event.pos()), self.handles))
        if len(hovered_handle) == 0:
            if self.hovered_handle is not None:
                self.hovered_handle.setBrush(self.handle_idle_brush)
                self.hovered_handle = None
            return
        if self.hovered_handle is not None and self.hovered_handle != hovered_handle[0]:
            self.hovered_handle.setBrush(self.handle_idle_brush)
        self.hovered_handle = hovered_handle[0]
        if self.hovered_handle == self.top_handle:
            self.top_handle.setBrush(self.handle_hover_brush)
        elif self.hovered_handle == self.bottom_handle:
            self.bottom_handle.setBrush(self.handle_hover_brush)
        else:
            self.mid_handle.setBrush(self.handle_hover_brush)

        self.scene().update()
    
    def adjust_stick(self):
        self.stick.top[0] = self.pos().x() + self.line.p1().x()
        self.stick.top[1] = self.pos().y() + self.line.p1().y()
        self.stick.bottom[0] = self.pos().x() + self.line.p2().x()
        self.stick.bottom[1] = self.pos().y() + self.line.p2().y()

    def adjust_handles(self):
        if self.line.p1().y() > self.line.p2().y():
            p1, p2 = self.line.p1(), self.line.p2()
            self.line.setP1(p2)
            self.line.setP2(p1)
            if self.hovered_handle is not None:
                self.hovered_handle.setBrush(self.handle_idle_brush)
                self.hovered_handle.setPen(self.handle_idle_pen)
                self.hovered_handle = self.top_handle if self.hovered_handle == self.bottom_handle else self.bottom_handle
                self.hovered_handle.setBrush(self.handle_press_brush)
                self.hovered_handle.setPen(self.handle_press_pen)
        rect = self.top_handle.rect()
        rect.moveCenter(self.line.p1())
        self.top_handle.setRect(rect)
        rect = self.bottom_handle.rect()
        rect.moveCenter(self.line.p2())
        self.bottom_handle.setRect(rect)
        rect = self.mid_handle.rect()
        rect.moveCenter(self.line.center())
        self.mid_handle.setRect(rect)
        self.btn_delete.setPos(self.top_handle.rect().center() - QPointF(self.btn_delete.boundingRect().width() / 2,
                                                               self.btn_delete.boundingRect().height() + self.top_handle.boundingRect().height() / 2))

    def set_available_for_linking(self, available: bool):
        self.available_for_linking = available

    def set_is_link_source(self, is_source: bool):
        self.link_source = is_source
        self.link_button.setPos(self.boundingRect().topLeft())
        self.link_button.set_width(int(self.boundingRect().width()))
        self.link_button.set_button_height(int(self.boundingRect().height()))
        self.link_button.adjust_text_to_button()
    
    def set_frame_color(self, color: Optional[QColor]):
        self.frame_color = color if color is not None else self.normal_color
        self.update()

    def set_is_linked(self, value: bool):
        self.is_linked = value
        if not self.is_linked:
            self.set_frame_color(None)
            if self.available_for_linking:
                self.highlight(QColor(0, 255, 0, 100))
            else:
                self.highlight(None)
        self.update_tooltip()

    def adjust_line(self):
        self.setPos(QPointF(0.5 * (self.stick.top[0] + self.stick.bottom[0]), 0.5 * (self.stick.top[1] + self.stick.bottom[1])))
        vec = 0.5 * (self.stick.top - self.stick.bottom)
        self.line.setP1(QPointF(vec[0], vec[1]))
        self.line.setP2(-self.line.p1())
        self.gline.setLine(self.line)
        self.adjust_handles()
        self.stick_label_text.setPos(self.line.p1() - QPointF(0.5 * self.stick_label_text.boundingRect().width(),
                                                             1.3 * self.stick_label_text.boundingRect().height()))
        self.update()

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

    def is_selected(self) -> bool:
        return self.selected

    def set_snow_height(self, height: int):
        self.measured_height = height
        self.update()

    def border_normal(self):
        self.current_color = self.normal_color
        self.update()

    def border_positive(self):
        self.current_color = self.positive_color
        self.update()

    def border_negative(self):
        self.current_color = self.negative_color
        self.update()

    @pyqtProperty(QColor)
    def highlight_color(self) -> QColor:
        return self.current_highlight_color

    @highlight_color.setter
    def highlight_color(self, color: QColor):
        self.current_highlight_color = color

    def highlight(self, color: Optional[QColor], animated: bool = False):
        self.highlighted = color is not None
        if not animated or color is None:
            self.highlight_animation.stop()
            self.current_highlight_color = self.normal_color if color is None else color
            self.update()
            return
        self.highlight_animation.setStartValue(color)
        self.highlight_animation.setEndValue(color)
        self.highlight_animation.setKeyValueAt(0.5, color.darker())
        self.highlight_animation.setDuration(2000)
        self.highlight_animation.setLoopCount(-1)
        self.highlight_animation.start()

    def handle_link_button_hovered(self, btn: Dict[str, Any]):
        self.link_button.setVisible(btn['hovered'])

    def handle_highlight_animation_value_changed(self, new: QColor):
        if not self.deleting:
            self.update(self.boundingRect().marginsAdded(QMarginsF(10, 10, 10, 10)))

    def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None:
        self.right_clicked.emit({'stick_widget': self})

    def set_stick_label(self, label: str):
        self.stick.label = label
        self.stick_label_text.setText(label)
        self.update_tooltip()
        self.update()

    def get_stick_label(self) -> str:
        return self.stick.label

    def get_stick_length_cm(self) -> int:
        return self.stick.length_cm

    def set_stick_length_cm(self, length: int):
        self.stick.length_cm = length
        self.update_tooltip()
        self.update()

    def update_tooltip(self):
        if self.mode != StickMode.Display or self.mode == StickMode.Measurement:
            self.setToolTip("")
            return
        snow_txt = "Snow height: "
        if self.stick.snow_height_px >= 0:
            snow_txt += str(self.stick.snow_height_cm) + " cm"
            self.stick_label_text.setText(str(self.stick.snow_height_cm))
        else:
            snow_txt = "not measured"
            self.stick_label_text.setVisible(False)
        self.stick_label_text.setText(self.stick.label)
        self.stick_label_text.setVisible(True)
        stick_view_text = ''
        role = ''
        if self.stick.alternative_view is not None:
            alt_view = self.stick.alternative_view
            role = " - primary"
            alt = "Secondary"
            if not self.stick.primary:
                role = " - secondary"
                alt = "Primary"
            stick_view_text = f'\n{alt} view: {alt_view.label} in {alt_view.camera_folder.name}\n'
        mark = '*' if self.stick.determines_quality else ''
        self.setToolTip(f'{mark}{self.stick.label}{role}{stick_view_text}\nLength: {self.stick.length_cm} cm\n{snow_txt}')

    def set_stick(self, stick: Stick):
        self.reset_d_btns()
        self.stick = stick
        self.adjust_line()
        self.adjust_handles()
        self.set_snow_height(stick.snow_height_px)
        self.update_tooltip()
        self.set_show_measurements(self.show_measurements)
        if self.mode == StickMode.Measurement:
            self.set_frame_color(QColor(200, 100, 0, 100) if not self.stick.is_visible else None)
            self.setVisible(True)
            self.clearly_visible_btn.setVisible(not self.stick.is_visible)
        else:
            self.setVisible(self.stick.is_visible)

    def set_show_measurements(self, show: bool):
        self.show_measurements = show
        if self.show_measurements:
            self.stick_label_text.setText(str(self.stick.snow_height_cm) if self.stick.snow_height_cm >= 0 else
                                          "n/a")
        else:
            self.stick_label_text.setText(self.stick.label)
        self.update()

    def handle_zero(self):
        self.measured_height = 0
        self.stick.set_snow_height_px(0)
        self.measurement_corrected.emit(self)

    def reset_d_btns(self):
        self.zero_btn.set_default_state()
Пример #38
0
class TransitionGraphicsItem(QGraphicsObject):
    # constant values
    SQUARE_SIDE = 10
    ARROW_SIZE = 12
    PEN_NORMAL_WIDTH = 1
    PEN_FOCUS_WIDTH = 3

    posChanged = pyqtSignal('QGraphicsItem')

    def __init__(self, data):
        super(QGraphicsObject, self).__init__()
        self.transitionData = data

        self.originLine = None
        self.destinationLine = None
        self.arrow = None
        self.textGraphics = None
        self.middleHandle = None

        self.graphicsOrigin = self.transitionData.origin.getGraphicsItem()
        self.graphicsDestination = self.transitionData.destination.getGraphicsItem()

        # connect position changed event
        self.graphicsOrigin.posChanged.connect(self.statePosChanged)
        self.graphicsDestination.posChanged.connect(self.statePosChanged)

        self.midPointX = (self.graphicsDestination.scenePos().x() + self.graphicsOrigin.scenePos().x()) / 2.0
        self.midPointY = (self.graphicsDestination.scenePos().y() + self.graphicsOrigin.scenePos().y()) / 2.0

        self.createOriginLine()
        self.createDestinationLine()

        self.createArrow()
        self.createMiddleHandle()
        self.createIdTextBox()

    def statePosChanged(self, state):
        if self.graphicsOrigin == state:
            self.createOriginLine()
        elif self.graphicsDestination == state:
            self.createDestinationLine()
            self.createArrow()

    def createOriginLine(self):
        if self.originLine == None:
            self.originLine = QGraphicsLineItem(self.midPointX, self.midPointY, self.graphicsOrigin.scenePos().x(),
                                                self.graphicsOrigin.scenePos().y(), self)
        else:
            self.originLine.setLine(QLineF(self.midPointX, self.midPointY, self.graphicsOrigin.scenePos().x(),
                                           self.graphicsOrigin.scenePos().y()))
        myLine = self.originLine.line()
        myLine.setLength(myLine.length() - StateGraphicsItem.NODE_WIDTH / 2)
        self.originLine.setLine(myLine)

    def createDestinationLine(self):
        if self.destinationLine == None:
            self.destinationLine = QGraphicsLineItem(self.midPointX, self.midPointY, self.graphicsDestination.scenePos().x(),
                                                     self.graphicsDestination.scenePos().y(), self)
        else:
            self.destinationLine.setLine(QLineF(self.midPointX, self.midPointY, self.graphicsDestination.scenePos().x(),
                                                self.graphicsDestination.scenePos().y()))

        myLine = self.destinationLine.line()
        myLine.setLength(myLine.length() - StateGraphicsItem.NODE_WIDTH / 2)
        self.destinationLine.setLine(myLine)

    def createArrow(self):
        # add an arrow to destination line
        myLine = self.destinationLine.line()
        myLine.setLength(myLine.length() - TransitionGraphicsItem.ARROW_SIZE)
        rotatePoint = myLine.p2() - self.destinationLine.line().p2()

        rightPointX = rotatePoint.x() * math.cos(math.pi / 6) - rotatePoint.y() * math.sin(math.pi / 6)
        rightPointY = rotatePoint.x() * math.sin(math.pi / 6) + rotatePoint.y() * math.cos(math.pi / 6)
        rightPoint = QPointF(rightPointX + self.destinationLine.line().x2(),
                             rightPointY + self.destinationLine.line().y2())

        leftPointX = rotatePoint.x() * math.cos(-math.pi / 6) - rotatePoint.y() * math.sin(-math.pi / 6)
        leftPointY = rotatePoint.x() * math.sin(-math.pi / 6) + rotatePoint.y() * math.cos(-math.pi / 6)
        leftPoint = QPointF(leftPointX + self.destinationLine.line().x2(),
                            leftPointY + self.destinationLine.line().y2())

        polygon = QPolygonF()
        polygon << rightPoint << leftPoint << self.destinationLine.line().p2() << rightPoint

        if self.arrow == None:
            self.arrow = QGraphicsPolygonItem(polygon, self)
        else:
            self.arrow.setPolygon(polygon)

        brush = QBrush(Qt.SolidPattern)
        brush.setColor(Qt.black)
        self.arrow.setBrush(brush)

    def createMiddleHandle(self):
        # create middle handle
        if self.middleHandle == None:
            self.middleHandle = RectHandleGraphicsItem(TransitionGraphicsItem.SQUARE_SIDE, self)
            self.middleHandle.setFlag(QGraphicsItem.ItemIsMovable)

        self.middleHandle.setPos(self.midPointX, self.midPointY)

    def createIdTextBox(self):
        if self.textGraphics == None:
            self.textGraphics = IdTextBoxGraphicsItem(self.transitionData.name, self)
            self.textGraphics.textChanged.connect(self.nameChanged)
        else:
            self.textGraphics.setPlainText(self.transitionData.name)
        textWidth = self.textGraphics.boundingRect().width()
        self.textGraphics.setPos(self.midPointX - textWidth / 2, self.midPointY + TransitionGraphicsItem.SQUARE_SIDE -
                                 (TransitionGraphicsItem.SQUARE_SIDE / 2) + 5)

    def updateMiddlePoints(self, newPosition):
        self.midPointX = newPosition.x()
        self.midPointY = newPosition.y()
        self.createOriginLine()
        self.createDestinationLine()
        self.createArrow()
        self.createIdTextBox()
        self.posChanged.emit(self)

    def nameChanged(self, name):
        self.transitionData.name = name
        self.createIdTextBox()

    def boundingRect(self):
        if self.middleHandle != None:
            return self.middleHandle.boundingRect()
        else:
            return None

    def disableInteraction(self):
        if self.middleHandle is not None:
            self.middleHandle.setFlag(QGraphicsItem.ItemIsMovable, False)
            self.middleHandle.disableInteraction()
Пример #39
0
class AnalogClock(Desklet):

    def __init__(self):
        super().__init__()

        self.center_x = 0
        self.center_y = 0
        self.radius = 1

        self.now = datetime.utcfromtimestamp(0)

        # build the clock face
        self.circle = QGraphicsEllipseItem(self.root)

        self.lines = []
        for _ in range(0, 60):
            self.lines.append(QGraphicsLineItem(self.root))

        self.hours_hand = QGraphicsLineItem(self.root)
        self.minutes_hand = QGraphicsLineItem(self.root)
        self.seconds_hand = QGraphicsLineItem(self.root)

        self.hand_circle = QGraphicsEllipseItem(self.root)

    def set_style(self, style):
        super().set_style(style)

        # minute
        pen = QPen(self.style.foreground_color)
        pen.setWidth(12)
        pen.setCapStyle(Qt.RoundCap)
        self.minutes_hand.setPen(pen)

        # hour
        pen = QPen(self.style.foreground_color)
        pen.setWidth(16)
        pen.setCapStyle(Qt.RoundCap)
        self.hours_hand.setPen(pen)

        # second
        pen = QPen(self.style.midcolor)
        pen.setWidth(4)
        pen.setCapStyle(Qt.RoundCap)
        self.seconds_hand.setPen(pen)

        # outer circle
        pen = QPen(self.style.foreground_color)
        pen.setWidth(6)
        self.circle.setPen(pen)

        # inner circle
        self.hand_circle.setBrush(QBrush(self.style.background_color))

        # minute lines
        pen = QPen()
        pen.setCapStyle(Qt.RoundCap)
        pen.setColor(self.style.midcolor)
        pen.setWidth(4)

        bold_pen = QPen()
        bold_pen.setCapStyle(Qt.RoundCap)
        bold_pen.setColor(self.style.foreground_color)
        bold_pen.setWidth(6)

        for i, line in enumerate(self.lines):
            if i % 5 == 0:
                line.setPen(bold_pen)
            else:
                line.setPen(pen)

        self.layout()

    def set_rect(self, rect):
        super().set_rect(rect)
        self.layout()

    def layout(self):
        self.center_x = self.rect.left() + self.rect.width() / 2.0
        self.center_y = self.rect.top() + self.rect.height() / 2.0
        self.radius = min(self.rect.width(), self.rect.height()) / 2.0 - 3.0

        self.circle.setRect(self.center_x - self.radius,
                            self.center_y - self.radius,
                            2 * self.radius,
                            2 * self.radius)
        self.hand_circle.setRect(self.center_x - 5,
                                 self.center_y - 5,
                                 10, 10)

        for i, line in enumerate(self.lines):
            angle = i * 2.0 * math.pi / 60.0
            if i % 5 == 0:
                line.setLine(self.center_x + math.cos(angle) * self.radius * 0.85,
                             self.center_y + math.sin(angle) * self.radius * 0.85,
                             self.center_x + math.cos(angle) * self.radius * 0.95,
                             self.center_y + math.sin(angle) * self.radius * 0.95)
            else:
                line.setLine(self.center_x + math.cos(angle) * self.radius * 0.90,
                             self.center_y + math.sin(angle) * self.radius * 0.90,
                             self.center_x + math.cos(angle) * self.radius * 0.95,
                             self.center_y + math.sin(angle) * self.radius * 0.95)

        self.update(self.now)

    def update(self, now):
        self.now = now

        hour = (self.now.hour / 12.0 + self.now.minute / 60.0 / 12.0) * 2.0 * math.pi - math.pi / 2.0
        minute = (self.now.minute / 60.0 + self.now.second / 60.0 / 60.0) * 2.0 * math.pi - math.pi / 2.0
        second = (self.now.second / 60.0) * 2.0 * math.pi - math.pi / 2.0

        self.set_seconds(second)
        self.set_minutes(minute)
        self.set_hours(hour)

    def set_seconds(self, n):
        self.seconds_hand.setLine(
            self.center_x + math.cos(n) * self.radius * 0.0,
            self.center_y + math.sin(n) * self.radius * 0.0,
            self.center_x + math.cos(n) * self.radius * 0.8,
            self.center_y + math.sin(n) * self.radius * 0.8)

    def set_minutes(self, n):
        self.minutes_hand.setLine(
            self.center_x + math.cos(n) * self.radius * 0.0,
            self.center_y + math.sin(n) * self.radius * 0.0,
            self.center_x + math.cos(n) * self.radius * 0.8,
            self.center_y + math.sin(n) * self.radius * 0.8)

    def set_hours(self, n):
        self.hours_hand.setLine(
            self.center_x + math.cos(n) * self.radius * 0.0,
            self.center_y + math.sin(n) * self.radius * 0.0,
            self.center_x + math.cos(n) * self.radius * 0.45,
            self.center_y + math.sin(n) * self.radius * 0.45)