def build_bonus_fields(self): for bonus_field in self._bonus_fields: brush = bonus_field["Brush"] pen = bonus_field["Pen"] bonus_fields = [] for coords in bonus_field["Coords"]: label = bonus_field["Name"] if coords == Coords.central(): label = '✸' square = self._board_squares[coords] square.setZValue(2) bonus_fields.append(square) field_name = QGraphicsSimpleTextItem(label) font = field_name.font() font.setPointSize(10) if coords == Coords.central(): font.setPointSize(20) fm = QFontMetrics(font) field_name.setZValue(2.1) field_name.setFont(font) x = coords.x * SQUARE_SIZE + (SQUARE_SIZE - fm.width(label)) / 2 y = coords.y * SQUARE_SIZE + (SQUARE_SIZE - fm.height()) / 2 field_name.setPos(x, y) field_name.setBrush(bonus_field["Label brush"]) self._labels[(x, y)] = field_name self.scene.addItem(field_name) paint_graphic_items(bonus_fields, pen, brush)
def createLabel(self) -> QGraphicsSimpleTextItem: """ Returns: the label item """ label = QGraphicsSimpleTextItem("%d" % (self._id_num)) label.setFont(_FONT) label.setZValue(styles.ZPATHHELIX) label.setParentItem(self) return label
def createLabel(self): """Summary Returns: TYPE: Description """ label = QGraphicsSimpleTextItem("%d" % (self._id_num)) label.setFont(_FONT) label.setZValue(styles.ZPATHHELIX) label.setParentItem(self) return label
def createLabel(self) -> QGraphicsSimpleTextItem: """Creates a text label to display the ID number. Font and Z are set in gridstyles. Returns: the label """ label = QGraphicsSimpleTextItem("%d" % self.idNum()) label.setFont(_FONT) label.setZValue(_ZVALUE) label.setParentItem(self) return label
class BasicNode(QGraphicsItemGroup): def __init__(self, model, manager, text_color=Qt.black): bg_color = model.get_bg_color() super(BasicNode, self).__init__() self.model = model text = model.get_display_text() self.manager = manager self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.text_graph = QGraphicsSimpleTextItem(text) self.text_graph.setBrush(text_color) bound = self.text_graph.boundingRect() r = QPointF(bound.width() / 2, bound.height() / 2) text_center = self.text_graph.pos() + r self.text_graph.setPos(-text_center) self.addToGroup(self.text_graph) self.box_graph = BoxOutline(bg_color) empty_space = QPointF(UNIT, UNIT) newr = (empty_space + r) self.box_graph.rect = QRectF(-newr, newr) self.addToGroup(self.box_graph) self.text_graph.setZValue(1.0) self.box_graph.setZValue(0.0) self.children = [] def itemChange(self, change, value): if change == QGraphicsItem.ItemSelectedChange: self.manager.selection_changed(self, value) return value else: return super(BasicNode, self).itemChange(change, value) def get_width(self): return self.boundingRect().width() def set_left_pos(self, pos): pos += QPoint(self.get_width() / 2.0, 0) self.setPos(pos) def left_pos(self): return self.pos() - QPointF(self.get_width() / 2.0, 0) def center_pos(self): return self.pos() def right_pos(self): return self.pos() + QPointF(self.get_width() / 2.0, 0) """
def init_ui(self): scene = QGraphicsScene() scene.setBackgroundBrush(QColor(100, 100, 100)) scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex) scene.setSceneRect(scene.itemsBoundingRect()) self.setDragMode(QGraphicsView.RubberBandDrag) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) self.frame_item = QGraphicsPixmapItem() self.text_item_offset = 0 self.rect_item_array = [] self.text_item_array = [] for i in range(0, 5): rect_item = QGraphicsRectItem() rect_item.setVisible(False) rect_item.setZValue(20.0) rect_item.setPen(QPen(Qt.red, 5)) rect_item.setRect(20, 20, 20, 20) scene.addItem(rect_item) self.rect_item_array.append(rect_item) text_item = QGraphicsSimpleTextItem("") text_item.setBrush(QBrush(Qt.red)) text_item.setZValue(20.0) text_item.setPos(10, 50) text_item.setFont(QFont("黑体", 32)) text_item.setVisible(False) scene.addItem(text_item) self.text_item_array.append(text_item) scene.addItem(self.frame_item) self.curr_factor = 1.0 self.setScene(scene)
def init_ui(self): scene = QGraphicsScene() scene.setBackgroundBrush(QColor(100, 100, 100)) scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex) scene.setSceneRect(scene.itemsBoundingRect()) self.setDragMode(QGraphicsView.RubberBandDrag) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) self.frame_item = QGraphicsPixmapItem() self.text_item_offset = 0 self.rect_item_array = [] self.text_item_array = [] for i in range(0, 5): rect_item = QGraphicsRectItem() rect_item.setVisible(False) rect_item.setZValue(20.0) rect_item.setPen(QPen(Qt.red, 5)) rect_item.setRect(20, 20, 20, 20) scene.addItem(rect_item) self.rect_item_array.append(rect_item) text_item = QGraphicsSimpleTextItem("") text_item.setBrush(QBrush(Qt.red)) text_item.setZValue(20.0) text_item.setPos(10, 50) text_item.setFont(QFont("黑体", 24)) text_item.setVisible(False) scene.addItem(text_item) self.text_item_array.append(text_item) scene.addItem(self.frame_item) self.curr_factor = 1.0 self.setScene(scene)
class NodeItem(QGraphicsItem): # 面向图形界面, 负责控制显示效果 MIN_SIZE, MAX_SIZE = 10, 100 press_callback = EMPTY_FUNC release_callback = EMPTY_FUNC double_click_callback = EMPTY_FUNC move_callback = EMPTY_FUNC def __init__(self, node_id): super().__init__() self.setZValue(2) self.setAcceptHoverEvents(True) self.setFlag(QGraphicsItem.ItemIsMovable) # 可以移动 self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) # 面向UINet 负责增添逻辑操作 self.node_id = node_id self.hover = False self.cached_size = None self.bounding_rect = QRectF() # XXX 在重绘时会被更新,重绘前可能会节点可能会被覆盖显示 # self.call_backs = CallTable() self.text_item = QGraphicsSimpleTextItem(self) self.text_item.setZValue(3) # 面向图形界面, 负责控制显示效果 self.style = { 'name': f' {node_id}', 'color': Qt.white, 'shape': 'Pie', # ('Pie', 'Rect', QPixmap) 'size': 0.5, # 0~1 的中间值 'text': '', 'text_color': Qt.black, 'show_text': False, } def type(self) -> int: return QGraphicsItem.UserType + abs(hash(NodeItem)) def boundingRect(self): return self.bounding_rect def shape(self): path = QPainterPath() path.addRect(self.bounding_rect) return path def paint(self, painter, option, widget=None) -> None: # 绘制尺寸 size = threshold(0.0, self.style['size'], 1.0) size = size * (self.MAX_SIZE - self.MIN_SIZE) + self.MIN_SIZE if size != self.cached_size: self.bounding_rect = QRectF(-size / 2, -size / 2, size, size) self.cached_size = size # 绘制图标或颜色和形状 if isinstance(self.style['shape'], QPixmap): pixmap = self.style['shape'] painter.drawPixmap(self.bounding_rect, pixmap, QRectF(pixmap.rect())) elif self.style['shape'] == 'Pie': painter.setBrush(self.style['color']) painter.drawEllipse(self.bounding_rect) # or drawRect elif self.style['shape'] == 'Rect': painter.setBrush(self.style['color']) painter.drawRect(self.bounding_rect) # or drawRect else: raise ValueError('未知shape类型', self.style['shape']) # 绘制说明 text = f"{self.style['name']}\n" if self.style['show_text'] or self.hover: text += str(self.style['text']) self.text_item.setPen(self.style['text_color']) self.text_item.setText(text) self.text_item.show() # ------------------------------------------------------------------------------------------------------------------ def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionHasChanged: self.style['pos'] = self.pos() # 更新位置变化 self.move_callback(self.node_id) return super().itemChange(change, value) def mousePressEvent(self, event): self.press_callback(self.node_id) return super().mousePressEvent(event) def mouseReleaseEvent(self, event): self.release_callback(self.node_id) return super().mouseReleaseEvent(event) def mouseDoubleClickEvent(self, event): self.double_click_callback(self.node_id) return super().mouseDoubleClickEvent(event) def hoverEnterEvent(self, event): self.hover = True return super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): self.hover = False return super().hoverLeaveEvent(event) # ------------------------------------------------------------------------------------------------------------------ def setStyle(self, style) -> None: for key in self.style: if key in style: self.style[key] = style[key] self.update() def checkPos(self, x, y): pos = QPointF(x, y) if self.pos() != pos: self.setPos(pos)
def createLabel(self): label = QGraphicsSimpleTextItem("%d" % self._virtual_helix.number()) label.setFont(_FONT) label.setZValue(_ZVALUE) label.setParentItem(self) return label
def createLabel(self): label = QGraphicsSimpleTextItem("%d" % self._virtual_helix.number()) label.setFont(_FONT) label.setZValue(styles.ZPATHHELIX) label.setParentItem(self) return label
class EdgeItem(QGraphicsItem): LINE_WIDTH = 1 OFFSET = 8 # 方向线偏离中心线的距离 MIN_ARROW_WIDTH, MAX_ARROW_WIDTH = 1, 8 double_click_callback = EMPTY_FUNC def __init__(self, edge_id): super().__init__() self.setZValue(1) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self.setAcceptHoverEvents(True) self.edge_id = edge_id self.text_item = QGraphicsSimpleTextItem('', self) self.text_item.setZValue(4) self.style = { 'name': f'Edge{edge_id}', 'color': Qt.black, 'width': 0.5, # 0~1 的中间值 'line': Qt.SolidLine, 'show_arrow': False, 'text': '', 'text_color': Qt.black, 'show_text': False, } self.hover = False def type(self): return QGraphicsItem.UserType + abs(hash(EdgeItem)) def boundingRect(self): return self.bounding_rect def shape(self): path = QPainterPath() path.addPolygon(self.shape_polygon) path.closeSubpath() return path # ------------------------------------------------------------------------- def adjust(self, src_p: QPointF, dst_p: QPointF): self.angle = getAngle(src_p, dst_p) self.src_p = src_p self.arrow_p = (src_p + 2 * dst_p) / 3 # 箭头开始位置, 前端2/3处 self.dst_p = dst_p W1 = 1 * self.OFFSET W2 = 2 * self.OFFSET W3 = 3 * self.OFFSET vec = getRightOffsetVector(self.angle) self.arrow_polygon = QPolygonF([ src_p + vec * W1, dst_p + vec * W1, self.arrow_p + vec * W2, self.arrow_p + vec * W1 ]) self.shape_polygon = QPolygonF( [src_p, src_p + vec * W2, dst_p + vec * W2, dst_p]) self.bounding_rect = QRectF(src_p, dst_p).normalized() # normalized 正方向 self.bounding_rect.adjust(-W3, -W3, W3, W3) self.text_p = ((src_p + dst_p) / 2) + vec * W1 self.text_item.setPos(self.text_p) self.prepareGeometryChange() # ------------------------------------------------------------------------- def paint(self, painter, option, widget=None): if self.style['show_arrow'] or self.hover: width = threshold(0.0, self.style['width'], 1.0) width = width * (self.MAX_ARROW_WIDTH - self.MIN_ARROW_WIDTH) + self.MIN_ARROW_WIDTH painter.setPen(QPen(self.style['color'], width, self.style['line'])) painter.setBrush(self.style['color']) painter.drawPolygon(self.arrow_polygon) else: # TODO 定制线类型 虚线或实线 painter.setPen(QPen(Qt.black, self.LINE_WIDTH)) painter.drawLine(self.src_p, self.dst_p) if (self.style['show_arrow'] and self.style['show_text']) or self.hover: self.text_item.setPen(self.style['text_color']) self.text_item.setText( f"{self.style['name']}\n{self.style['text']}") self.text_item.show() else: self.text_item.hide() # ------------------------------------------------------------------------- def mouseDoubleClickEvent(self, event): self.double_click_callback(self.edge_id) super().mouseDoubleClickEvent(event) def hoverEnterEvent(self, event): self.hover = True self.update() super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): self.hover = False self.update() super().hoverLeaveEvent(event) # ------------------------------------------------------------------------- def setStyle(self, style) -> None: for key in self.style: try: self.style[key] = style[key] except KeyError: pass self.update()
def createLabel(self): label = QGraphicsSimpleTextItem("%d" % self._virtual_helix.number()) label.setFont(self._FONT) label.setZValue(self._ZVALUE) label.setParentItem(self) return label
class ClearanceWidthGraph(BaseGraphic): def __init__(self, *args): super(ClearanceWidthGraph, self).__init__(*args) self.dimension_analysis = self.section_analyzer.dimension_analysis self.clearance_analysis = self.section_analyzer.clearance_analysis self.min_horizontal_clearance = self.dimension_analysis.min_horizontal_clearance self.graph_zero = [None, None] self.graph_end = [None, None] self.clearance_label = QGraphicsSimpleTextItem() self.addToGroup(self.clearance_label) self.clearance_label.setZValue(1.0) self.init_dimension() def init_dimension(self): super(ClearanceWidthGraph, self).init_dimension() height_start = self.dimension_analysis.bounding_rect[2] height_end = self.dimension_analysis.bounding_rect[3] self.content_height = (height_end - height_start) * self.height_multiplier self.update_graph_size() self.create_axis() self.create_scale() self.add_clearance_graph() def create_axis(self): bounding_end = abs(self.dimension_analysis.bounding_rect[3]) bounding_start = abs(self.dimension_analysis.bounding_rect[2]) pen = QPen() pen.setWidthF(0.5) # horizontal line self.graph_zero[0] = self.position[0] + self.margin - self.line_extend self.graph_zero[1] = self.position[1] + bounding_start * self.height_multiplier + self.margin self.graph_end[0] = self.graph_zero[0] + self.content_width + self.line_extend self.graph_end[1] = self.graph_zero[1] line_item_horizontal = QGraphicsLineItem(self.graph_zero[0], self.graph_zero[1], self.graph_end[0], self.graph_end[1]) line_item_horizontal.setPen(pen) self.addToGroup(line_item_horizontal) center = (self.graph_zero[0] + self.line_extend), self.graph_zero[1] y_top = center[1] - (bounding_start*self.height_multiplier) y_bottom = center[1]+(bounding_end*self.height_multiplier) line_item_vertical = QGraphicsLineItem(center[0], y_top, center[0], y_bottom) line_item_vertical.setPen(pen) self.addToGroup(line_item_vertical) pen_thin = QPen() pen_thin.setWidthF(0.2) start_graph = center[1] - 10 while start_graph > center[1] - bounding_start * self.height_multiplier: line_item_horizontal = QGraphicsLineItem(self.graph_zero[0], start_graph, self.graph_end[0], start_graph) line_item_horizontal.setPen(pen_thin) line_item_horizontal.setZValue(-0.5) self.addToGroup(line_item_horizontal) start_graph -= 10 start_graph = center[1] + 10 while start_graph < center[1] + bounding_end * self.height_multiplier: line_item_horizontal = QGraphicsLineItem(self.graph_zero[0], start_graph, self.graph_end[0], start_graph) line_item_horizontal.setPen(pen_thin) line_item_horizontal.setZValue(-0.5) self.addToGroup(line_item_horizontal) start_graph += 10 def create_scale(self): section_num = len(self.section_analyzer.section_list) section_distance = self.section_analyzer.section_distance total_distance = section_num * section_distance div = int(Math.integer_division(total_distance, 1.0)) print(total_distance) for i in range(div+1): x = self.graph_zero[0] + i * self.length_multiplier + self.line_extend y = self.graph_zero[1] scale_text = QGraphicsSimpleTextItem("%.2f" % float(i)) scale_text.setPos(x, y) self.addToGroup(scale_text) start_to_zero = self.graph_zero[1] - self.position[1] step = abs(start_to_zero) // 25 x = self.graph_zero[0] - 15 y = self.graph_zero[1] for i in range(int(step)-1): if i > 0: value = i * 25 / 100 scene_y = y - i * 25 text = QGraphicsSimpleTextItem("-%.2f" % value) text.setPos(x, scene_y) self.addToGroup(text) start_to_zero = self.position[1] + self.height - self.graph_zero[1] step = abs(start_to_zero) // 25 x = self.graph_zero[0] - 15 y = self.graph_zero[1] for i in range(int(step)-1): if i > 0: value = i * 25 / 100 scene_y = y + i * 25 text = QGraphicsSimpleTextItem("%.2f" % value) text.setPos(x, scene_y) self.addToGroup(text) def add_clearance_graph(self): print("-----------------------------------------") horizontal_clearance = self.clearance_analysis.horizontal_clearance x_init = self.graph_zero[0] + self.line_extend y_init = self.graph_zero[1] for i in range(len(horizontal_clearance)): clearance_points = horizontal_clearance[i] x = x_init + i * self.dimension_analysis.section_distance * self.length_multiplier left = -self.dimension_analysis.domain_length right = self.dimension_analysis.domain_length if clearance_points[0]: left = clearance_points[0] if clearance_points[1]: right = clearance_points[1] clearance = right - left y_top = y_init + left * self.height_multiplier y_bottom = y_init + right * self.height_multiplier pen_red = QPen() red = Color.create_qcolor_from_rgb_tuple(Color.red) pen_red.setColor(red) pen_green = QPen() green = Color.create_qcolor_from_rgb_tuple(Color.green) pen_green.setColor(green) line = QGraphicsLineItem(x, y_top, x, y_bottom) if clearance < self.min_horizontal_clearance: line.setPen(pen_red) else: line.setPen(pen_green) self.addToGroup(line) pass def set_distance_pointer(self, distance): horizontal_clearance = self.clearance_analysis.horizontal_clearance super(ClearanceWidthGraph, self).set_distance_pointer(distance) index = int(distance/self.section_distance) clearance = None if index < len(horizontal_clearance): clearance = horizontal_clearance[index] x = self.distance_pointer.line().x1() y = self.distance_pointer.line().y1() + (self.distance_pointer.line().y2() - self.distance_pointer.line().y1())/8 self.clearance_label.setPos(x, y) if clearance: if clearance[1] and clearance[0]: distance = clearance[1] - clearance[0] self.clearance_label.setText("clearance = %.2f" % distance)
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)
class CameraView(QGraphicsObject): font: QFont = QFont("monospace", 16) stick_link_requested = pyqtSignal(StickWidget) stick_context_menu = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject') stick_widgets_out_of_sync = pyqtSignal('PyQt_PyObject') visibility_toggled = pyqtSignal() synchronize_clicked = pyqtSignal('PyQt_PyObject') previous_photo_clicked = pyqtSignal('PyQt_PyObject') next_photo_clicked = pyqtSignal('PyQt_PyObject') sync_confirm_clicked = pyqtSignal('PyQt_PyObject') sync_cancel_clicked = pyqtSignal('PyQt_PyObject') first_photo_clicked = pyqtSignal('PyQt_PyObject') enter_pressed = pyqtSignal() def __init__(self, scale: float, parent: Optional[QGraphicsItem] = None): QGraphicsObject.__init__(self, parent) self.current_highlight_color = QColor(0, 0, 0, 0) self.current_timer = -1 self.scaling = scale self.pixmap = QGraphicsPixmapItem(self) self.stick_widgets: List[StickWidget] = [] self.link_cam_text = QGraphicsSimpleTextItem("Link camera...", self) self.link_cam_text.setZValue(40) self.link_cam_text.setVisible(False) self.link_cam_text.setFont(CameraView.font) self.link_cam_text.setPos(0, 0) self.link_cam_text.setPen(QPen(QColor(255, 255, 255, 255))) self.link_cam_text.setBrush(QBrush(QColor(255, 255, 255, 255))) self.show_add_buttons = False self.camera = None self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.show_stick_widgets = False self.setAcceptHoverEvents(True) self.stick_edit_mode = False self.original_pixmap = self.pixmap.pixmap() self.hovered = False self.mode = 0 # TODO make enum Mode self.click_handler = None self.double_click_handler: Callable[[int, int], None] = None self.stick_widget_mode = StickMode.Display self.highlight_animation = QPropertyAnimation(self, b"highlight_color") self.highlight_animation.setEasingCurve(QEasingCurve.Linear) self.highlight_animation.valueChanged.connect( self.handle_highlight_color_changed) self.highlight_rect = QGraphicsRectItem(self) self.highlight_rect.setZValue(4) self.highlight_rect.setPen(QPen(QColor(0, 0, 0, 0))) self.title_btn = Button('btn_title', '', parent=self) self.title_btn.setFlag(QGraphicsItem.ItemIgnoresTransformations, False) self.title_btn.setZValue(5) self.title_btn.setVisible(False) self.sticks_without_width: List[Stick] = [] self.current_image_name: str = '' self.control_widget = ControlWidget(parent=self) self.control_widget.setFlag(QGraphicsItem.ItemIgnoresTransformations, False) self.control_widget.setVisible(True) self._connect_control_buttons() self.image_available = True self.blur_eff = QGraphicsBlurEffect() self.blur_eff.setBlurRadius(5.0) self.blur_eff.setEnabled(False) self.pixmap.setGraphicsEffect(self.blur_eff) self.overlay_message = QGraphicsSimpleTextItem('not available', parent=self) font = self.title_btn.font font.setPointSize(48) self.overlay_message.setFont(font) self.overlay_message.setBrush(QBrush(QColor(200, 200, 200, 200))) self.overlay_message.setPen(QPen(QColor(0, 0, 0, 200), 2.0)) self.overlay_message.setVisible(False) self.overlay_message.setZValue(6) self.stick_box = QGraphicsRectItem(parent=self) self.stick_box.setFlag(QGraphicsItem.ItemIsMovable, True) self.stick_box.setVisible(False) self.stick_box_start_pos = QPoint() def _connect_control_buttons(self): self.control_widget.synchronize_btn.clicked.connect( lambda: self.synchronize_clicked.emit(self)) self.control_widget.prev_photo_btn.clicked.connect( lambda: self.previous_photo_clicked.emit(self)) self.control_widget.next_photo_btn.clicked.connect( lambda: self.next_photo_clicked.emit(self)) self.control_widget.accept_btn.clicked.connect( lambda: self.sync_confirm_clicked.emit(self)) self.control_widget.cancel_btn.clicked.connect( lambda: self.sync_cancel_clicked.emit(self)) self.control_widget.first_photo_btn.clicked.connect( lambda: self.first_photo_clicked.emit(self)) def paint(self, painter: QPainter, option: PyQt5.QtWidgets.QStyleOptionGraphicsItem, widget: QWidget): if self.pixmap.pixmap().isNull(): return painter.setRenderHint(QPainter.Antialiasing, True) if self.show_stick_widgets: brush = QBrush(QColor(255, 255, 255, 100)) painter.fillRect(self.boundingRect(), brush) if self.mode and self.hovered: pen = QPen(QColor(0, 125, 200, 255)) pen.setWidth(4) painter.setPen(pen) def boundingRect(self) -> PyQt5.QtCore.QRectF: return self.pixmap.boundingRect().united( self.title_btn.boundingRect().translated(self.title_btn.pos())) def initialise_with(self, camera: Camera): if self.camera is not None: self.camera.stick_added.disconnect(self.handle_stick_created) self.camera.sticks_added.disconnect(self.handle_sticks_added) self.camera.stick_removed.disconnect(self.handle_stick_removed) self.camera.sticks_removed.disconnect(self.handle_sticks_removed) self.camera.stick_changed.disconnect(self.handle_stick_changed) self.camera = camera self.prepareGeometryChange() self.set_image(camera.rep_image, Path(camera.rep_image_path).name) self.title_btn.set_label(self.camera.folder.name) self.title_btn.set_height(46) self.title_btn.fit_to_contents() self.title_btn.set_width(int(self.boundingRect().width())) self.title_btn.setPos(0, self.boundingRect().height()) self.control_widget.title_btn.set_label(self.camera.folder.name) self.camera.stick_added.connect(self.handle_stick_created) self.camera.sticks_added.connect(self.handle_sticks_added) self.camera.stick_removed.connect(self.handle_stick_removed) self.camera.sticks_removed.connect(self.handle_sticks_removed) self.camera.stick_changed.connect(self.handle_stick_changed) self.control_widget.set_font_height(32) self.control_widget.set_widget_height( self.title_btn.boundingRect().height()) self.control_widget.set_widget_width(int(self.boundingRect().width())) self.control_widget.setPos(0, self.pixmap.boundingRect().height() ) #self.boundingRect().height()) self.control_widget.set_mode('view') self.update_stick_widgets() def set_image(self, img: Optional[np.ndarray] = None, image_name: Optional[str] = None): if img is None: self.show_overlay_message('not available') return self.show_overlay_message(None) self.prepareGeometryChange() barray = QByteArray(img.tobytes()) image = QImage(barray, img.shape[1], img.shape[0], QImage.Format_BGR888) self.original_pixmap = QPixmap.fromImage(image) self.pixmap.setPixmap(self.original_pixmap) self.highlight_rect.setRect(self.boundingRect()) self.current_image_name = image_name def update_stick_widgets(self): stick_length = 60 for stick in self.camera.sticks: sw = StickWidget(stick, self.camera, self) sw.set_mode(self.stick_widget_mode) self.connect_stick_widget_signals(sw) self.stick_widgets.append(sw) stick_length = stick.length_cm self.update_stick_box() self.scene().update() def scale_item(self, factor: float): self.prepareGeometryChange() pixmap = self.original_pixmap.scaledToHeight( int(self.original_pixmap.height() * factor)) self.pixmap.setPixmap(pixmap) self.__update_title() def set_show_stick_widgets(self, value: bool): for sw in self.stick_widgets: sw.setVisible(value) self.scene().update() def hoverEnterEvent(self, e: QGraphicsSceneHoverEvent): self.hovered = True self.scene().update(self.sceneBoundingRect()) def hoverLeaveEvent(self, e: QGraphicsSceneHoverEvent): self.hovered = False self.scene().update(self.sceneBoundingRect()) def mousePressEvent(self, e: QGraphicsSceneMouseEvent): super().mousePressEvent(e) def mouseReleaseEvent(self, e: QGraphicsSceneMouseEvent): if self.mode == 1: self.click_handler(self.camera) def mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent): if self.stick_widget_mode == StickMode.EditDelete: x = event.pos().toPoint().x() y = event.pos().toPoint().y() stick = self.camera.create_new_sticks( [(np.array([[x, y - 50], [x, y + 50]]), 3)], self.current_image_name)[ 0] #self.dataset.create_new_stick(self.camera) self.sticks_without_width.append(stick) def set_button_mode(self, click_handler: Callable[[Camera], None], data: str): self.mode = 1 # TODO make a proper ENUM self.click_handler = lambda c: click_handler(c, data) def set_display_mode(self): self.mode = 0 # TODO make a proper ENUM self.click_handler = None def _remove_stick_widgets(self): for sw in self.stick_widgets: sw.setParentItem(None) self.scene().removeItem(sw) sw.deleteLater() self.stick_widgets.clear() def handle_stick_created(self, stick: Stick): if stick.camera_id != self.camera.id: return sw = StickWidget(stick, self.camera, self) sw.set_mode(self.stick_widget_mode) self.connect_stick_widget_signals(sw) self.stick_widgets.append(sw) self.stick_widgets_out_of_sync.emit(self) self.update() def handle_stick_removed(self, stick: Stick): if stick.camera_id != self.camera.id: return stick_widget = next( filter(lambda sw: sw.stick.id == stick.id, self.stick_widgets)) self.disconnect_stick_widget_signals(stick_widget) self.stick_widgets.remove(stick_widget) stick_widget.setParentItem(None) self.scene().removeItem(stick_widget) stick_widget.deleteLater() self.update() def handle_sticks_removed(self, sticks: List[Stick]): if sticks[0].camera_id != self.camera.id: return for stick in sticks: to_remove: StickWidget = None for sw in self.stick_widgets: if sw.stick.id == stick.id: to_remove = sw break self.stick_widgets.remove(to_remove) to_remove.setParentItem(None) if self.scene() is not None: self.scene().removeItem(to_remove) to_remove.deleteLater() self.update() def handle_sticks_added(self, sticks: List[Stick]): if len(sticks) == 0: return if sticks[0].camera_id != self.camera.id: return for stick in sticks: sw = StickWidget(stick, self.camera, self) sw.set_mode(self.stick_widget_mode) self.connect_stick_widget_signals(sw) self.stick_widgets.append(sw) self.update_stick_box() self.stick_widgets_out_of_sync.emit(self) self.update() def connect_stick_widget_signals(self, stick_widget: StickWidget): stick_widget.delete_clicked.connect( self.handle_stick_widget_delete_clicked) stick_widget.stick_changed.connect(self.handle_stick_widget_changed) stick_widget.link_initiated.connect(self.handle_stick_link_initiated) stick_widget.right_clicked.connect( self.handle_stick_widget_context_menu) def disconnect_stick_widget_signals(self, stick_widget: StickWidget): stick_widget.delete_clicked.disconnect( self.handle_stick_widget_delete_clicked) stick_widget.stick_changed.disconnect(self.handle_stick_widget_changed) stick_widget.link_initiated.disconnect( self.handle_stick_link_initiated) stick_widget.right_clicked.disconnect( self.handle_stick_widget_context_menu) def handle_stick_widget_delete_clicked(self, stick: Stick): self.camera.remove_stick(stick) def set_stick_widgets_mode(self, mode: StickMode): self.stick_widget_mode = mode for sw in self.stick_widgets: sw.set_mode(mode) self.set_stick_edit_mode(mode == StickMode.Edit) def handle_stick_widget_changed(self, stick_widget: StickWidget): self.camera.stick_changed.emit(stick_widget.stick) def handle_stick_changed(self, stick: Stick): if stick.camera_id != self.camera.id: return sw = next( filter(lambda _sw: _sw.stick.id == stick.id, self.stick_widgets)) sw.adjust_line() sw.update_tooltip() def handle_stick_link_initiated(self, stick_widget: StickWidget): self.stick_link_requested.emit(stick_widget) def get_top_left(self) -> QPointF: return self.sceneBoundingRect().topLeft() def get_top_right(self) -> QPointF: return self.sceneBoundingRect().topRight() def highlight(self, color: Optional[QColor]): if color is None: self.highlight_animation.stop() self.highlight_rect.setVisible(False) return alpha = color.alpha() color.setAlpha(0) self.highlight_animation.setStartValue(color) self.highlight_animation.setEndValue(color) color.setAlpha(alpha) self.highlight_animation.setKeyValueAt(0.5, color) self.highlight_animation.setDuration(2000) self.highlight_animation.setLoopCount(-1) self.highlight_rect.setPen(QPen(color)) self.highlight_rect.setVisible(True) self.highlight_animation.start() @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 handle_highlight_color_changed(self, color: QColor): self.highlight_rect.setBrush(QBrush(color)) self.update() def handle_stick_widget_context_menu(self, sender: Dict[str, StickWidget]): self.stick_context_menu.emit(sender['stick_widget'], self) def show_overlay_message(self, msg: Optional[str]): if msg is None: self.overlay_message.setVisible(False) self.blur_eff.setEnabled(False) return self.overlay_message.setText(msg) self.overlay_message.setPos( self.pixmap.boundingRect().center() - QPointF(0.5 * self.overlay_message.boundingRect().width(), 0.5 * self.overlay_message.boundingRect().height())) self.overlay_message.setVisible(True) self.blur_eff.setEnabled(True) def show_status_message(self, msg: Optional[str]): if msg is None: self.control_widget.set_title_text(self.camera.folder.name) else: self.control_widget.set_title_text(msg) def update_stick_box(self): left = 9000 right = 0 top = 9000 bottom = -1 for stick in self.camera.sticks: left = min(left, min(stick.top[0], stick.bottom[0])) right = max(right, max(stick.top[0], stick.bottom[0])) top = min(top, min(stick.top[1], stick.bottom[1])) bottom = max(bottom, max(stick.top[1], stick.bottom[1])) left -= 100 right += 100 top -= 100 bottom += 100 self.stick_box.setRect(left, top, right - left, bottom - top) pen = QPen(QColor(0, 100, 200, 200)) pen.setWidth(2) pen.setStyle(Qt.DashLine) self.stick_box.setPen(pen) def set_stick_edit_mode(self, is_edit: bool): if is_edit: self.update_stick_box() self.stick_box_start_pos = self.stick_box.pos() for sw in self.stick_widgets: sw.setParentItem(self.stick_box) else: offset = self.stick_box.pos() - self.stick_box_start_pos for sw in self.stick_widgets: stick = sw.stick stick.translate(np.array([int(offset.x()), int(offset.y())])) sw.setParentItem(self) sw.set_stick(stick) self.stick_box.setParentItem(None) self.stick_box = QGraphicsRectItem(self) self.stick_box.setFlag(QGraphicsItem.ItemIsMovable, True) self.stick_box.setVisible(False) self.stick_box.setVisible(is_edit) def keyPressEvent(self, event: QKeyEvent) -> None: pass def keyReleaseEvent(self, event: QKeyEvent) -> None: if event.key() in [Qt.Key_Right, Qt.Key_Tab, Qt.Key_Space]: self.control_widget.next_photo_btn.click_button(True) elif event.key() in [Qt.Key_Left]: self.control_widget.prev_photo_btn.click_button(True) elif event.key() == Qt.Key_S: self.enter_pressed.emit()
def mainFunc(self, playing, scriptList, scriptPos): if self.inputImg is not None: self.outputImg = self.inputImg pass_ = 0 fx = 1.0 fy = 1.0 offset_x = 0.0 offset_y = 0.0 for i in range(scriptPos): widget = scriptList[i][1] if widget.objectName() == 'resize': fx *= widget.dblFx.value() fy *= widget.dblFx.value() pass_ += widget.pass_ for item in widget.graphicsItems: try: # ~ print('item.rect() ', item.rect()) if fx != 1.0: item.setScale(1 / fx) # ~ if fy != 1.0: # ~ item.setHeight(item.height()/fy) if offset_x: item.setX((item.x() + offset_x) / fx) # ~ item.setX( (item.x() + offset_x)/1.0 ) # ~ print('offset_x: ', offset_x, ' fx: ', fx) if offset_y: item.setY((item.y() + offset_y) / fy) # ~ item.setY( (item.y() + offset_y)/1.0 ) self.graphicsView.scene().addItem(item) item.setZValue(1) except RuntimeError as e: print(time.time(), 'Runtime Error show_graphics_items mainFunc') print(e) if widget.objectName() == 'circles': if widget.chkCrop.isChecked(): try: circle = widget.graphicsItems[0] # ~ circle = item offset_x = circle.x() offset_x = circle.rect().left() offset_y = circle.y() offset_y = circle.rect().top() # ~ print('offer setter ', offset_x) except IndexError as e: print(e) if self.chkShowPass.isChecked(): # ~ h = self.graphicsView.scene().height() # ~ w = self.graphicsView.scene().width() h = self.inputImg.shape[0] w = self.inputImg.shape[1] # ~ sfh = float(h)/float(self.graphicsView.minimumHeight()) # ~ sfw = float(w)/float(self.graphicsView.minimumWidth()) # ~ sf = min(sfh,sfw) outlineRect = QGraphicsRectItem(0, 0, w, h) outlineLine = QGraphicsLineItem(0, h, w, 0) font = QFont("DejaVu Sans", 45) if pass_ == scriptPos: # the overall result is pass text = 'PASS' pen = QPen(QColor(0, 255, 0), 8) #green for pass brush = QBrush(QColor(0, 255, 0)) else: # overall result is fail text = 'FAIL' pen = QPen(QColor(255, 0, 0), 8) #red for fail brush = QBrush(QColor(255, 0, 0)) outlineLine.setPen(pen) self.graphicsView.scene().addItem(outlineLine) outlineLine.setZValue(1) outlineRect.setPen(pen) self.graphicsView.scene().addItem(outlineRect) textItem = QGraphicsSimpleTextItem(text, ) textItem.setFont(font) textItem.setBrush(brush) textItem.setPos(w / 10.0, h / 10.0) self.graphicsView.scene().addItem(textItem) textItem.setZValue(1)
class VLineChartView(QChartView): bar_hovered = pyqtSignal(bool, str) def __init__(self, data: pd.DataFrame): super(VLineChartView, self).__init__() self._stocks = data self._category = self._stocks['trade_date'] self._chart = QChart() self._chart.setAnimationOptions(QChart.SeriesAnimations) self._series = QStackedBarSeries() # 成交量以万股为单位 self._vol_multiple = 10000 self.init_chart() self._zero_value = (0, self._chart.axisY().min()) self._max_value = (len(self._chart.axisX().categories()), self._chart.axisY().max()) self._zero_point = self._chart.mapToPosition( QPointF(self._zero_value[0], self._zero_value[1])) self._max_point = self._chart.mapToPosition( QPointF(self._max_value[0], self._max_value[1])) # 计算x轴单个cate的宽度,用来处理横线不能画到边界 self._cate_width = (self._max_point.x() - self._zero_point.x()) / len( self._category) self._series.hovered.connect(self.on_series_hovered) x_index_list = np.percentile(range(len(self._category)), [0, 25, 50, 75, 100]) self._x_axis_list = [ QGraphicsSimpleTextItem(self._category[int(index)], self._chart) for index in x_index_list ] [axis.setText(axis.text()[4:]) for axis in self._x_axis_list[1:]] self._v_b = QGraphicsSimpleTextItem('B', self._chart) self._v_b.setZValue(100) def on_series_hovered(self, status, index): self.bar_hovered.emit(status, self._category[index]) def clear_series_value(self): self._series.clear() self._stocks = None self._chart.axisY().setRange(0, 10) self._chart.axisX().setCategories(list()) def add_series_values(self, data: pd.DataFrame, is_init=False): self._stocks = data bar_red = QBarSet('red') bar_red.setColor(Qt.red) bar_green = QBarSet('green') bar_green.setColor(Qt.green) for _, stock in self._stocks.iterrows(): if stock['open'] < stock['close']: bar_red.append(stock['vol'] / self._vol_multiple) bar_green.append(0) else: bar_red.append(0) bar_green.append(stock['vol'] / self._vol_multiple) self._series.append(bar_red) self._series.append(bar_green) if not is_init: self._stocks = data self._category = self._stocks['trade_date'] axis_x = self._chart.axisX() axis_y = self._chart.axisY() axis_x.setCategories(self._category) max_p = self._stocks[[ 'vol', ]].stack().max() min_p = self._stocks[[ 'vol', ]].stack().min() axis_y.setRange(min_p / self._vol_multiple * 0.9, max_p / self._vol_multiple * 1.1) self._zero_value = (0, self._chart.axisY().min()) self._max_value = (len(self._chart.axisX().categories()), self._chart.axisY().max()) # 计算x轴单个cate的宽度,用来处理横线不能画到边界 self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(self._category) def resizeEvent(self, event): super(VLineChartView, self).resizeEvent(event) self._zero_point = self._chart.mapToPosition( QPointF(self._zero_value[0], self._zero_value[1])) self._max_point = self._chart.mapToPosition( QPointF(self._max_value[0], self._max_value[1])) self._cate_width = (self._max_point.x() - self._zero_point.x()) / len( self._category) # 绘制自定义X轴 self._x_axis_list[0].setPos(self._zero_point.x() - self._cate_width, self._zero_point.y() + 10) self._x_axis_list[1].setPos(self._max_point.x() * 0.25, self._zero_point.y() + 10) self._x_axis_list[2].setPos(self._max_point.x() * 0.5, self._zero_point.y() + 10) self._x_axis_list[3].setPos(self._max_point.x() * 0.75, self._zero_point.y() + 10) self._x_axis_list[4].setPos( self._max_point.x() - self._x_axis_list[-1].boundingRect().width(), self._zero_point.y() + 10) # 20180207 这个日期的柱形图上面画一个字母b vol = self._stocks[self._stocks['trade_date'] == '20180207']['vol'] / self._vol_multiple print('vol:', vol, ' trade_date:', '20180207') pos = self._chart.mapToPosition( QPointF(list(self._category).index('20180207'), vol)) pos = QPointF(pos.x() - self._cate_width / 2, pos.y() - self._v_b.boundingRect().height()) self._v_b.setPos(pos) def max_point(self): return QPointF(self._max_point.x() + self._cate_width / 2, self._max_point.y()) def min_point(self): return QPointF(self._zero_point.x() - self._cate_width / 2, self._zero_point.y()) def init_chart(self): self.add_series_values(self._stocks, True) self._chart.addSeries(self._series) self._chart.createDefaultAxes() self._chart.setLocalizeNumbers(True) axis_x = self._chart.axisX() axis_y = self._chart.axisY() axis_x.setGridLineVisible(False) axis_y.setGridLineVisible(False) axis_y.setLabelFormat("%.2f") axis_x.setCategories(self._category) axis_x.setLabelsVisible(False) max_p = self._stocks[[ 'vol', ]].stack().max() min_p = self._stocks[[ 'vol', ]].stack().min() axis_y.setRange(min_p / self._vol_multiple * 0.9, max_p / self._vol_multiple * 1.1) # chart的图例 legend = self._chart.legend() legend.hide() # 设置图例由Series来决定样式 # legend.setMarkerShape(QLegend.MarkerShapeFromSeries) self.setChart(self._chart) self._chart.layout().setContentsMargins(0, 0, 0, 0) # 设置内边界的bottom为0 # margins = self._chart.margins() # self._chart.setMargins(QMargins(margins.left(), 0, margins.right(), 0)) self._chart.setBackgroundRoundness(0)