def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) center = QPointF(self.width() / 2, self.height() / 2) radius = self.radius() ellipse_line_width = self.radius() * 60 / 512 # big ellipse pen = QPen() pen.setWidth(ellipse_line_width) pen.setColor(self.color) painter.setPen(pen) painter.drawEllipse(center, radius - pen.width() / 2, radius - pen.width() / 2) # dots pen = QPen() pen.setColor(QColor(0, 0, 0, 0)) painter.setPen(pen) brush = QBrush() brush.setStyle(Qt.SolidPattern) dot_size = radius * 200 / 1024 color = copy.copy(self.color) self._change_color(painter, brush, color, 0) painter.drawEllipse( QPointF((self.width() - radius - ellipse_line_width / 2) / 2, self.height() / 2), dot_size, dot_size) self._change_color(painter, brush, color, 15) painter.drawEllipse(center, dot_size, dot_size) self._change_color(painter, brush, color, 30) painter.drawEllipse( QPointF((self.width() + radius + ellipse_line_width / 2) / 2, self.height() / 2), dot_size, dot_size)
def draw_cursor(self, event_x, event_y, drawing_tool_radius, new_circle=False): """ Draws a blue circle where the user clicked. :param event_x: QGraphicsScene event attribute: event.scenePos().x() :param event_y: QGraphicsScene event attribute: event.scenePos().y() :param drawing_tool_radius: the current radius of the drawing tool :param new_circle: True when the circle object is being created rather than updated. """ self.draw_tool_radius = drawing_tool_radius self.current_cursor_x = event_x - self.draw_tool_radius self.current_cursor_y = event_y - self.draw_tool_radius if new_circle: self.cursor = QGraphicsEllipseItem(self.current_cursor_x, self.current_cursor_y, self.draw_tool_radius * 2, self.draw_tool_radius * 2) pen = QPen(QColor("blue")) pen.setWidth(0) self.cursor.setPen(pen) self.cursor.setZValue(1) self.addItem(self.cursor) elif self.cursor is not None: self.cursor.setRect(self.current_cursor_x, self.current_cursor_y, self.draw_tool_radius * 2, self.draw_tool_radius * 2)
def particle_pen(self, index: int): pen = QPen() colors = self.settings.list_value(default_settings.particle_color) colors = [QColor(c) for c in colors] pen.setColor(colors[index % len(colors)]) pen.setStyle(Qt.SolidLine) pen.setWidth(0) return pen
def create_icon(player_colour: QColor) -> QPixmap: size = 200 icon = QPixmap(size, size) icon.fill(Qt.transparent) painter = QPainter(icon) try: painter.setBrush(player_colour) pen = QPen() pen.setWidth(3) painter.setPen(pen) painter.drawEllipse(1, 1, size - 2, size - 2) finally: painter.end() return icon
def resizeEvent(self, event): bounds = self.scene.itemsBoundingRect() if bounds.width() <= 0 or bounds.height() <= 0: return # do nothing if size is zero self.view.fitInView(bounds, Qt.KeepAspectRatio) if self.rectangle is not None: self.scene.removeItem(self.rectangle) pen = QPen(Qt.green) pen.setWidth(0) pen.setStyle(Qt.DashLine) self.rectangle = self.scene.addRect( self.calculate_center_square(bounds), pen) self.view.centerOn(0, 0) self.view.raise_()
def trajectory_pen(self, index: int): if self.settings.boolean_value( default_settings.same_trajectory_color_with_particle): return self.particle_pen(index) else: pen = QPen() colors = self.settings.list_value( default_settings.trajectory_color) colors = [QColor(c) for c in colors] pen.setStyle(Qt.SolidLine) pen.setColor(colors[index % len(colors)]) pen.setWidth( self.settings.int_value(default_settings.trajectory_size)) pen.setJoinStyle(Qt.RoundJoin) pen.setCapStyle(Qt.RoundCap) return pen
def paint(self, painter, option, widget=None): qgp = self.getqgp() pen = QPen() pen.setWidth(2) qgp.setPen(pen) qgp.setBrush(QBrush(Qt.NoBrush)) painter.setClipRect(option.exposedRect) qgp.paint(painter, option, widget) lastp = self.points[-1] angle = radians(qgp.path().angleAtPercent(1.0)) angle = angle + pi p = lastp + QPointF( cos(angle - pi / 6.0) * 10, -sin(angle - pi / 6.0) * 10) q = lastp + QPointF( cos(angle + pi / 6.0) * 10, -sin(angle + pi / 6.0) * 10) painter.setBrush(QBrush(QColor("black"))) self.head = QPolygonF([lastp, p, q]) painter.drawPolygon(self.head)
def paintmap(self, surface): ww = surface.window().width() wh = surface.window().height() self.vb.setMaximum(self.qimg.height() - (wh // self.line.height)) self.line.x_map = ww - 32 r = QRect(self.line.x_map, 2, 32, wh - 4) surface.drawImage(r, self.qimg) factor = wh / self.qimg.height() pen = surface.pen() p = QPen(Qt.white) p.setCapStyle(Qt.RoundCap) p.setWidth(2) surface.setPen(p) l0 = self.vb.value() * factor l1 = l0 + (wh // self.line.height) * factor pad = 4 p1 = QPointF(self.line.x_map - pad, l0) p2 = QPointF(self.line.x_map - pad, l1) if l1 - l0 > 4: p.setWidth(2) surface.drawLine(p1, p2) surface.drawLine(p1, QPointF(self.line.x_map, l0)) surface.drawLine(p2, QPointF(self.line.x_map, l1)) else: p.setWidth(4) surface.drawLine(p1, p2) surface.setPen(pen)
def draw(self, painter: QPainter): render_display(self.display, painter) pen = QPen() pen.setWidth(self.width // 50) pen.setCapStyle(Qt.PenCapStyle.RoundCap) painter.setPen(pen)
def draw(self, art: QPixmap, painter: typing.Optional[QPainter] = None): filled_portion = 0.6 if self.is_shuffled and not self.row_clues else 0.9 scaled_art = art.scaled(self.rect.width() * filled_portion, self.rect.height() * filled_portion, Qt.AspectRatioMode.KeepAspectRatio) if painter is None: painter = QPainter(self.target) painter.fillRect(self.rect, QColor('white')) cell_height = round(scaled_art.height() / self.rows) vertical_padding = self.rect.height() - self.rows * cell_height row_padding = vertical_padding / self.rows cell_width = round(scaled_art.width() / self.cols) horizontal_padding = self.rect.width() - self.cols * cell_width col_padding = horizontal_padding / self.cols padding = min(row_padding, col_padding) left_border = self.cols * (col_padding - padding) / 2 top_border = self.rows * (row_padding - padding) / 2 font = painter.font() font.setPixelSize(padding / 2.6) painter.setFont(font) old_pen = painter.pen() grey_pen = QPen(QColor('lightgrey')) width = max(cell_width / 35, 2) grey_pen.setWidth(round(width)) y = top_border cell_index = 0 for i in range(self.rows): x = left_border for j in range(self.cols): si, sj, label = self.cells[cell_index] clue = self.clues.get(label.lower(), label) sx = sj * cell_width sy = si * cell_height painter.setPen(grey_pen) painter.drawRect(x + padding / 2, y, cell_width, cell_height) painter.setPen(old_pen) if self.is_shuffled: original_size = new_size = font.pixelSize() while True: # noinspection PyTypeChecker rect = painter.boundingRect(0, 0, cell_width, padding, Qt.AlignmentFlag.AlignLeft, clue) if (rect.width() <= cell_width + padding and rect.height() <= padding): break new_size *= 0.9 font.setPixelSize(new_size) painter.setFont(font) if not self.row_clues: painter.drawText(x, y + cell_height, cell_width + padding, padding, Qt.AlignmentFlag.AlignHCenter, clue) else: painter.drawPixmap(x + padding / 2, y, cell_width, cell_height, self.row_clues[si], 0, 0, 0, 0) painter.drawPixmap(x + padding / 2, y, cell_width, cell_height, self.column_clues[sj], 0, 0, 0, 0) font.setPixelSize(original_size) painter.setFont(font) painter.drawPixmap(x + padding / 2, y, cell_width, cell_height, scaled_art, sx, sy, cell_width, cell_height) x += cell_width + padding cell_index += 1 y += cell_height + padding
class HexAreaWidget(QWidget): ''' Responsible for painting the area that actually containts the hex display ''' signal_resized = Signal() signal_scroll_wheel_changed = Signal(int) signal_cursor_changed = Signal(int) signal_selection_updated = Signal(int) signal_key_cursor_pressed = Signal(KeyType) signal_key_selection_pressed = Signal(KeyType) signal_context_menu_shown = Signal(QPoint) signal_show_tooltip_at_offset = Signal(int, QPoint) signal_go_to_pointer_at_offset = Signal(int) def __init__(self, parent: QWidget): # , scroll_bar: QScrollBar, statusBar: QLabel super().__init__(parent=parent) self.line_height = 18 self.byte_width = 25 self.bytes_per_line = settings.get_bytes_per_line() self.label_offset_x = 5 self.label_length = 100 self.is_dragging_to_select = False self.display_data: List[DisplayByte] = [] self.display_labels: List[str] = [] # TODO make configurable self.font = QFont('DejaVu Sans Mono, Courier, Monospace', 12) self.label_color = QColor(128, 128, 128) self.byte_color = QColor(210, 210, 210) self.selection_color = QPen(QColor(97, 175, 239)) self.selection_color.setWidth(2) self.annotation_pen = QPen(QColor(0, 0, 0), 2) self.enabled_constraint_pen = QPen(QColor(255, 80, 0), 2) self.disabled_constraint_pen = QPen(QColor(130, 40, 0), 2) # Make this widget focussable on click, so that we can reduce the context of the shortcut, so that multiple shortcuts are possible in the same window self.setFocusPolicy(Qt.FocusPolicy.ClickFocus) def wheelEvent(self, event): # TODO make scroll speed configurable lines_delta = -int( event.angleDelta().y() / self.line_height) * self.bytes_per_line self.signal_scroll_wheel_changed.emit(lines_delta) def resizeEvent(self, event: QResizeEvent) -> None: self.signal_resized.emit() def paintEvent(self, ev): p = QPainter(self) p.setFont(self.font) #p.fillRect(self.rect(), QBrush(Qt.blue)) length = len(self.display_data) # Reduce to the number of lines that are available in the data num_rows = length // self.bytes_per_line if length % self.bytes_per_line > 0: num_rows += 1 for l in range(num_rows): p.setPen(self.label_color) # Draw address label # self.instance.get_local_label(self.start_offset + l * self.bytes_per_line) position_string = self.display_labels[l] p.drawText(QPoint(self.label_offset_x, (l + 1) * self.line_height), position_string) for i in range( 0, min(self.bytes_per_line, length - self.bytes_per_line * l)): #virtual_address = self.start_offset + l*self.bytes_per_line + i p.setPen(self.byte_color) current_byte = self.display_data[i + l * self.bytes_per_line] if current_byte.background is not None: p.setBackground(current_byte.background) p.setBackgroundMode(Qt.OpaqueMode) p.drawText( QPoint(self.label_length + i * self.byte_width, (l + 1) * self.line_height), current_byte.text) p.setBackgroundMode(Qt.TransparentMode) # Draw selection rects if current_byte.is_selected: p.setPen(self.selection_color) p.drawRect( # TODO make these offsets configurable/dependent on font? self.label_length + i * self.byte_width - 3, (l) * self.line_height + 3, self.byte_width, self.line_height) # Draw annotation underlines if len(current_byte.annotations) > 0: y_offset = 0 for annotation in current_byte.annotations: self.annotation_pen.setColor(annotation.color) p.setPen(self.annotation_pen) x = self.label_length + i * self.byte_width y = (l + 1) * self.line_height + y_offset + 2 p.drawLine(x, y, x + self.byte_width, y) y_offset += 2 # Draw constraint pipes if len(current_byte.constraints) > 0: enabled = False for constraint in current_byte.constraints: if constraint.enabled: enabled = True break if enabled: p.setPen(self.enabled_constraint_pen) else: p.setPen(self.disabled_constraint_pen) x = self.label_length + i * self.byte_width - 2 y = (l) * self.line_height + 3 p.drawLine(x, y, x, y + self.line_height) def number_of_lines_on_screen(self): # +1 to draw cutof lines as well return int(self.height() // self.line_height) + 1 def contextMenuEvent(self, event: QContextMenuEvent) -> None: self.signal_context_menu_shown.emit(event.globalPos()) def mousePressEvent(self, event: QMouseEvent) -> None: if event.button() == Qt.LeftButton: # TODO handle click on label, etc offset = self.xy_to_offset(event.x(), event.y()) if offset is not None: ctrl = event.modifiers( ) & Qt.ControlModifier == Qt.ControlModifier shift = event.modifiers( ) & Qt.ShiftModifier == Qt.ShiftModifier if ctrl: # Go to pointer self.signal_go_to_pointer_at_offset.emit(offset) return if shift: # Move selection instead of cursor self.signal_selection_updated.emit(offset) self.is_dragging_to_select = True return self.signal_cursor_changed.emit(offset) self.is_dragging_to_select = True def mouseMoveEvent(self, event: QMouseEvent) -> None: if self.is_dragging_to_select: offset = self.xy_to_offset(event.x(), event.y()) if offset is not None: self.signal_selection_updated.emit(offset) def mouseReleaseEvent(self, event: QMouseEvent) -> None: self.is_dragging_to_select = False def xy_to_offset(self, x, y) -> Optional[int]: ''' Returns offsets in bytes from the start_offset or None if not clicked on a byte ''' line = y // self.line_height start = self.label_offset_x + self.label_length if x < start or x > start + self.bytes_per_line * self.byte_width: return None col = (x - start) // (self.byte_width) return line * self.bytes_per_line + col def keyPressEvent(self, event: QKeyEvent) -> None: shift = event.modifiers() & Qt.ShiftModifier == Qt.ShiftModifier key = event.key() if key == Qt.Key_Up: if shift: self.signal_key_selection_pressed.emit(KeyType.UP) else: self.signal_key_cursor_pressed.emit(KeyType.UP) elif key == Qt.Key_Down: if shift: self.signal_key_selection_pressed.emit(KeyType.DOWN) else: self.signal_key_cursor_pressed.emit(KeyType.DOWN) elif key == Qt.Key_Left: if shift: self.signal_key_selection_pressed.emit(KeyType.LEFT) else: self.signal_key_cursor_pressed.emit(KeyType.LEFT) elif key == Qt.Key_Right: if shift: self.signal_key_selection_pressed.emit(KeyType.RIGHT) else: self.signal_key_cursor_pressed.emit(KeyType.RIGHT) elif key == Qt.Key_PageUp: if shift: self.signal_key_selection_pressed.emit(KeyType.PAGE_UP) else: self.signal_key_cursor_pressed.emit(KeyType.PAGE_UP) elif key == Qt.Key_PageDown: if shift: self.signal_key_selection_pressed.emit(KeyType.PAGE_DOWN) else: self.signal_key_cursor_pressed.emit(KeyType.PAGE_DOWN) elif key == Qt.Key_Home: if shift: self.signal_key_selection_pressed.emit(KeyType.HOME) else: self.signal_key_cursor_pressed.emit(KeyType.HOME) elif key == Qt.Key_End: if shift: self.signal_key_selection_pressed.emit(KeyType.END) else: self.signal_key_cursor_pressed.emit(KeyType.END) def event(self, event: QEvent) -> bool: if event.type() == QEvent.ToolTip: offset = self.xy_to_offset(event.pos().x(), event.pos().y()) if offset is None: QToolTip.hideText() return True self.signal_show_tooltip_at_offset.emit(offset, event.globalPos()) return True return super().event(event)