def contains(self, p: QPoint): cx = self.pos.x() + self.size / 2.0 cy = self.pos.y() + self.size / 2.0 dx = p.x() - cx dy = p.y() - cy if math.sqrt(dx * dx + dy * dy) <= self.size + 1: return True return False
def paintEvent(self, ev): offset = QPoint(0, 0) p = QPainter(self) p.setClipRect(ev.rect()) bottom = self.rect().bottom() if self.results: for i, (prefix, full, text) in enumerate(self.results): size = prefix.size() if offset.y() + size.height() > bottom: break self.max_result = i offset.setX(0) if i in (self.current_result, self.mouse_hover_result): p.save() if i != self.current_result: p.setPen(Qt.DotLine) p.drawLine(offset, QPoint(self.width(), offset.y())) p.restore() offset.setY(offset.y() + self.MARGIN // 2) p.drawStaticText(offset, prefix) offset.setX(self.maxwidth + 5) p.drawStaticText(offset, self.divider) offset.setX(offset.x() + self.divider.size().width()) p.drawStaticText(offset, full) offset.setY(offset.y() + size.height() + self.MARGIN // 2) if i in (self.current_result, self.mouse_hover_result): offset.setX(0) p.save() if i != self.current_result: p.setPen(Qt.DotLine) p.drawLine(offset, QPoint(self.width(), offset.y())) p.restore() else: p.drawText(self.rect(), Qt.AlignCenter, _('No results found')) p.end()
class Block(QWidget): border_color = QColor(137, 117, 89) border_pen = QPen(border_color, 2) selected_pen = QPen(border_color.lighter().lighter(), 2) padding = 5 def __init__(self, type_name: str, name: str, parent: QWidget=None): QWidget.__init__(self, parent) self.settings = {"Name": Setting("Name", StringValue(name), parent=self)} self.outputs = {} self.inputs = {} self.__type_name = type_name self._bg_color = QColor(159, 160, 144, 255) self._fg_color = QColor(255, 255, 255) self._resizable = True self.setMinimumSize(90, 120) self.__origin = QPoint(0, 0) self.__action = Action.NONE self.__status = Mode.EDIT_LOGIC self.__selected = False self.__line = None self.__label = None self.setMouseTracking(True) if self._resizable: self.__init_corner() def remove_connections(self): for k in self.inputs: self.inputs[k].delete() for k in self.outputs: self.outputs[k].delete() def delete(self): self.remove_connections() def name(self): return self.settings["Name"].data() def type_name(self): return self.__type_name def mode(self): return self.__status def action(self): return self.__action def setVisible(self, val): QWidget.setVisible(self, val) self.update_nodes() def set_mode(self, mode): self.__status = mode if (mode == Mode.EDIT_LOGIC or mode == Mode.DEBUG) and not self.isVisible(): self.setVisible(True) elif (mode == Mode.RUN or mode == Mode.EDIT_GUI) and self.isVisible(): self.setVisible(False) def __init_corner(self): path = QPainterPath() path.moveTo(-Block.padding, -15 - Block.padding) path.lineTo(-15 - Block.padding, -Block.padding) path.lineTo(-Block.padding, -Block.padding) path.closeSubpath() self.__corner_path = path def _add_input(self, name: str, t: Type): if not name in self.inputs: x = 1 y = 40 + Block.padding + len(self.inputs) * 13 self.inputs[name] = Input(t, QPoint(x, y), self) def _add_output(self, name: str, t: Type): if not name in self.outputs: x = self.width() - 10 y = 40 + Block.padding + len(self.outputs) * 13 self.outputs[name] = Output(t, QPoint(x, y), self) def _add_setting(self, name, value, constant=False): self.settings[name] = Setting(name, value, constant, self) def bg(self): return self._bg_color def title_bg(self): return self._bg_color.light(80) def selected(self): return self.__selected def set_selected(self, selected): self.__selected = selected def deselect(self): if self.__selected: self.set_selected(False) eff = self.graphicsEffect() del eff self.setGraphicsEffect(None) def select(self): if not self.__selected: self.set_selected(True) effect = QGraphicsDropShadowEffect() effect.setBlurRadius(20) effect.setXOffset(0) effect.setYOffset(0) effect.setColor(QColor(0, 0, 0, 180)) self.setGraphicsEffect(effect) self.raise_() def pen(self): return Block.selected_pen if self.__selected else Block.border_pen def _paint(self, p: QPainter): self._paint_bg(p) self._paint_title(p) p.setPen(QPen(self.pen().brush(), 1)) self._paint_ins(p) self._paint_outs(p) self._paint_content(p) def _paint_bg(self, p: QPainter): pen = Block.selected_pen if self.__selected else Block.border_pen p.setRenderHint(QPainter.Antialiasing, True) p.setPen(pen) p.setBrush(self.bg()) p.drawRoundedRect(Block.padding, Block.padding, self.width() - 2 * Block.padding, self.height() - 2 * Block.padding, 8, 8) p.setBrush(self.title_bg()) p.drawRoundedRect(Block.padding, Block.padding, self.width() - 2 * Block.padding, 35 + Block.padding, 8, 8) p.setBrush(self.bg()) p.setPen(QColor(0, 0, 0, 0)) p.drawRect(1 + Block.padding, 35 + Block.padding, self.width() - 2 - 2 * Block.padding, 10) p.setPen(pen) if self._resizable: p.setBrush(pen.brush()) p.drawPath(self.__corner_path.translated(self.width(), self.height())) def _paint_title(self, p: QPainter): p.drawLine(Block.padding, 35 + Block.padding, self.width() - Block.padding, 35 + Block.padding) p.setPen(self._fg_color) f = p.font() f.setPointSize(10) f.setBold(True) p.setFont(f) p.drawText(QRectF(4 + Block.padding, Block.padding + 2, self.width() - 12, 25), str(self.settings["Name"].value())) f.setBold(False) f.setPointSize(8) p.setPen(QColor(self._fg_color.red(), self._fg_color.green(), self._fg_color.blue(), 100)) p.setFont(f) p.drawText(QRectF(4 + Block.padding, 18 + Block.padding, self.width() - 12, 15), str(self.__type_name)) def _paint_ins(self, p: QPainter): for i in self.inputs: self.inputs[i].paint(p) def _paint_outs(self, p: QPainter): for i in self.outputs: self.outputs[i].paint(p) def _paint_content(self, p: QPainter): # nothing to do return def paintEvent(self, e: QPaintEvent): if e.isAccepted(): p = QPainter(self) self._paint(p) def _check_action(self, action): if self.__action != Action.NONE and action != Action.NONE: return False return True def node(self, p): for k in self.inputs: i = self.inputs[k] if i.contains(p): return i for k in self.outputs: o = self.outputs[k] if o.contains(p): return o return None def node_name(self, n): for k in self.inputs: i = self.inputs[k] if n == i: return k for k in self.outputs: o = self.outputs[k] if o == n: return k return '' def __create_line(self, n): l = n.get_line() self.parent().add_line(l) return l def mousePressEvent(self, e: QMouseEvent): self.parent().select(self) n = self.node(e.pos()) if n is not None: self.__line = self.__create_line(n) self.__action = Action.CONNECTING if self.__label is not None and self.__label.node() is not n: self.parent().delete_label(self.__label) self.__label = None return if self.__label is not None: self.parent().delete_label(self.__label) self.__label = None if self._resizable: if abs(e.x() - self.width()) < 8 + Block.padding \ and abs(e.y() - self.height()) < 8 + Block.padding \ and self._check_action(Action.RESIZE): self.__origin = e.pos() self.__action = Action.RESIZE return if self._check_action(Action.DRAG): self.__origin = e.pos() self.__action = Action.DRAG def mouseMoveEvent(self, e: QMouseEvent): if self.__action == Action.DRAG: dx = e.x() - self.__origin.x() dy = e.y() - self.__origin.y() self.set_pos(self.x() + dx, self.y() + dy) elif self.__action == Action.RESIZE: self.set_size(e.x(), e.y()) elif self.__action == Action.CONNECTING and self.__line is not None: p = QPoint(e.x() + self.x(), e.y() + self.y()) n = self.parent().get_node(p) if n is not None and n.compatible(self.__line.n1): self.__line.status(True) else: self.__line.status(False) self.__line.update(p) else: n = self.node(e.pos()) if self.__label is None and n is not None: self.__label = self.parent().create_label(self.node_name(n), n) elif self.__label is not None and self.__label.node() is not n and n is not None: self.parent().delete_label(self.__label) self.__label = self.parent().create_label(self.node_name(n), n) elif n is None: self.parent().delete_label(self.__label) self.__label = None def mouseReleaseEvent(self, e: QMouseEvent): if self.__action == Action.CONNECTING and self.__line is not None: self.parent().check_line(self.__line) self.__line = None self.__action = Action.NONE if self.__label is not None: self.parent().delete_label(self.__label) self.__label = None def leaveEvent(self, e): QWidget.leaveEvent(self, e) if self.__label is not None: self.parent().delete_label(self.__label) self.__label = None def mouseDoubleClickEvent(self, e: QMouseEvent): self._double_click() def _double_click(self): # to be defined in the sub blocks return def set_pos(self, x: int, y: int): xx = x if x >= 0 else 0 yy = y if y >= 0 else 0 if xx + self.width() > self.parent().width(): xx = self.parent().width() - self.width() if yy + self.height() > self.parent().height(): yy = self.parent().height() - self.height() self.setGeometry(xx, yy, self.width(), self.height()) def set_size(self, w: int, h: int): if self._resizable: width = w if w >= self.minimumWidth() else self.minimumWidth() height = h if h >= self.minimumHeight() else self.minimumHeight() self.setGeometry(self.x(), self.y(), width, height) def setGeometry(self, *__args): QWidget.setGeometry(self, *__args) self.update_nodes() def update_nodes(self): x = self.width() - 5 - Block.padding for k in self.outputs: o = self.outputs[k] o.pos.setX(x) o.set_visible(self.isVisible()) o.update() for k in self.inputs: i = self.inputs[k] i.set_visible(self.isVisible()) i.update() def _corner_path(self): return self.__corner_path
class PaintBoard(QWidget): # Define virtual panel coordinates for different shapes/regions VPCoord_Start = [316, 332] #LeftTopX, Y VPCoord_Circle = [316, 332, 336, 363] #LeftTopX, Y, RightBotX, Y VPCoord_Rect = [336, 332, 356, 363] #LeftTopX, Y, RightBotX, Y VPCoord_Tri = [316, 363, 336, 395] #LeftTopX, Y, RightBotX, Y VPCoord_Line = [336, 363, 356, 395] #LeftTopX, Y, RightBotX, Y # A flag to check if the user is currently using the virtual panel usingVP = False def __init__(self, sizeX, sizeY, Parent=None): ''' Constructor ''' super(PaintBoard, self).__init__(Parent) self.__InitData(sizeX, sizeY) #Initialize Data first, then interface/view self.__InitView() print("Init PaintBoard") def __InitView(self): self.setFixedSize(self.__size) def __InitData(self, sizeX, sizeY): self.__size = QSize(sizeX, sizeY) self.__board = QPixmap( self.__size) # Make a new QPixmap as paint board,350px * 250px self.__board.fill(Qt.white) #Fill the paint board with white self.__IsEmpty = True #board is empty by default self.EraserMode = False #eraser mode is disabled by default self.__lastPos = None self.__currentPos = QPoint(0, 0) self.__painter = QPainter() self.__thickness = 1 #default pen thickness is 1 self.__penColor = QColor("black") #default color is black self.__colorList = QColor.colorNames() #get the list of colors def Clear(self): #Clear the board self.__board.fill(Qt.white) self.update() self.__IsEmpty = True def ChangePenColor(self, color="black"): self.__penColor = QColor(color) def ChangePenThickness(self, thickness=1): self.__thickness = thickness def IsEmpty(self): #Is the board empty return self.__IsEmpty def GetContentAsQImage(self): #return the content of the board (return QImage) image = self.__board.toImage() return image def paintEvent(self, paintEvent): self.__painter.begin(self) self.__painter.drawPixmap(0, 0, self.__board) self.__painter.end() # print("inside paintEvent") def penPressEvent(self, pos): self.__currentPos = QPoint(pos[0], pos[1]) self.__lastPos = self.__currentPos def penMoveEvent(self, pos, pressure): pen_x = pos[0] pen_y = pos[1] pen_pressure = pressure if self.__lastPos is None: self.__lastPos = QPoint(pen_x, pen_y) elif (abs(pen_x - self.__lastPos.x()) > 21 or abs(pen_y - self.__lastPos.y()) > 21): self.__lastPos = QPoint(pen_x, pen_y) self.__currentPos = QPoint(pen_x, pen_y) self.__painter.begin(self.__board) if self.EraserMode == False: #Non-Eraser mode self.__painter.setPen(QPen( self.__penColor, self.__thickness)) #Set pen color, thickness else: #Eraser mode: pen color is white, thickness is 6 self.__painter.setPen(QPen(Qt.white, 6)) self.__painter.drawLine(self.__lastPos, self.__currentPos) self.__painter.end() self.__lastPos = self.__currentPos self.update() #Show updates # Virtual Panel event def penVPEvent(self, pos, pressure): pass ''' # Check if the pressure is over 500 if(pen_pressure > 400): # Check which region the pen is in and prepare to draw shape accordingly if(pen_x < self.VPCoord_Circle[2] and pen_y < self.VPCoord_Circle[3]): print("A") elif(pen_x < self.VPCoord_Rect[2] and pen_y < self.VPCoord_Rect[3]): print("B") elif(pen_x < self.VPCoord_Tri[2] and pen_y < self.VPCoord_Tri[3]): print("C") elif(pen_x < self.VPCoord_Line[2] and pen_y < self.VPCoord_Line[3]): print("D") ''' def penReleaseEvent(self, pos): self.__IsEmpty = False #board is not empty def paintEllipse(self, center_x, center_y, radias1, radias2): self.__painter.begin(self.__board) self.__painter.setPen(QPen(self.__penColor, self.__thickness)) self.__painter.drawEllipse(QPoint(center_x, center_y), radias1, radias2) self.__painter.end() self.update() #Show updates def paintRect(self, center_x, center_y, upper_left_x, upper_left_y): width = abs(2 * (center_x - upper_left_x)) height = abs(2 * (center_y - upper_left_y)) self.__painter.begin(self.__board) self.__painter.setPen(QPen(self.__penColor, self.__thickness)) self.__painter.drawRect(upper_left_x, upper_left_y, width, height) self.__painter.end() self.update() #Show updates def paintTriangle(self, points): self.__painter.begin(self.__board) self.__painter.setPen(QPen(self.__penColor, self.__thickness)) self.__painter.drawPolygon(points) self.__painter.end() self.update() #Show updates def paintLine(self, P1_x, P1_y, P2_x, P2_y): P1 = QPoint(P1_x, P1_y) P2 = QPoint(P2_x, P2_y) self.__painter.begin(self.__board) self.__painter.setPen(QPen(self.__penColor, self.__thickness)) self.__painter.drawLine(P1, P2) self.__painter.end() self.update() #Show updates def paintArc(self, center_x, center_y, start_x, start_y, end_x, end_y): radius = math.sqrt( math.pow(center_x - start_x, 2) + math.pow(center_y - start_y, 2)) rect = QRectF(center_x - radius, center_y - radius, radius * 2, radius * 2) startAngle = 16 * math.atan2(start_x - center_y, start_x - center_x) * 180.0 / math.pi endAngle = 16 * math.atan2(end_y - center_y, end_x - center_x) * 180.0 / math.pi spanAngle = endAngle - startAngle self.__painter.begin(self.__board) self.__painter.setPen(QPen(self.__penColor, self.__thickness)) self.__painter.drawArc(rect, startAngle, spanAngle) self.__painter.end() self.update() #Show updates def paintBezierSpline(self, pointListX, pointListY): P1 = QPoint(int(pointListX[0]), int(pointListY[0])) path = QtGui.QPainterPath() path.moveTo(P1) self.__painter.begin(self.__board) self.__painter.setPen(QPen(self.__penColor, self.__thickness)) i = 0 while i < len(pointListX) - 3: P2 = QPoint(int(pointListX[i + 1]), int(pointListY[i + 1])) P3 = QPoint(int(pointListX[i + 2]), int(pointListY[i + 2])) P4 = QPoint(int(pointListX[i + 3]), int(pointListY[i + 3])) path.cubicTo(P2, P3, P4) self.__painter.drawPath(path) i += 3 self.__painter.end() self.update() #Show updates