def drawAction(self, painter, actionName, pixmap, left, top, highlightedColor, drawDisclosure, borderColor): if (pixmap != None): iconRect = QRect(left, top, self.ICON_WIDTH, self.ICON_WIDTH) if highlightedColor is not None: painter.fillRect(iconRect, highlightedColor) # draw the icon. Its opacity depends on mouse over. p = self.treeView().mapFromGlobal(QCursor.pos()) if not iconRect.contains(p): painter.setOpacity(0.7) else: painter.setOpacity(1.0) self.lastHitAction = actionName self.drawPixmap(painter, pixmap, left, top) painter.setOpacity(1.0) if drawDisclosure: painter.drawPixmap(iconRect, self.DISCLOSURE_IMAGE) if borderColor: oldPen = painter.pen() painter.setPen(QPen(borderColor, 1)) painter.drawRect(iconRect) painter.setPen(oldPen)
def resize_visible_rows(self): viewport_rect = QRect(QPoint(0, 0), self.viewport().size()) for row in range(self.model().rowCount()): rect = self.visualRect(self.model().index(row, 0)) is_visible = viewport_rect.contains(rect) if is_visible: self.resizeRowToContents(row)
def drawIcon(self, painter: QPainter, rect: QRect, icon: int) -> None: """ """ if icon != 0: if rect.contains(self.__lastPoint): painter.setPen(self.__iconPressColor if self.__pressed else self.__iconHoverColor) else: painter.setPen(self.__iconNormalColor) painter.drawText(rect, Qt.AlignHCenter | Qt.AlignVCenter, chr(int(icon)))
def updateLineNumberArea(self, rect: QRect, dy: int = 0): # todo: move this into LineNumberArea if dy: self.lineNumberArea.scroll(0, dy) else: self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) if rect.contains(self.viewport().rect()): self.setViewportMargins(self.lineNumberArea.numberWidth(), 0, 0, 0)
def _object_in_jump_area(level: Level, level_object: LevelObject): for jump in level.jumps: screen = jump.screen_index if level.is_vertical: rect = QRect(0, SCREEN_WIDTH * screen, SCREEN_WIDTH, SCREEN_HEIGHT,) else: rect = QRect(SCREEN_WIDTH * screen, 0, SCREEN_WIDTH, GROUND,) if rect.contains(QPoint(*level_object.get_position())): return True else: return False
def _update_game_snap(self, game_pos: QRect): # If the top left spot of us fall into game window, then we move with the game tl = self.geometry().topLeft() if not game_pos.contains(tl): self.__last_game_pos = game_pos return dx = game_pos.x() - self.__last_game_pos.x() dy = game_pos.y() - self.__last_game_pos.y() pos = self.pos() pos.setX(pos.x() + dx) pos.setY(pos.y() + dy) self.move(pos) self.__last_game_pos = game_pos
def mouseReleaseEvent(self, e): # rubberBand selection if e.button() == Qt.RightButton: return self.rubberBand.hide() grid = self.scene().grid screenOrigin = e.screenPos() - e.pos().toPoint() rubberRect = QRect(self.origin, e.screenPos()).normalized() for i in range(grid.size): for j in range(grid.size): if rubberRect.contains((grid.gridNodes[i][j].pos() + screenOrigin).toPoint()): grid.gridNodes[i][j].setSelected(True) else: if type(grid.gridNodes[i][j].parentItem()) is nodeGroup: grid.gridNodes[i][j].parentItem().setSelected(False) grid.gridNodes[i][j].setSelected(False) # pick color from self.QImg p = e.pos().toPoint() c = QColor(self.QImg.pixel(p - self.offset().toPoint())) r, g, b, _ = c.getRgb() self.onMouseRelease(p, r, g, b)
def _drawWarning(self, painter, rect, item): warning = item.data(renderSetupRoles.NODE_WARNING) if warning and len(warning) > 0: fm = QFontMetrics(self.treeView().font()) if item.type() == renderSetup.RENDER_OVERRIDE_TYPE: left = self.getTextRect( rect, item).right() + baseDelegate.BaseDelegate.ACTION_BORDER else: left = self.getTextRect(rect, item).left() + fm.boundingRect( item.data(Qt.DisplayRole)).width( ) + baseDelegate.BaseDelegate.ACTION_BORDER top = rect.top() + baseDelegate.BaseDelegate.ICON_TOP_OFFSET painter.drawPixmap(left, top, baseDelegate.BaseDelegate.WARNING_IMAGE) iconRect = QRect(left, top, baseDelegate.BaseDelegate.WARNING_ICON_WIDTH, baseDelegate.BaseDelegate.WARNING_ICON_WIDTH) p = self.treeView().mapFromGlobal(QCursor.pos()) if iconRect.contains(p): item.setToolTip(warning)
def intersect_node(node: Node, pos: QPoint): node_rect = QRect(node.metadata['PositionX'], node.metadata['PositionY'], node_width, node_header_height) return node_rect.contains(pos)
class LevelObject(ObjectLike): def __init__( self, data: bytearray, object_set: int, palette_group, pattern_table: PatternTable, objects_ref: List["LevelObject"], is_vertical: bool, index: int, size_minimal: bool = False, ): self.object_set = ObjectSet(object_set) self.pattern_table = pattern_table self.tsa_data = ROM.get_tsa_data(object_set) self.x_position = 0 self.y_position = 0 self.rendered_base_x = 0 self.rendered_base_y = 0 self.palette_group = palette_group self.index_in_level = index self.objects_ref = objects_ref self.vertical_level = is_vertical self.data = data self.selected = False self.size_minimal = size_minimal if self.size_minimal: self.ground_level = 0 else: self.ground_level = GROUND self._setup() def _setup(self): data = self.data # where to look for the graphic data? self.domain = (data[0] & 0b1110_0000) >> 5 # position relative to the start of the level (top) self.original_y = data[0] & 0b0001_1111 self.y_position = self.original_y # position relative to the start of the level (left) self.original_x = data[1] self.x_position = self.original_x if self.vertical_level: offset = (self.x_position // SCREEN_WIDTH) * SCREEN_HEIGHT self.y_position += offset self.x_position %= SCREEN_WIDTH # describes what object it is self._obj_index = 0x00 self.obj_index = data[2] object_data = self.object_set.get_definition_of(self.type) self.width = object_data.bmp_width self.height = object_data.bmp_height self.orientation = object_data.orientation self.ending = object_data.ending self.description = object_data.description self.blocks = [int(block) for block in object_data.rom_object_design] self.block_cache = {} self.is_4byte = object_data.is_4byte if self.is_4byte and len(self.data) == 3: self.data.append(0) elif not self.is_4byte and len(data) == 4: del self.data[3] self._length = 0 self.secondary_length = 0 self._calculate_lengths() self.rect = QRect() self._render() @property def obj_index(self): return self._obj_index @obj_index.setter def obj_index(self, value): self._obj_index = value self.is_single_block = self.obj_index <= 0x0F domain_offset = self.domain * 0x1F if self.is_single_block: self.type = self.obj_index + domain_offset else: self.type = (self.obj_index >> 4) + domain_offset + 16 - 1 @property def length(self): return self._length @length.setter def length(self, value): if not self.is_4byte and not self.is_single_block: self._obj_index &= 0xF0 self._obj_index |= value & 0x0F self._length = value def _calculate_lengths(self): if self.is_single_block: self._length = 1 else: self._length = self.obj_index & 0b0000_1111 if self.is_4byte: self.secondary_length = self.length self.length = self.data[3] def render(self): self._render() def _render(self): self.rendered_base_x = base_x = self.x_position self.rendered_base_y = base_y = self.y_position self.rendered_width = new_width = self.width self.rendered_height = new_height = self.height try: self.index_in_level = self.objects_ref.index(self) except ValueError: # the object has not been added yet, so stick with the one given in the constructor pass blocks_to_draw = [] if self.orientation == TO_THE_SKY: base_x = self.x_position base_y = SKY for _ in range(self.y_position): blocks_to_draw.extend(self.blocks[0:self.width]) blocks_to_draw.extend(self.blocks[-self.width:]) elif self.orientation == DESERT_PIPE_BOX: # segments are the horizontal sections, which are 8 blocks long # two of those are drawn per length bit # rows are the 4 block high rows Mario can walk in is_pipe_box_type_b = self.obj_index // 0x10 == 4 rows_per_box = self.height lines_per_row = 4 segment_width = self.width segments = (self.length + 1) * 2 box_height = lines_per_row * rows_per_box new_width = segments * segment_width new_height = box_height for row_number in range(rows_per_box): for line in range(lines_per_row): if is_pipe_box_type_b and row_number > 0 and line == 0: # in pipebox type b we do not repeat the horizontal beams line += 1 start = line * segment_width stop = start + segment_width for segment_number in range(segments): blocks_to_draw.extend(self.blocks[start:stop]) if is_pipe_box_type_b: # draw another open row start = segment_width else: # draw the first row again to close the box start = 0 stop = start + segment_width for segment_number in range(segments): blocks_to_draw.extend(self.blocks[start:stop]) elif self.orientation in [ DIAG_DOWN_LEFT, DIAG_DOWN_RIGHT, DIAG_UP_RIGHT, DIAG_WEIRD ]: if self.ending == UNIFORM: new_height = (self.length + 1) * self.height new_width = (self.length + 1) * self.width left = [BLANK] right = [BLANK] slopes = self.blocks elif self.ending == END_ON_TOP_OR_LEFT: new_height = (self.length + 1) * self.height new_width = (self.length + 1) * (self.width - 1 ) # without fill block if self.orientation in [DIAG_DOWN_RIGHT, DIAG_UP_RIGHT]: fill_block = self.blocks[0:1] slopes = self.blocks[1:] left = fill_block right = [BLANK] elif self.orientation == DIAG_DOWN_LEFT: fill_block = self.blocks[-1:] slopes = self.blocks[0:-1] right = fill_block left = [BLANK] else: fill_block = self.blocks[0:1] slopes = self.blocks[1:] right = [BLANK] left = fill_block elif self.ending == END_ON_BOTTOM_OR_RIGHT: new_height = (self.length + 1) * self.height new_width = (self.length + 1) * (self.width - 1 ) # without fill block fill_block = self.blocks[-1:] slopes = self.blocks[0:-1] left = [BLANK] right = fill_block else: # todo other two ends not used with diagonals? print(self.description) self.rendered_blocks = [] return rows = [] if self.height > self.width: slope_width = self.width else: slope_width = len(slopes) for y in range(new_height): amount_right = (y // self.height) * slope_width amount_left = new_width - slope_width - amount_right offset = y % self.height rows.append(amount_left * left + slopes[offset:offset + slope_width] + amount_right * right) if self.orientation in [DIAG_UP_RIGHT]: for row in rows: row.reverse() if self.orientation in [DIAG_DOWN_RIGHT, DIAG_UP_RIGHT]: if not self.height > self.width: # special case for 60 degree platform wire down right rows.reverse() if self.orientation in [DIAG_UP_RIGHT]: base_y -= new_height - 1 if self.orientation in [DIAG_DOWN_LEFT]: base_x -= new_width - slope_width for row in rows: blocks_to_draw.extend(row) elif self.orientation in [PYRAMID_TO_GROUND, PYRAMID_2]: # since pyramids grow horizontally in both directions when extending # we need to check for new ground every time it grows base_x += 1 # set the new base_x to the tip of the pyramid for y in range(base_y, self.ground_level): new_height = y - base_y new_width = 2 * new_height bottom_row = QRect(base_x, y, new_width, 1) if any([ bottom_row.intersects(obj.get_rect()) and y == obj.get_rect().top() for obj in self.objects_ref[0:self.index_in_level] ]): break base_x = base_x - (new_width // 2) blank = self.blocks[0] left_slope = self.blocks[1] left_fill = self.blocks[2] right_fill = self.blocks[3] right_slope = self.blocks[4] for y in range(new_height): blank_blocks = (new_width // 2) - (y + 1) middle_blocks = y # times two blocks_to_draw.extend(blank_blocks * [blank]) blocks_to_draw.append(left_slope) blocks_to_draw.extend(middle_blocks * [left_fill] + middle_blocks * [right_fill]) blocks_to_draw.append(right_slope) blocks_to_draw.extend(blank_blocks * [blank]) elif self.orientation == ENDING: page_width = 16 page_limit = page_width - self.x_position % page_width new_width = page_width + page_limit + 1 new_height = (GROUND - 1) - SKY for y in range(SKY, GROUND - 1): blocks_to_draw.append(self.blocks[0]) blocks_to_draw.extend([self.blocks[1]] * (new_width - 1)) # todo magic number # ending graphics rom_offset = ENDING_OBJECT_OFFSET + self.object_set.get_ending_offset( ) * 0x60 rom = ROM() ending_graphic_height = 6 floor_height = 1 y_offset = GROUND - floor_height - ending_graphic_height for y in range(ending_graphic_height): for x in range(page_width): block_index = rom.get_byte(rom_offset + y * page_width + x - 1) block_position = (y_offset + y) * new_width + x + page_limit + 1 blocks_to_draw[block_position] = block_index # Mushroom/Fire flower/Star is categorized as an enemy elif self.orientation == VERTICAL: new_height = self.length + 1 new_width = self.width if self.ending == UNIFORM: if self.is_4byte: # there is one VERTICAL 4-byte object: Vertically oriented X-blocks # the width is the primary expansion new_width = (self.obj_index & 0x0F) + 1 for _ in range(new_height): for x in range(new_width): for y in range(self.height): blocks_to_draw.append(self.blocks[x % self.width]) elif self.ending == END_ON_TOP_OR_LEFT: # in case the drawn object is smaller than its actual size for y in range(min(self.height, new_height)): offset = y * self.width blocks_to_draw.extend(self.blocks[offset:offset + self.width]) additional_rows = new_height - self.height # assume only the last row needs to repeat # todo true for giant blocks? if additional_rows > 0: last_row = self.blocks[-self.width:] for _ in range(additional_rows): blocks_to_draw.extend(last_row) elif self.ending == END_ON_BOTTOM_OR_RIGHT: additional_rows = new_height - self.height # assume only the first row needs to repeat # todo true for giant blocks? if additional_rows > 0: last_row = self.blocks[0:self.width] for _ in range(additional_rows): blocks_to_draw.extend(last_row) # in case the drawn object is smaller than its actual size for y in range(min(self.height, new_height)): offset = y * self.width blocks_to_draw.extend(self.blocks[offset:offset + self.width]) elif self.ending == TWO_ENDS: # object exists on ships top_row = self.blocks[0:self.width] bottom_row = self.blocks[-self.width:] blocks_to_draw.extend(top_row) additional_rows = new_height - 2 # repeat second to last row if additional_rows > 0: for _ in range(additional_rows): blocks_to_draw.extend( self.blocks[-2 * self.width:-self.width]) if new_height > 1: blocks_to_draw.extend(bottom_row) elif self.orientation in [HORIZONTAL, HORIZ_TO_GROUND, HORIZONTAL_2]: new_width = self.length + 1 if self.orientation == HORIZ_TO_GROUND: # to the ground only, until it hits something for y in range(base_y, self.ground_level): bottom_row = QRect(base_x, y, new_width, 1) if any([ bottom_row.intersects(obj.get_rect()) and y == obj.get_rect().top() for obj in self.objects_ref[0:self.index_in_level] ]): new_height = y - base_y break else: # nothing underneath this object, extend to the ground new_height = self.ground_level - base_y if self.is_single_block: new_width = self.length elif self.orientation == HORIZONTAL_2 and self.ending == TWO_ENDS: # floating platforms seem to just be one shorter for some reason new_width -= 1 else: new_height = self.height + self.secondary_length if self.ending == UNIFORM and not self.is_4byte: for y in range(new_height): offset = (y % self.height) * self.width for _ in range(0, new_width): blocks_to_draw.extend(self.blocks[offset:offset + self.width]) # in case of giant blocks new_width *= self.width elif self.ending == UNIFORM and self.is_4byte: # 4 byte objects top = self.blocks[0:1] bottom = self.blocks[-1:] new_height = self.height + self.secondary_length # ceilings are one shorter than normal if self.height > self.width: new_height -= 1 blocks_to_draw.extend(new_width * top) for _ in range(1, new_height): blocks_to_draw.extend(new_width * bottom) elif self.ending == END_ON_TOP_OR_LEFT: for y in range(new_height): offset = y * self.width blocks_to_draw.append(self.blocks[offset]) for x in range(1, new_width): blocks_to_draw.append(self.blocks[offset + 1]) elif self.ending == END_ON_BOTTOM_OR_RIGHT: for y in range(new_height): offset = y * self.width for x in range(new_width - 1): blocks_to_draw.append(self.blocks[offset]) blocks_to_draw.append(self.blocks[offset + self.width - 1]) elif self.ending == TWO_ENDS: top_and_bottom_line = 2 for y in range(self.height): offset = y * self.width left, *middle, right = self.blocks[offset:offset + self.width] blocks_to_draw.append(left) blocks_to_draw.extend(middle * (new_width - top_and_bottom_line)) blocks_to_draw.append(right) if not len(blocks_to_draw) % self.height == 0: print( f"Blocks to draw are not divisible by height. {self}") new_width = int(len(blocks_to_draw) / self.height) top_row = blocks_to_draw[0:new_width] bottom_row = blocks_to_draw[-new_width:] middle_blocks = blocks_to_draw[new_width:-new_width] new_rows = new_height - top_and_bottom_line if new_rows >= 0: blocks_to_draw = top_row + middle_blocks * new_rows + bottom_row else: if not self.orientation == SINGLE_BLOCK_OBJECT: print(f"Didn't render {self.description}") # breakpoint() # for not yet implemented objects and single block objects if blocks_to_draw: self.rendered_blocks = blocks_to_draw else: self.rendered_blocks = self.blocks self.rendered_width = new_width self.rendered_height = new_height self.rendered_base_x = base_x self.rendered_base_y = base_y if new_width and not self.rendered_height == len( self.rendered_blocks) / new_width: print( f"Not enough Blocks for calculated height: {self.description}. " f"Blocks for height: {len(self.rendered_blocks) / new_width}. Rendered height: {self.rendered_height}" ) self.rendered_height = len(self.rendered_blocks) / new_width elif new_width == 0: print( f"Calculated Width is 0, setting to 1: {self.description}. " f"Blocks to draw: {len(self.rendered_blocks)}. Rendered height: {self.rendered_height}" ) self.rendered_width = 1 self.rect = QRect(self.rendered_base_x, self.rendered_base_y, self.rendered_width, self.rendered_height) def draw(self, painter: QPainter, block_length, transparent): for index, block_index in enumerate(self.rendered_blocks): if block_index == BLANK: continue x = self.rendered_base_x + index % self.rendered_width y = self.rendered_base_y + index // self.rendered_width self._draw_block(painter, block_index, x, y, block_length, transparent) def _draw_block(self, painter: QPainter, block_index, x, y, block_length, transparent): if block_index not in self.block_cache: if block_index > 0xFF: rom_block_index = ROM().get_byte( block_index ) # block_index is an offset into the graphic memory block = Block(rom_block_index, self.palette_group, self.pattern_table, self.tsa_data) else: block = Block(block_index, self.palette_group, self.pattern_table, self.tsa_data) self.block_cache[block_index] = block self.block_cache[block_index].draw( painter, x * block_length, y * block_length, block_length=block_length, selected=self.selected, transparent=transparent, ) def set_position(self, x, y): # todo also check for the upper bounds x = max(0, x) y = max(0, y) x_diff = self.x_position - self.rendered_base_x y_diff = self.y_position - self.rendered_base_y self.rendered_base_x = int(x) self.rendered_base_y = int(y) self.x_position = self.rendered_base_x + x_diff self.y_position = self.rendered_base_y + y_diff self._render() def move_by(self, dx: int, dy: int): new_x = self.rendered_base_x + dx new_y = self.rendered_base_y + dy self.set_position(new_x, new_y) def get_position(self): return self.x_position, self.y_position def expands(self): expands = EXPANDS_NOT if self.is_single_block: return expands if self.is_4byte: expands |= EXPANDS_BOTH elif self.orientation in [HORIZONTAL, HORIZONTAL_2, HORIZ_TO_GROUND ] or self.orientation in [ DIAG_DOWN_LEFT, DIAG_DOWN_RIGHT, DIAG_UP_RIGHT, DIAG_WEIRD, ]: expands |= EXPANDS_HORIZ elif self.orientation in [VERTICAL, DIAG_WEIRD]: expands |= EXPANDS_VERT return expands def primary_expansion(self): if self.orientation in [HORIZONTAL, HORIZONTAL_2, HORIZ_TO_GROUND ] or self.orientation in [ DIAG_DOWN_LEFT, DIAG_DOWN_RIGHT, DIAG_UP_RIGHT, DIAG_WEIRD, ]: if self.is_4byte: return EXPANDS_VERT else: return EXPANDS_HORIZ elif self.orientation in [VERTICAL]: if self.is_4byte: return EXPANDS_HORIZ else: return EXPANDS_VERT else: return EXPANDS_BOTH def resize_x(self, x: int): if self.expands() & EXPANDS_HORIZ == 0: return if self.primary_expansion() == EXPANDS_HORIZ: length = x - self.x_position length = max(0, length) length = min(length, 0x0F) base_index = (self.obj_index // 0x10) * 0x10 self.obj_index = base_index + length self.data[2] = self.obj_index else: length = x - self.x_position length = max(0, length) length = min(length, 0xFF) if self.is_4byte: self.data[3] = length else: raise ValueError("Resize impossible", self) self._calculate_lengths() self._render() def resize_y(self, y: int): if self.expands() & EXPANDS_VERT == 0: return if self.primary_expansion() == EXPANDS_VERT: length = y - self.y_position length = max(0, length) length = min(length, 0x0F) base_index = (self.obj_index // 0x10) * 0x10 self.obj_index = base_index + length self.data[2] = self.obj_index else: length = y - self.y_position length = max(0, length) length = min(length, 0xFF) if self.is_4byte: self.data[3] = length else: raise ValueError("Resize impossible", self) self._calculate_lengths() self._render() def resize_by(self, dx: int, dy: int): if dx: self.resize_x(self.x_position + dx) if dy: self.resize_y(self.y_position + dy) def increment_type(self): self.change_type(True) def decrement_type(self): self.change_type(False) def change_type(self, increment: int): if self.obj_index < 0x10 or self.obj_index == 0x10 and not increment: value = 1 else: self.obj_index = self.obj_index // 0x10 * 0x10 value = 0x10 if not increment: value *= -1 new_type = self.obj_index + value if new_type < 0 and self.domain > 0: new_domain = self.domain - 1 new_type = 0xF0 elif new_type > 0xFF and self.domain < 7: new_domain = self.domain + 1 new_type = 0x00 else: new_type = min(0xFF, new_type) new_type = max(0, new_type) new_domain = self.domain self.data[0] &= 0b0001_1111 self.data[0] |= new_domain << 5 self.data[2] = new_type self._setup() def __contains__(self, item: Tuple[int, int]) -> bool: x, y = item return self.point_in(x, y) def point_in(self, x: int, y: int) -> bool: return self.rect.contains(x, y) def get_status_info(self) -> List[tuple]: return [ ("x", self.rendered_base_x), ("y", self.rendered_base_y), ("Width", self.rendered_width), ("Height", self.rendered_height), ("Orientation", ORIENTATION_TO_STR[self.orientation]), ("Ending", ENDING_STR[self.ending]), ] def display_size(self, zoom_factor: int = 1): return QSize(self.rendered_width * Block.SIDE_LENGTH, self.rendered_height * Block.SIDE_LENGTH) * zoom_factor def as_image(self) -> QImage: self.rendered_base_x = 0 self.rendered_base_y = 0 image = QImage( QSize(self.rendered_width * Block.SIDE_LENGTH, self.rendered_height * Block.SIDE_LENGTH), QImage.Format_RGB888, ) bg_color = bg_color_for_object_set(self.object_set.number, 0) image.fill(bg_color) painter = QPainter(image) self.draw(painter, Block.SIDE_LENGTH, True) return image def to_bytes(self) -> bytearray: data = bytearray() if self.vertical_level: # todo from vertical to non-vertical is bugged, because it # seems like you can't convert the coordinates 1:1 # there seems to be ambiguity offset = self.y_position // SCREEN_HEIGHT x_position = self.x_position + offset * SCREEN_WIDTH y_position = self.y_position % SCREEN_HEIGHT else: x_position = self.x_position y_position = self.y_position if self.orientation in [PYRAMID_TO_GROUND, PYRAMID_2]: x_position = self.rendered_base_x - 1 + self.rendered_width // 2 data.append((self.domain << 5) | y_position) data.append(x_position) if not self.is_4byte and not self.is_single_block: third_byte = (self.obj_index & 0xF0) + self.length else: third_byte = self.obj_index data.append(third_byte) if self.is_4byte: data.append(self.length) return data def __repr__(self) -> str: return f"LevelObject {self.description} at {self.x_position}, {self.y_position}" def __eq__(self, other): if not isinstance(other, LevelObject): return False else: return self.to_bytes() == other.to_bytes() def __lt__(self, other): return self.index_in_level < other.index_in_level
class PaletteWidget(QtWidgets.QWidget): # Color changed signal changed = QtCore.Signal() @property def color(self): return QtGui.QColor.fromHsvF(self._hue, self._saturation, self._value) @color.setter def color(self, value): # If this is an integer, assume is RGBA if not isinstance(value, QtGui.QColor): r = (value & 0xff000000) >> 24 g = (value & 0xff0000) >> 16 b = (value & 0xff00) >> 8 value = QtGui.QColor.fromRgb(r, g, b) self._set_color(value) self.RGBvalue.setText(value.name()) def __init__(self, parent=None, RGBvalue=None): super(PaletteWidget, self).__init__(parent) self._hue = 1.0 self._saturation = 1.0 self._value = 1.0 self._hue_width = 24 self._gap = 8 self._color = QtGui.QColor.fromHslF(self._hue, 1.0, 1.0) self._calculate_bounds() self._draw_palette() self.RGBvalue = RGBvalue # Calculate the sizes of various bits of our UI def _calculate_bounds(self): width = self.width() height = self.height() # Hue palette self._hue_rect = QRect(width - self._hue_width, 0, self._hue_width, height) # Shades palette self._shades_rect = QRect(0, 0, width - (self._hue_width + self._gap), height) # Render our palette to an image def _draw_palette(self): # Create an image with a white background self._image = QtGui.QImage(QtCore.QSize(self.width(), self.height()), QtGui.QImage.Format.Format_RGB32) self._image.fill(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) # Draw on our image with no pen qp = QtGui.QPainter() qp.begin(self._image) qp.setPen(QtCore.Qt.NoPen) # Render hues rect = self._hue_rect for x in xrange(rect.x(), rect.x() + rect.width()): for y in xrange(rect.y(), rect.y() + rect.height(), 8): h = float(y) / rect.height() s = 1.0 v = 1.0 c = QtGui.QColor.fromHsvF(h, s, v) qp.setBrush(c) qp.drawRect(x, y, 8, 8) # Render hue selection marker qp.setBrush(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) qp.drawRect(rect.x(), self._hue * rect.height(), rect.width(), 2) # Render shades rect = self._shades_rect width = float(rect.width()) steps = int(round(width / 8.0)) step_size = width / steps x = rect.x() while x < rect.width() + rect.x(): w = int(round(step_size)) for y in xrange(rect.y(), rect.y() + rect.height(), 8): h = self._hue s = 1 - float(y) / rect.height() v = float(x) / rect.width() c = QtGui.QColor.fromHsvF(h, s, v) qp.setBrush(c) qp.drawRect(x, y, w, 8) x += w width -= w steps -= 1 if steps > 0: step_size = width / steps # Render color selection marker qp.setBrush(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) qp.drawRect(rect.x(), (1 - self._saturation) * rect.height(), rect.width(), 1) qp.drawRect(self._value * rect.width(), rect.y(), 1, rect.height()) qp.end() def paintEvent(self, event): # Render our palette image to the screen qp = QtGui.QPainter() qp.begin(self) qp.drawImage(QPoint(0, 0), self._image) qp.end() def mousePressEvent(self, event): mouse = QPoint(event.pos()) if event.buttons() & QtCore.Qt.LeftButton: # Click on hues? if self._hue_rect.contains(mouse.x(), mouse.y()): y = mouse.y() c = QtGui.QColor.fromHsvF(float(y) / self.height(), self._saturation, self._value) self.color = c # Click on colors? elif self._shades_rect.contains(mouse.x(), mouse.y()): # calculate saturation and value x = mouse.x() y = mouse.y() c = QtGui.QColor.fromHsvF(self._hue, 1 - float(y) / self._shades_rect.height(), float(x) / self._shades_rect.width()) self.color = c def mouseMoveEvent(self, event): if event.buttons() & QtCore.Qt.LeftButton: self.mousePressEvent(event) def resizeEvent(self, event): self._calculate_bounds() self._draw_palette() # Set the current color def _set_color(self, c): h, s, v, _ = c.getHsvF() self._hue = h self._saturation = s self._value = v self._draw_palette() self.repaint() self.changed.emit()
class QMaxRollout(QtW.QWidget): collapsed = Signal() expanded = Signal() toggled = Signal(bool) def __init__(self, *args): super(QMaxRollout, self).__init__(*args) self._expanded = True self._delayed = False, False self.setSizePolicy( QtW.QSizePolicy(QtW.QSizePolicy.MinimumExpanding, QtW.QSizePolicy.Minimum)) self._full_title = str() self._title = self._full_title self._header_rect = QRect() self._title_bound_rect = QRect() self._base_margin = QMargins(9, 9, 9, 9) self._icon_width = 24 self._storage = QtW.QWidget() self._storage.setVisible(False) self._system_font = QtG.QFont() # noinspection PyPep8Naming def setExpanded(self, state): self._expand_collapse(state) # noinspection PyPep8Naming def setExpandedDelay(self, state): self._delayed = True, state def _expand_collapse(self, state): self._expanded = state children = self.children() for child in children: if hasattr(child, "setVisible"): child.setVisible(state) else: layout_collapse_and_restore(child, state) if state: self.expanded.emit() else: self.collapsed.emit() self.update() # noinspection PyPep8Naming def isExpanded(self): return self._expanded def title(self): return self._full_title # noinspection PyPep8Naming def setTitle(self, title): self._full_title = title def toggle(self): self._expand_collapse(not self._expanded) self.toggled.emit(self._expanded) # --------------------------------------------------- # Events # --------------------------------------------------- def mouseReleaseEvent(self, e): point = (e.localPos()).toPoint() if self._header_rect.contains(point): self.toggle() e.accept() else: e.ignore() def paintEvent(self, e): # If delayed expand/collapse is activated, it won't process until the next paint event if self._delayed[0]: self._expand_collapse(self._delayed[1]) self._delayed = False, False # Object Repaint qp = QtG.QPainter() qp.begin(self) self._system_font = qp.font() self._recalculate_header() super(QMaxRollout, self).paintEvent(e) self._draw_widget(qp) qp.end() e.accept() # --------------------------------------------------- # Paint Event Helpers # --------------------------------------------------- def _recalculate_header(self): fnt_title = bold_font(self._system_font) font_metric = QtG.QFontMetricsF(fnt_title) if self._full_title == str(): prop = self.property('title') if prop is not None: self._title_bound_rect = font_metric.boundingRect(prop) self._full_title = prop else: self._title_bound_rect = font_metric.boundingRect("UNTITLED") else: self._title_bound_rect = font_metric.boundingRect(self._full_title) title_space = self.width() - (self._icon_width + self._base_margin.right() + self._base_margin.left()) # If the title is too big to fit in the box if self._title_bound_rect.width() > title_space: self._title = chop_title(self._full_title, title_space, self._title_bound_rect.width()) self._title_bound_rect = font_metric.boundingRect(self._title) else: self._title = self._full_title self._header_rect = QRect( self._base_margin.left(), self._base_margin.top(), self.width() - (self._base_margin.left() - self._base_margin.right()), self._title_bound_rect.height() + self._base_margin.top()) def _draw_widget(self, qp): size = self.size() w = size.width() h = size.height() mrgn = self._base_margin br_bkg = QtG.QBrush(QtG.QColor(80, 80, 80)) # pal = self.palette() # br_bkg = pal.alternateBase() pn_bkg = QtG.QPen(QtG.QColor(62, 62, 62), 2) br_arr = QtG.QBrush(QtG.QColor(185, 185, 185)) pn_arr = QtG.QPen(QtG.QColor(62, 62, 62), 0) # For debugging # pn_test = QtG.QPen(Qt.red, 1) pn_txt = QtG.QPen(QtG.QColor(255, 255, 255)) rnd_x, rnd_y = calculate_round_corners(w, h, 11) rct_bkg = QRect(0, 0, w, h) qp.setPen(pn_bkg) qp.setBrush(br_bkg) qp.setRenderHint(QtG.QPainter.Antialiasing) qp.drawRoundRect(rct_bkg, rnd_x, rnd_y, Qt.RelativeSize) tx_title = self._title qp.setFont(bold_font(self._system_font)) rct_icon = QRect(mrgn.left(), mrgn.top(), self._icon_width, self._title_bound_rect.height()) rct_title = QRect(rct_icon.right(), mrgn.top(), self._title_bound_rect.width(), self._title_bound_rect.height()) pgn_arrow = center_poly_in_rect(max_arrow(not self._expanded), rct_icon) qp.setPen(pn_arr) qp.setBrush(br_arr) qp.drawPolygon(pgn_arrow) qp.setPen(pn_txt) qp.drawText(rct_title, tx_title) open_margins = QMargins( mrgn.left(), self._title_bound_rect.height() + (mrgn.top() * 2), mrgn.right(), mrgn.bottom()) closed_margins = QMargins(mrgn.left(), self._title_bound_rect.height() + mrgn.top(), mrgn.right(), mrgn.bottom()) mrgn = open_margins if self._expanded else closed_margins self.setContentsMargins(mrgn) # For debugging # qp.setPen(pn_test) qp.setBrush(Qt.NoBrush) qp.setFont(self._system_font)
class EnemyObject(ObjectLike): def __init__(self, data, png_data, palette_group): super(EnemyObject, self).__init__() self.is_4byte = False self.is_single_block = True self.length = 0 self.obj_index = data[0] self.x_position = data[1] self.y_position = data[2] self.domain = 0 self.pattern_table = PatternTable(0x4C) self.palette_group = palette_group self.object_set = ObjectSet(ENEMY_OBJECT_SET) self.bg_color = NESPalette[palette_group[0][0]] self.png_data = png_data self.rect = QRect() self.selected = False self._setup() def _setup(self): obj_def = self.object_set.get_definition_of(self.obj_index) self.description = obj_def.description self.width = obj_def.bmp_width self.height = obj_def.bmp_height self.rect = QRect(self.x_position, self.y_position, self.width, self.height) self._render(obj_def) def _render(self, obj_def): self.blocks = [] block_ids = obj_def.object_design for block_id in block_ids: x = (block_id % 64) * Block.WIDTH y = (block_id // 64) * Block.WIDTH self.blocks.append( self.png_data.copy(QRect(x, y, Block.WIDTH, Block.HEIGHT))) def render(self): # nothing to re-render since enemies are just copied over pass def draw(self, painter: QPainter, block_length, _): for i, image in enumerate(self.blocks): x = self.x_position + (i % self.width) y = self.y_position + (i // self.width) x_offset = int(enemy_handle_x[self.obj_index]) y_offset = int(enemy_handle_y[self.obj_index]) x += x_offset y += y_offset block = image.copy() mask = block.createMaskFromColor( QColor(*MASK_COLOR).rgb(), Qt.MaskOutColor) block.setAlphaChannel(mask) # todo better effect if self.selected: apply_selection_overlay(block, mask) if block_length != Block.SIDE_LENGTH: block = block.scaled(block_length, block_length) painter.drawImage(x * block_length, y * block_length, block) def get_status_info(self): return [("Name", self.description), ("X", self.x_position), ("Y", self.y_position)] def __contains__(self, item): x, y = item return self.point_in(x, y) def point_in(self, x, y): return self.rect.contains(x, y) def set_position(self, x, y): # todo also check for the upper bounds x = max(0, x) y = max(0, y) self.x_position = x self.y_position = y self.rect = QRect(self.x_position, self.y_position, self.width, self.height) def move_by(self, dx, dy): new_x = self.x_position + dx new_y = self.y_position + dy self.set_position(new_x, new_y) def get_position(self): return self.x_position, self.y_position def resize_by(self, dx, dy): pass @property def type(self): return self.obj_index def change_type(self, new_type): self.obj_index = new_type self._setup() def increment_type(self): self.obj_index = min(0xFF, self.obj_index + 1) self._setup() def decrement_type(self): self.obj_index = max(0, self.obj_index - 1) self._setup() def to_bytes(self): return bytearray([self.obj_index, self.x_position, self.y_position]) def as_image(self) -> QImage: image = QImage( QSize(self.width * Block.SIDE_LENGTH, self.height * Block.SIDE_LENGTH), QImage.Format_RGBA8888, ) image.fill(QColor(0, 0, 0, 0)) painter = QPainter(image) self.draw(painter, Block.SIDE_LENGTH, True) return image def __repr__(self): return f"EnemyObject {self.description} at {self.x_position}, {self.y_position}"
class MapObject(ObjectLike): def __init__(self, block, x, y): self.x_position = x self.y_position = y self.block = block self.rect = QRect(self.x_position, self.y_position, 1, 1) if self.block.index in map_object_names: self.name = map_object_names[self.block.index] else: self.name = str(hex(self.block.index)) self.selected = False def set_position(self, x, y): x = int(x) y = int(y) self.rect = QRect(x, y, 1, 1) self.x_position = x self.y_position = y def get_position(self): return self.x_position, self.y_position def render(self): pass def draw(self, dc, block_length, _=None): self.block.draw( dc, self.x_position * block_length, self.y_position * block_length, block_length=block_length, selected=self.selected, transparent=False, ) def get_status_info(self): return ("x", self.x_position), ("y", self.y_position), ("Block Type", self.name) def to_bytes(self): return self.block.index def move_by(self, dx, dy): self.set_position(self.x_position + dx, self.y_position + dy) def resize_to(self, x, y): return def resize_by(self, dx, dy): return def point_in(self, x, y): return self.rect.contains(x, y) def change_type(self, new_type): pass def get_rect(self): return self.rect def __contains__(self, point): pass
class SchematicEditor(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setMouseTracking(True) self.setFocusPolicy(Qt.StrongFocus) self.elements = list() self.wires = list() self.guidepoints = list() self.guidelines = list() self._ghost_wire = None self.wiring_mode = False self.closest_point = None self._wire_start = None self.select_rect = None self.selected_elements = list() self.moved = False self.grabbed_element = None self.grab_offset = None def _draw_wire(self, painter, line, ghost): p = QPen(Qt.black, 8, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap) path = QPainterPath(line.p1()) path.lineTo(line.p2()) stroker = QPainterPathStroker(p) stroke = stroker.createStroke(path) fill_color = QColor(255, 255, 255) outline_color = QColor(0, 0, 0) if ghost: fill_color.setAlphaF(0.5) outline_color.setAlphaF(0.5) painter.setPen(QPen(outline_color, 2)) painter.fillPath(stroke, fill_color) painter.drawPath(stroke) def _draw_wires(self, painter): path = QPainterPath() for wire in self.wires: path.moveTo(wire.p1()) path.lineTo(wire.p2()) p = QPen(Qt.black, 8, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap) stroker = QPainterPathStroker(p) stroke = stroker.createStroke(path).simplified() fill_color = QColor(255, 255, 255) outline_color = QColor(0, 0, 0) painter.setPen(QPen(outline_color, 2)) painter.fillPath(stroke, fill_color) painter.drawPath(stroke) def _draw_pin(self, painter, point): fill_color = QColor(255, 255, 255) outline_color = QColor(0, 0, 0) painter.setBrush(fill_color) painter.setPen(QPen(outline_color, 2)) painter.drawEllipse(point.x() - 4, point.y() - 4, 8, 8) def paintEvent(self, *args): painter = QPainter(self) painter.setRenderHint(QPainter.HighQualityAntialiasing) r = self.rect() painter.fillRect(r, Qt.white) for element in self.elements: painter.translate(element.bounding_box.topLeft()) element.paint(painter) painter.translate(-element.bounding_box.topLeft()) self._draw_wires(painter) for element in self.elements: for pin in element.pins(): p = pin.position + element.bounding_box.topLeft() self._draw_pin(painter, p) painter.setPen(QPen(Qt.red, 1, Qt.DashLine)) painter.setBrush(Qt.transparent) for element in self.selected_elements: bb = element.bounding_box bb = bb.marginsAdded(QMargins(2, 2, 1, 1)) painter.drawRect(bb) if self.select_rect is not None: painter.setBrush(QColor(0, 0, 255, 64)) painter.setPen(QColor(0, 0, 255, 128)) painter.drawRect(self.select_rect) if self.wiring_mode: painter.setPen(QPen(Qt.red, 1, Qt.PenStyle.DotLine)) for line in self.guidelines: painter.drawLine(line) if self._ghost_wire: self._draw_wire(painter, self._ghost_wire, True) if self.closest_point is not None: p = self.closest_point painter.drawEllipse(p.x() - 4, p.y() - 4, 8, 8) def _pick(self, p): for element in self.elements: if element.bounding_box.contains(p): return element return None def _closest_guideline_point(self, point): currd = None closest = None is_junction = False for element in self.elements: for pin in element.pins(): p = pin.position + element.bounding_box.topLeft() d = QVector2D(p - point).lengthSquared() if (currd is None or d < currd) and d < 2500: currd = d closest = p is_junction = True for wire in self.wires: for p in (wire.p1(), wire.p2()): d = QVector2D(p - point).lengthSquared() if (currd is None or d < currd) and d < 2500: currd = d closest = p is_junction = True for line in self.guidelines: p = _closest_point(line, point) d = QVector2D(p - point).lengthSquared() if not _is_point_on_line(line, p): continue if self._wire_start is not None: delta = p - self._wire_start if delta.x() != 0 and delta.y() != 0: continue if (currd is None or ((not is_junction and d < currd) or (is_junction and abs(d - currd) > 100))) and d < 2500: currd = d closest = p return closest def _closest_assist_point(self, point): gp = self._closest_guideline_point(point) return gp def mousePressEvent(self, e): if self.wiring_mode: pass else: self.grabbed_element = self._pick(e.pos()) if self.grabbed_element is not None: self.grab_offset = self.grabbed_element.bounding_box.topLeft( ) - e.pos() else: self.select_rect = QRect(e.pos(), QSize(0, 0)) def mouseMoveEvent(self, e): if self.wiring_mode: self.closest_point = self._closest_assist_point(e.pos()) if self._wire_start is not None and self.closest_point is not None: self._ghost_wire = QLine(self._wire_start, self.closest_point) else: self._ghost_wire = None self.update() else: if self.grabbed_element is not None: self.grabbed_element.bounding_box.moveTopLeft(e.pos() + self.grab_offset) self.moved = True self.update() elif self.select_rect is not None: self.select_rect.setBottomRight(e.pos()) if self.select_rect.size() != QSize(0, 0): self.selected_elements = list() for element in self.elements: if self.select_rect.contains(element.bounding_box): self.selected_elements.append(element) self.update() def mouseReleaseEvent(self, e): if self.wiring_mode: if e.button() == Qt.RightButton: self._wire_start = None self.update() elif self.closest_point is not None: if self._wire_start is None: self._wire_start = self.closest_point elif self.closest_point != self._wire_start: wire_end = self.closest_point self.wires.append(QLine(self._wire_start, wire_end)) self._wire_start = None self._build_guidelines() self.update() else: moved = self.moved if self.grabbed_element is not None: self.grabbed_element = None self.moved = False if not moved: self.selected_elements = list() if self.select_rect is not None and self.select_rect.size( ) != QSize(0, 0): for element in self.elements: if self.select_rect.contains(element.bounding_box): self.selected_elements.append(element) else: for element in self.elements: bb = element.bounding_box if bb.contains(e.pos()): self.selected_elements.append(element) break self.select_rect = None self.update() def _build_guidelines(self): self.guidelines = list() for element in self.elements: for pin in element.pins(): p = pin.position + element.bounding_box.topLeft() if pin.direction.y() == 0: if pin.direction.x() > 0: self.guidelines.append( QLine(p.x(), p.y(), self.rect().width(), p.y())) else: self.guidelines.append(QLine(p.x(), p.y(), 0, p.y())) self.guidelines.append( QLine(p.x(), 0, p.x(), self.rect().height())) else: if pin.direction.y() > 0: self.guidelines.append( QLine(p.x(), p.y(), p.x(), self.rect().height())) else: self.guidelines.append(QLine(p.x(), p.y(), p.x(), 0)) self.guidelines.append( QLine(0, p.y(), self.rect().width(), p.y())) for wire in self.wires: for p in (wire.p1(), wire.p2()): self.guidelines.append( QLine(0, p.y(), self.rect().width(), p.y())) self.guidelines.append( QLine(p.x(), 0, p.x(), self.rect().height())) def resizeEvent(self, e): super().resizeEvent(e) self._build_guidelines() self.update() def _leave_wiring_mode(self): self.wiring_mode = False def _enter_wiring_mode(self): self.wiring_mode = True self._ghost_wire = None self.closest_point = None self.selected_elements = list() self._build_guidelines() def keyReleaseEvent(self, e): if e.key() == Qt.Key_W: if not self.wiring_mode: self._enter_wiring_mode() self.wiring_mode = True else: self._leave_wiring_mode() self.wiring_mode = False self.update() elif e.key() == Qt.Key_Escape: if self.wiring_mode: self._leave_wiring_mode() self.wiring_mode = False self.update()
class Screenshot(QGraphicsView): """ Main Class """ screen_shot_grabed = Signal(QImage) screen_shot_pos_grabed = Signal(QRect) widget_closed = Signal() def __init__(self, flags=constant.DEFAULT, parent=None): """ flags: binary flags. see the flags in the constant.py """ super().__init__(parent) # Init self.penColorNow = QColor(PENCOLOR) self.penSizeNow = PENSIZE self.fontNow = QFont('Sans') self.clipboard = QApplication.clipboard() self.drawListResult = [ ] # draw list that sure to be drew, [action, coord] self.drawListProcess = None # the process to the result self.selected_area = QRect( ) # a QRect instance which stands for the selected area self.selectedAreaRaw = QRect() self.mousePosition = MousePosition.OUTSIDE_AREA # mouse position self.screenPixel = None self.textRect = None self.mousePressed = False self.action = ACTION_SELECT self.mousePoint = self.cursor().pos() self.startX, self.startY = 0, 0 # the point where you start self.endX, self.endY = 0, 0 # the point where you end self.pointPath = QPainterPath( ) # the point mouse passes, used by draw free line self.items_to_remove = [ ] # the items that should not draw on screenshot picture self.textPosition = None # result self.target_img = None self.target_img_pos = None # Init window self.getscreenshot() self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) self.setMouseTracking(True) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setContentsMargins(0, 0, 0, 0) self.setStyleSheet("QGraphicsView { border-style: none; }") self.tooBar = MyToolBar(flags, self) self.tooBar.trigger.connect(self.changeAction) self.penSetBar = None if flags & constant.RECT or flags & constant.ELLIPSE or flags & constant.LINE or flags & constant.FREEPEN \ or flags & constant.ARROW or flags & constant.TEXT: self.penSetBar = PenSetWidget(self) self.penSetBar.penSizeTrigger.connect(self.changePenSize) self.penSetBar.penColorTrigger.connect(self.changePenColor) self.penSetBar.fontChangeTrigger.connect(self.changeFont) self.textInput = TextInput(self) self.textInput.inputChanged.connect(self.textChange) self.textInput.cancelPressed.connect(self.cancelInput) self.textInput.okPressed.connect(self.okInput) self.graphics_scene = QGraphicsScene(0, 0, self.screenPixel.width(), self.screenPixel.height()) self.show() self.setScene(self.graphics_scene) self.windowHandle().setScreen(QGuiApplication.screenAt(QCursor.pos())) self.scale = self.get_scale() # self.setFixedSize(self.screenPixel.width(), self.screenPixel.height()) self.setGeometry(QGuiApplication.screenAt(QCursor.pos()).geometry()) self.showFullScreen() self.redraw() QShortcut(QKeySequence('ctrl+s'), self).activated.connect(self.saveScreenshot) QShortcut(QKeySequence('esc'), self).activated.connect(self.close) @staticmethod def take_screenshot(flags): loop = QEventLoop() screen_shot = Screenshot(flags) screen_shot.show() screen_shot.widget_closed.connect(loop.quit) loop.exec_() img = screen_shot.target_img return img @staticmethod def take_screenshot_pos(flags): loop = QEventLoop() screen_shot = Screenshot(flags) screen_shot.show() screen_shot.widget_closed.connect(loop.quit) loop.exec_() pos = screen_shot.target_img_pos return pos def getscreenshot(self): screen = QGuiApplication.screenAt(QCursor.pos()) self.screenPixel = screen.grabWindow(0) def mousePressEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if event.button() != Qt.LeftButton: return if self.action is None: self.action = ACTION_SELECT self.startX, self.startY = event.x(), event.y() if self.action == ACTION_SELECT: if self.mousePosition == MousePosition.OUTSIDE_AREA: self.mousePressed = True self.selected_area = QRect() self.selected_area.setTopLeft(QPoint(event.x(), event.y())) self.selected_area.setBottomRight(QPoint(event.x(), event.y())) self.redraw() elif self.mousePosition == MousePosition.INSIDE_AREA: self.mousePressed = True else: pass elif self.action == ACTION_MOVE_SELECTED: if self.mousePosition == MousePosition.OUTSIDE_AREA: self.action = ACTION_SELECT self.selected_area = QRect() self.selected_area.setTopLeft(QPoint(event.x(), event.y())) self.selected_area.setBottomRight(QPoint(event.x(), event.y())) self.redraw() self.mousePressed = True elif self.action in DRAW_ACTION: self.mousePressed = True if self.action == ACTION_FREEPEN: self.pointPath = QPainterPath() self.pointPath.moveTo(QPoint(event.x(), event.y())) elif self.action == ACTION_TEXT: if self.textPosition is None: self.textPosition = QPoint(event.x(), event.y()) self.textRect = None self.redraw() def mouseMoveEvent(self, event: QMouseEvent): """ :type event: QMouseEvent :param event: :return: """ self.mousePoint = QPoint(event.globalPos().x(), event.globalPos().y()) if self.action is None: self.action = ACTION_SELECT if not self.mousePressed: point = QPoint(event.x(), event.y()) self.detect_mouse_position(point) self.setCursorStyle() self.redraw() else: self.endX, self.endY = event.x(), event.y() # if self.mousePosition != OUTSIDE_AREA: # self.action = ACTION_MOVE_SELECTED if self.action == ACTION_SELECT: self.selected_area.setBottomRight(QPoint(event.x(), event.y())) self.redraw() elif self.action == ACTION_MOVE_SELECTED: self.selected_area = QRect(self.selectedAreaRaw) if self.mousePosition == MousePosition.INSIDE_AREA: move_to_x = event.x( ) - self.startX + self.selected_area.left() move_to_y = event.y( ) - self.startY + self.selected_area.top() if 0 <= move_to_x <= self.screenPixel.width( ) - 1 - self.selected_area.width(): self.selected_area.moveLeft(move_to_x) if 0 <= move_to_y <= self.screenPixel.height( ) - 1 - self.selected_area.height(): self.selected_area.moveTop(move_to_y) self.selected_area = self.selected_area.normalized() self.selectedAreaRaw = QRect(self.selected_area) self.startX, self.startY = event.x(), event.y() self.redraw() elif self.mousePosition == MousePosition.ON_THE_LEFT_SIDE: move_to_x = event.x( ) - self.startX + self.selected_area.left() if move_to_x <= self.selected_area.right(): self.selected_area.setLeft(move_to_x) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_RIGHT_SIDE: move_to_x = event.x( ) - self.startX + self.selected_area.right() self.selected_area.setRight(move_to_x) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_UP_SIDE: move_to_y = event.y( ) - self.startY + self.selected_area.top() self.selected_area.setTop(move_to_y) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_DOWN_SIDE: move_to_y = event.y( ) - self.startY + self.selected_area.bottom() self.selected_area.setBottom(move_to_y) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_TOP_LEFT_CORNER: move_to_x = event.x( ) - self.startX + self.selected_area.left() move_to_y = event.y( ) - self.startY + self.selected_area.top() self.selected_area.setTopLeft(QPoint(move_to_x, move_to_y)) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_BOTTOM_RIGHT_CORNER: move_to_x = event.x( ) - self.startX + self.selected_area.right() move_to_y = event.y( ) - self.startY + self.selected_area.bottom() self.selected_area.setBottomRight( QPoint(move_to_x, move_to_y)) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_TOP_RIGHT_CORNER: move_to_x = event.x( ) - self.startX + self.selected_area.right() move_to_y = event.y( ) - self.startY + self.selected_area.top() self.selected_area.setTopRight(QPoint( move_to_x, move_to_y)) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_BOTTOM_LEFT_CORNER: move_to_x = event.x( ) - self.startX + self.selected_area.left() move_to_y = event.y( ) - self.startY + self.selected_area.bottom() self.selected_area.setBottomLeft( QPoint(move_to_x, move_to_y)) self.redraw() else: pass elif self.action == ACTION_RECT: self.drawRect(self.startX, self.startY, event.x(), event.y(), False) self.redraw() pass elif self.action == ACTION_ELLIPSE: self.drawEllipse(self.startX, self.startY, event.x(), event.y(), False) self.redraw() elif self.action == ACTION_ARROW: self.drawArrow(self.startX, self.startY, event.x(), event.y(), False) self.redraw() elif self.action == ACTION_LINE: self.drawLine(self.startX, self.startY, event.x(), event.y(), False) self.redraw() elif self.action == ACTION_FREEPEN: y1, y2 = event.x(), event.y() rect = self.selected_area.normalized() if y1 <= rect.left(): y1 = rect.left() elif y1 >= rect.right(): y1 = rect.right() if y2 <= rect.top(): y2 = rect.top() elif y2 >= rect.bottom(): y2 = rect.bottom() self.pointPath.lineTo(y1, y2) self.drawFreeLine(self.pointPath, False) self.redraw() def mouseReleaseEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if event.button() != Qt.LeftButton: return if self.mousePressed: self.mousePressed = False self.endX, self.endY = event.x(), event.y() if self.action == ACTION_SELECT: self.selected_area.setBottomRight(QPoint(event.x(), event.y())) self.selectedAreaRaw = QRect(self.selected_area) self.action = ACTION_MOVE_SELECTED self.redraw() elif self.action == ACTION_MOVE_SELECTED: self.selectedAreaRaw = QRect(self.selected_area) self.redraw() # self.action = None elif self.action == ACTION_RECT: self.drawRect(self.startX, self.startY, event.x(), event.y(), True) self.redraw() elif self.action == ACTION_ELLIPSE: self.drawEllipse(self.startX, self.startY, event.x(), event.y(), True) self.redraw() elif self.action == ACTION_ARROW: self.drawArrow(self.startX, self.startY, event.x(), event.y(), True) self.redraw() elif self.action == ACTION_LINE: self.drawLine(self.startX, self.startY, event.x(), event.y(), True) self.redraw() elif self.action == ACTION_FREEPEN: self.drawFreeLine(self.pointPath, True) self.redraw() def detect_mouse_position(self, point): """ :type point: QPoint :param point: the mouse position you want to check :return: """ if self.selected_area == QRect(): self.mousePosition = MousePosition.OUTSIDE_AREA return if self.selected_area.left() - ERRORRANGE <= point.x( ) <= self.selected_area.left() and ( self.selected_area.top() - ERRORRANGE <= point.y() <= self.selected_area.top()): self.mousePosition = MousePosition.ON_THE_TOP_LEFT_CORNER elif self.selected_area.right() <= point.x( ) <= self.selected_area.right() + ERRORRANGE and ( self.selected_area.top() - ERRORRANGE <= point.y() <= self.selected_area.top()): self.mousePosition = MousePosition.ON_THE_TOP_RIGHT_CORNER elif self.selected_area.left() - ERRORRANGE <= point.x( ) <= self.selected_area.left() and ( self.selected_area.bottom() <= point.y() <= self.selected_area.bottom() + ERRORRANGE): self.mousePosition = MousePosition.ON_THE_BOTTOM_LEFT_CORNER elif self.selected_area.right() <= point.x( ) <= self.selected_area.right() + ERRORRANGE and ( self.selected_area.bottom() <= point.y() <= self.selected_area.bottom() + ERRORRANGE): self.mousePosition = MousePosition.ON_THE_BOTTOM_RIGHT_CORNER elif -ERRORRANGE <= point.x() - self.selected_area.left() <= 0 and ( self.selected_area.topLeft().y() < point.y() < self.selected_area.bottomLeft().y()): self.mousePosition = MousePosition.ON_THE_LEFT_SIDE elif 0 <= point.x() - self.selected_area.right() <= ERRORRANGE and ( self.selected_area.topRight().y() < point.y() < self.selected_area.bottomRight().y()): self.mousePosition = MousePosition.ON_THE_RIGHT_SIDE elif -ERRORRANGE <= point.y() - self.selected_area.top() <= 0 and ( self.selected_area.topLeft().x() < point.x() < self.selected_area.topRight().x()): self.mousePosition = MousePosition.ON_THE_UP_SIDE elif 0 <= point.y() - self.selected_area.bottom() <= ERRORRANGE and ( self.selected_area.bottomLeft().x() < point.x() < self.selected_area.bottomRight().x()): self.mousePosition = MousePosition.ON_THE_DOWN_SIDE elif not self.selected_area.contains(point): self.mousePosition = MousePosition.OUTSIDE_AREA else: self.mousePosition = MousePosition.INSIDE_AREA def setCursorStyle(self): if self.action in DRAW_ACTION: self.setCursor(Qt.CrossCursor) return if self.mousePosition == MousePosition.ON_THE_LEFT_SIDE or \ self.mousePosition == MousePosition.ON_THE_RIGHT_SIDE: self.setCursor(Qt.SizeHorCursor) elif self.mousePosition == MousePosition.ON_THE_UP_SIDE or \ self.mousePosition == MousePosition.ON_THE_DOWN_SIDE: self.setCursor(Qt.SizeVerCursor) elif self.mousePosition == MousePosition.ON_THE_TOP_LEFT_CORNER or \ self.mousePosition == MousePosition.ON_THE_BOTTOM_RIGHT_CORNER: self.setCursor(Qt.SizeFDiagCursor) elif self.mousePosition == MousePosition.ON_THE_TOP_RIGHT_CORNER or \ self.mousePosition == MousePosition.ON_THE_BOTTOM_LEFT_CORNER: self.setCursor(Qt.SizeBDiagCursor) elif self.mousePosition == MousePosition.OUTSIDE_AREA: self.setCursor(Qt.ArrowCursor) elif self.mousePosition == MousePosition.INSIDE_AREA: self.setCursor(Qt.OpenHandCursor) else: self.setCursor(Qt.ArrowCursor) pass def drawMagnifier(self): # First, calculate the magnifier position due to the mouse position watch_area_width = 16 watch_area_height = 16 cursor_pos = self.mousePoint watch_area = QRect( QPoint(cursor_pos.x() - watch_area_width / 2, cursor_pos.y() - watch_area_height / 2), QPoint(cursor_pos.x() + watch_area_width / 2, cursor_pos.y() + watch_area_height / 2)) if watch_area.left() < 0: watch_area.moveLeft(0) watch_area.moveRight(watch_area_width) if self.mousePoint.x( ) + watch_area_width / 2 >= self.screenPixel.width(): watch_area.moveRight(self.screenPixel.width() - 1) watch_area.moveLeft(watch_area.right() - watch_area_width) if self.mousePoint.y() - watch_area_height / 2 < 0: watch_area.moveTop(0) watch_area.moveBottom(watch_area_height) if self.mousePoint.y( ) + watch_area_height / 2 >= self.screenPixel.height(): watch_area.moveBottom(self.screenPixel.height() - 1) watch_area.moveTop(watch_area.bottom() - watch_area_height) # tricks to solve the hidpi impact on QCursor.pos() watch_area.setTopLeft( QPoint(watch_area.topLeft().x() * self.scale, watch_area.topLeft().y() * self.scale)) watch_area.setBottomRight( QPoint(watch_area.bottomRight().x() * self.scale, watch_area.bottomRight().y() * self.scale)) watch_area_pixmap = self.screenPixel.copy(watch_area) # second, calculate the magnifier area magnifier_area_width = watch_area_width * 10 magnifier_area_height = watch_area_height * 10 font_area_height = 40 cursor_size = 24 magnifier_area = QRectF( QPoint(QCursor.pos().x() + cursor_size, QCursor.pos().y() + cursor_size), QPoint(QCursor.pos().x() + cursor_size + magnifier_area_width, QCursor.pos().y() + cursor_size + magnifier_area_height)) if magnifier_area.right() >= self.screenPixel.width(): magnifier_area.moveLeft(QCursor.pos().x() - magnifier_area_width - cursor_size / 2) if magnifier_area.bottom( ) + font_area_height >= self.screenPixel.height(): magnifier_area.moveTop(QCursor.pos().y() - magnifier_area_height - cursor_size / 2 - font_area_height) # third, draw the watch area to magnifier area watch_area_scaled = watch_area_pixmap.scaled( QSize(magnifier_area_width * self.scale, magnifier_area_height * self.scale)) magnifier_pixmap = self.graphics_scene.addPixmap(watch_area_scaled) magnifier_pixmap.setOffset(magnifier_area.topLeft()) # then draw lines and text self.graphics_scene.addRect(QRectF(magnifier_area), QPen(QColor(255, 255, 255), 2)) self.graphics_scene.addLine( QLineF( QPointF(magnifier_area.center().x(), magnifier_area.top()), QPointF(magnifier_area.center().x(), magnifier_area.bottom())), QPen(QColor(0, 255, 255), 2)) self.graphics_scene.addLine( QLineF( QPointF(magnifier_area.left(), magnifier_area.center().y()), QPointF(magnifier_area.right(), magnifier_area.center().y())), QPen(QColor(0, 255, 255), 2)) # get the rgb of mouse point point_rgb = QColor(self.screenPixel.toImage().pixel(self.mousePoint)) # draw information self.graphics_scene.addRect( QRectF( magnifier_area.bottomLeft(), magnifier_area.bottomRight() + QPoint(0, font_area_height + 30)), QPen(Qt.black), QBrush(Qt.black)) rgb_info = self.graphics_scene.addSimpleText( ' Rgb: ({0}, {1}, {2})'.format(point_rgb.red(), point_rgb.green(), point_rgb.blue())) rgb_info.setPos(magnifier_area.bottomLeft() + QPoint(0, 5)) rgb_info.setPen(QPen(QColor(255, 255, 255), 2)) rect = self.selected_area.normalized() size_info = self.graphics_scene.addSimpleText( ' Size: {0} x {1}'.format(rect.width() * self.scale, rect.height() * self.scale)) size_info.setPos(magnifier_area.bottomLeft() + QPoint(0, 15) + QPoint(0, font_area_height / 2)) size_info.setPen(QPen(QColor(255, 255, 255), 2)) def get_scale(self): return self.devicePixelRatio() def saveScreenshot(self, clipboard=False, fileName='screenshot.png', picType='png'): fullWindow = QRect(0, 0, self.width() - 1, self.height() - 1) selected = QRect(self.selected_area) if selected.left() < 0: selected.setLeft(0) if selected.right() >= self.width(): selected.setRight(self.width() - 1) if selected.top() < 0: selected.setTop(0) if selected.bottom() >= self.height(): selected.setBottom(self.height() - 1) source = (fullWindow & selected) source.setTopLeft( QPoint(source.topLeft().x() * self.scale, source.topLeft().y() * self.scale)) source.setBottomRight( QPoint(source.bottomRight().x() * self.scale, source.bottomRight().y() * self.scale)) image = self.screenPixel.copy(source) if clipboard: QGuiApplication.clipboard().setImage(image.toImage(), QClipboard.Clipboard) else: image.save(fileName, picType, 10) self.target_img = image self.target_img_pos = source self.screen_shot_grabed.emit(image.toImage()) self.screen_shot_pos_grabed.emit(source) def redraw(self): self.graphics_scene.clear() # draw screenshot self.graphics_scene.addPixmap(self.screenPixel) # prepare for drawing selected area rect = QRectF(self.selected_area) rect = rect.normalized() top_left_point = rect.topLeft() top_right_point = rect.topRight() bottom_left_point = rect.bottomLeft() bottom_right_point = rect.bottomRight() top_middle_point = (top_left_point + top_right_point) / 2 left_middle_point = (top_left_point + bottom_left_point) / 2 bottom_middle_point = (bottom_left_point + bottom_right_point) / 2 right_middle_point = (top_right_point + bottom_right_point) / 2 # draw the picture mask mask = QColor(0, 0, 0, 155) if self.selected_area == QRect(): self.graphics_scene.addRect(0, 0, self.screenPixel.width(), self.screenPixel.height(), QPen(Qt.NoPen), mask) else: self.graphics_scene.addRect(0, 0, self.screenPixel.width(), top_right_point.y(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect(0, top_left_point.y(), top_left_point.x(), rect.height(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect( top_right_point.x(), top_right_point.y(), self.screenPixel.width() - top_right_point.x(), rect.height(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect( 0, bottom_left_point.y(), self.screenPixel.width(), self.screenPixel.height() - bottom_left_point.y(), QPen(Qt.NoPen), mask) # draw the toolBar if self.action != ACTION_SELECT: spacing = 5 # show the toolbar first, then move it to the correct position # because the width of it may be wrong if this is the first time it shows self.tooBar.show() dest = QPointF(rect.bottomRight() - QPointF(self.tooBar.width(), 0) - QPointF(spacing, -spacing)) if dest.x() < spacing: dest.setX(spacing) pen_set_bar_height = self.penSetBar.height( ) if self.penSetBar is not None else 0 if dest.y() + self.tooBar.height( ) + pen_set_bar_height >= self.height(): if rect.top() - self.tooBar.height( ) - pen_set_bar_height < spacing: dest.setY(rect.top() + spacing) else: dest.setY(rect.top() - self.tooBar.height() - pen_set_bar_height - spacing) self.tooBar.move(dest.toPoint()) if self.penSetBar is not None: self.penSetBar.show() self.penSetBar.move(dest.toPoint() + QPoint(0, self.tooBar.height() + spacing)) if self.action == ACTION_TEXT: self.penSetBar.showFontWidget() else: self.penSetBar.showPenWidget() else: self.tooBar.hide() if self.penSetBar is not None: self.penSetBar.hide() # draw the list for step in self.drawListResult: self.drawOneStep(step) if self.drawListProcess is not None: self.drawOneStep(self.drawListProcess) if self.action != ACTION_TEXT: self.drawListProcess = None if self.selected_area != QRect(): self.items_to_remove = [] # draw the selected rectangle pen = QPen(QColor(0, 255, 255), 2) self.items_to_remove.append(self.graphics_scene.addRect(rect, pen)) # draw the drag point radius = QPoint(3, 3) brush = QBrush(QColor(0, 255, 255)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_left_point - radius, top_left_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_middle_point - radius, top_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_right_point - radius, top_right_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(left_middle_point - radius, left_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(right_middle_point - radius, right_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_left_point - radius, bottom_left_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_middle_point - radius, bottom_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_right_point - radius, bottom_right_point + radius), pen, brush)) # draw the textedit if self.textPosition is not None: textSpacing = 50 position = QPoint() if self.textPosition.x() + self.textInput.width( ) >= self.screenPixel.width(): position.setX(self.textPosition.x() - self.textInput.width()) else: position.setX(self.textPosition.x()) if self.textRect is not None: if self.textPosition.y() + self.textInput.height( ) + self.textRect.height() >= self.screenPixel.height(): position.setY(self.textPosition.y() - self.textInput.height() - self.textRect.height()) else: position.setY(self.textPosition.y() + self.textRect.height()) else: if self.textPosition.y() + self.textInput.height( ) >= self.screenPixel.height(): position.setY(self.textPosition.y() - self.textInput.height()) else: position.setY(self.textPosition.y()) self.textInput.move(position) self.textInput.show() # self.textInput.getFocus() # draw the magnifier if self.action == ACTION_SELECT: self.drawMagnifier() if self.mousePressed: self.drawSizeInfo() if self.action == ACTION_MOVE_SELECTED: self.drawSizeInfo() # deal with every step in drawList def drawOneStep(self, step): """ :type step: tuple """ if step[0] == ACTION_RECT: self.graphics_scene.addRect( QRectF(QPointF(step[1], step[2]), QPointF(step[3], step[4])), step[5]) elif step[0] == ACTION_ELLIPSE: self.graphics_scene.addEllipse( QRectF(QPointF(step[1], step[2]), QPointF(step[3], step[4])), step[5]) elif step[0] == ACTION_ARROW: arrow = QPolygonF() linex = float(step[1] - step[3]) liney = float(step[2] - step[4]) line = sqrt(pow(linex, 2) + pow(liney, 2)) # in case to divided by 0 if line == 0: return sinAngel = liney / line cosAngel = linex / line # sideLength is the length of bottom side of the body of an arrow # arrowSize is the size of the head of an arrow, left and right # sides' size is arrowSize, and the bottom side's size is arrowSize / 2 sideLength = step[5].width() arrowSize = 8 bottomSize = arrowSize / 2 tmpPoint = QPointF(step[3] + arrowSize * sideLength * cosAngel, step[4] + arrowSize * sideLength * sinAngel) point1 = QPointF(step[1] + sideLength * sinAngel, step[2] - sideLength * cosAngel) point2 = QPointF(step[1] - sideLength * sinAngel, step[2] + sideLength * cosAngel) point3 = QPointF(tmpPoint.x() - sideLength * sinAngel, tmpPoint.y() + sideLength * cosAngel) point4 = QPointF(tmpPoint.x() - bottomSize * sideLength * sinAngel, tmpPoint.y() + bottomSize * sideLength * cosAngel) point5 = QPointF(step[3], step[4]) point6 = QPointF(tmpPoint.x() + bottomSize * sideLength * sinAngel, tmpPoint.y() - bottomSize * sideLength * cosAngel) point7 = QPointF(tmpPoint.x() + sideLength * sinAngel, tmpPoint.y() - sideLength * cosAngel) arrow.append(point1) arrow.append(point2) arrow.append(point3) arrow.append(point4) arrow.append(point5) arrow.append(point6) arrow.append(point7) arrow.append(point1) self.graphics_scene.addPolygon(arrow, step[5], step[6]) elif step[0] == ACTION_LINE: self.graphics_scene.addLine( QLineF(QPointF(step[1], step[2]), QPointF(step[3], step[4])), step[5]) elif step[0] == ACTION_FREEPEN: self.graphics_scene.addPath(step[1], step[2]) elif step[0] == ACTION_TEXT: textAdd = self.graphics_scene.addSimpleText(step[1], step[2]) textAdd.setPos(step[3]) textAdd.setBrush(QBrush(step[4])) self.textRect = textAdd.boundingRect() # draw the size information on the top left corner def drawSizeInfo(self): sizeInfoAreaWidth = 200 sizeInfoAreaHeight = 30 spacing = 5 rect = self.selected_area.normalized() sizeInfoArea = QRect(rect.left(), rect.top() - spacing - sizeInfoAreaHeight, sizeInfoAreaWidth, sizeInfoAreaHeight) if sizeInfoArea.top() < 0: sizeInfoArea.moveTopLeft(rect.topLeft() + QPoint(spacing, spacing)) if sizeInfoArea.right() >= self.screenPixel.width(): sizeInfoArea.moveTopLeft(rect.topLeft() - QPoint(spacing, spacing) - QPoint(sizeInfoAreaWidth, 0)) if sizeInfoArea.left() < spacing: sizeInfoArea.moveLeft(spacing) if sizeInfoArea.top() < spacing: sizeInfoArea.moveTop(spacing) self.items_to_remove.append( self.graphics_scene.addRect(QRectF(sizeInfoArea), QPen(Qt.white), QBrush(Qt.black))) sizeInfo = self.graphics_scene.addSimpleText(' {0} x {1}'.format( rect.width() * self.scale, rect.height() * self.scale)) sizeInfo.setPos(sizeInfoArea.topLeft() + QPoint(0, 2)) sizeInfo.setPen(QPen(QColor(255, 255, 255), 2)) self.items_to_remove.append(sizeInfo) def drawRect(self, x1, x2, y1, y2, result): rect = self.selected_area.normalized() tmpRect = QRect(QPoint(x1, x2), QPoint(y1, y2)).normalized() resultRect = rect & tmpRect tmp = [ ACTION_RECT, resultRect.topLeft().x(), resultRect.topLeft().y(), resultRect.bottomRight().x(), resultRect.bottomRight().y(), QPen(QColor(self.penColorNow), int(self.penSizeNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def drawEllipse(self, x1, x2, y1, y2, result): rect = self.selected_area.normalized() tmpRect = QRect(QPoint(x1, x2), QPoint(y1, y2)).normalized() resultRect = rect & tmpRect tmp = [ ACTION_ELLIPSE, resultRect.topLeft().x(), resultRect.topLeft().y(), resultRect.bottomRight().x(), resultRect.bottomRight().y(), QPen(QColor(self.penColorNow), int(self.penSizeNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def drawArrow(self, x1, x2, y1, y2, result): rect = self.selected_area.normalized() if y1 <= rect.left(): y1 = rect.left() elif y1 >= rect.right(): y1 = rect.right() if y2 <= rect.top(): y2 = rect.top() elif y2 >= rect.bottom(): y2 = rect.bottom() tmp = [ ACTION_ARROW, x1, x2, y1, y2, QPen(QColor(self.penColorNow), int(self.penSizeNow)), QBrush(QColor(self.penColorNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def drawLine(self, x1, x2, y1, y2, result): rect = self.selected_area.normalized() if y1 <= rect.left(): y1 = rect.left() elif y1 >= rect.right(): y1 = rect.right() if y2 <= rect.top(): y2 = rect.top() elif y2 >= rect.bottom(): y2 = rect.bottom() tmp = [ ACTION_LINE, x1, x2, y1, y2, QPen(QColor(self.penColorNow), int(self.penSizeNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def drawFreeLine(self, pointPath, result): tmp = [ ACTION_FREEPEN, QPainterPath(pointPath), QPen(QColor(self.penColorNow), int(self.penSizeNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def textChange(self): if self.textPosition is None: return self.text = self.textInput.getText() self.drawListProcess = [ ACTION_TEXT, str(self.text), QFont(self.fontNow), QPoint(self.textPosition), QColor(self.penColorNow) ] self.redraw() def undoOperation(self): if len(self.drawListResult) == 0: self.action = ACTION_SELECT self.selected_area = QRect() self.selectedAreaRaw = QRect() self.tooBar.hide() if self.penSetBar is not None: self.penSetBar.hide() else: self.drawListResult.pop() self.redraw() def saveOperation(self): filename = QFileDialog.getSaveFileName(self, 'Save file', './screenshot.png', '*.png;;*.jpg') if len(filename[0]) == 0: return else: self.saveScreenshot(False, filename[0], filename[1][2:]) self.close() def close(self): self.widget_closed.emit() super().close() self.tooBar.close() if self.penSetBar is not None: self.penSetBar.close() def saveToClipboard(self): QApplication.clipboard().setText('Test in save function') self.saveScreenshot(True) self.close() # slots def changeAction(self, nextAction): QApplication.clipboard().setText('Test in changeAction function') if nextAction == ACTION_UNDO: self.undoOperation() elif nextAction == ACTION_SAVE: self.saveOperation() elif nextAction == ACTION_CANCEL: self.close() elif nextAction == ACTION_SURE: self.saveToClipboard() else: self.action = nextAction self.setFocus() def changePenSize(self, nextPenSize): self.penSizeNow = nextPenSize def changePenColor(self, nextPenColor): self.penColorNow = nextPenColor def cancelInput(self): self.drawListProcess = None self.textPosition = None self.textRect = None self.textInput.hide() self.textInput.clearText() self.redraw() def okInput(self): self.text = self.textInput.getText() self.drawListResult.append([ ACTION_TEXT, str(self.text), QFont(self.fontNow), QPoint(self.textPosition), QColor(self.penColorNow) ]) self.textPosition = None self.textRect = None self.textInput.hide() self.textInput.clearText() self.redraw() def changeFont(self, font): self.fontNow = font