吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1540|回复: 17
收起左侧

[Python 原创] 【Python】用鼠标点出像素画工具

  [复制链接]
DeviLeo 发表于 2024-4-8 16:59

0x0 前言

老母亲要给小孙女的衣服上缝上图案,于是去百度搜了几张图,画质堪忧,格子也没有标序号,几乎不能看。
想着做个小工具,一来练练手,二来方便修改,导出图片后打印也清晰。

0x1 环境

  • python3.7+
  • pillow
  • pyqt6
# 安装pillow
$ python3 -m pip install pillow

# 安装pyqt6
$ python3 -m pip install pyside6

0x2 核心代码

Stitch类主要实现了像素画棋盘格的大小、位置的计算,根据传入的鼠标位置获取对应的格子位置,以及保存和加载功能。

class Stitch():
    def __init__(self, **kwargs) -> None:
        self.line_normal = kwargs.get('line_normal', 1)
        self.line_thick = kwargs.get('line_thick', 2)
        self.thick_per_count = kwargs.get('thick_per_count', 5)

        self.row = kwargs['row']
        self.col = kwargs['col']
        self.row_size = kwargs['row_size']
        self.col_size = kwargs['col_size']
        self.pad = kwargs['pad']

        self.color_map = kwargs.get('color_map')
        self.generate_cell_map()

        self.update_board_size()

    def update_board_size(self):

        self.h = self.get_board_size(self.row, self.row_size, self.thick_per_count, self.line_normal, self.line_thick)
        self.w = self.get_board_size(self.col, self.col_size, self.thick_per_count, self.line_normal, self.line_thick)

    def save_json(self, file):
        json_dict = {
            "row": self.row,
            "col": self.col,
            "row_size": self.row_size,
            "col_size": self.col_size,
            "pad": self.pad,
            "line_normal": self.line_normal,
            "line_thick": self.line_thick,
            "thick_per_count": self.thick_per_count,
            "color_map": self.color_map
        }
        with open(file, 'w') as f:
            json.dump(json_dict, f)

    @classmethod
    def default(cls):
        json_dict = {
            'line_normal': 1,
            'line_thick': 1,
            'thick_per_count': 5,
            'row': 32,
            'col': 32,
            'row_size': 16,
            'col_size': 16,
            'pad': 32,
            'color_map': {}
        }
        return cls(**json_dict)

    @classmethod
    def from_json(cls, file):
        with open(file, 'r') as f:
            json_dict = json.load(f)
            return cls(**json_dict)

    def generate_cell_map(self):
        self.cell_map = {}
        if self.color_map is None:
            self.color_map = {}

        for color, cells in self.color_map.items():
            for row, col in cells:
                cell_id = self.get_cell_id(row, col)
                self.cell_map[cell_id] = color

    def update_color_map(self, row, col, hex_color):
        cell_id = self.get_cell_id(row, col)
        if cell_id is None:
            return

        pt = [row, col]
        prev_color = self.cell_map.get(cell_id, None)
        if prev_color is not None:
            if prev_color in self.color_map:
                cmap = self.color_map[prev_color]
                if pt in cmap:
                    cmap.remove(pt)

        if hex_color is not None:
            if hex_color in self.color_map:
                self.color_map[hex_color].append(pt)
            else:
                self.color_map[hex_color] = [pt]

        self.cell_map[cell_id] = hex_color

    def get_board_lines(self):
        rows, cols = [], []

        x0, y0 = self.pad, self.pad

        # draw rows
        x1 = x0
        x2 = x0 + self.w - 1
        for i in range(self.row+1):
            n = i + 1
            is_thick = i % self.thick_per_count == 0 or i == self.row
            y = self.get_line_pos(n, self.row_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)
            rows.append([(x1, y), (x2, y), is_thick])

        # draw cols
        y1 = y0
        y2 = y0 + self.h - 1
        for i in range(self.col+1):
            n = i + 1
            is_thick = i % self.thick_per_count == 0 or i == self.col
            x = self.get_line_pos(n, self.col_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)
            cols.append([(x, y1), (x, y2), is_thick])

        return rows, cols

    def get_board_str(self):
        rows, cols = [], []

        row_font_size = self.row_size
        col_font_size = self.col_size
        x0, y0 = self.pad, self.pad

        # draw row text
        x1 = x0
        x2 = x0 + self.w - 1
        for i in range(self.row):
            n = i + 1
            if n % self.thick_per_count == 0 or i == self.row:
                y = self.get_cell_pos(n, self.row_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)
                y += (self.row_size + row_font_size) / 2
                rows.append([x1-row_font_size-2, y, str(n)])
                rows.append([x2+2, y, str(n)])

        # draw col text
        # draw cols
        y1 = y0
        y2 = y0 + self.h - 1
        for i in range(self.col):
            n = i + 1
            if n % self.thick_per_count == 0 or i == self.col:
                x = self.get_cell_pos(n, self.col_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)
                x += (self.col_size - col_font_size) / 2 
                rows.append([x, y1-2, str(n)])
                rows.append([x, y2+col_font_size+2, str(n)])

        return rows, cols

    def get_cell_id(self, row, col):
        if row is None or col is None:
            return None

        cell_id = (row - 1) * self.row_size + col
        return cell_id

    def get_cell_qrect(self, row, col):
        rect = self.get_cell_rect(row, col, self.row_size, self.col_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)
        qrect = [rect[0], rect[1], rect[2]-rect[0]+1, rect[3]-rect[1]+1]
        return qrect

    def get_cell(self, x, y):
        cell_row = self.get_cell_by_pos(y, self.row_size, self.h, self.row)
        cell_col = self.get_cell_by_pos(x, self.col_size, self.w, self.col)
        cell_id = None
        cell_qrect = None
        if cell_row is not None and cell_col is not None:
            cell_qrect = self.get_cell_qrect(cell_row, cell_col)
            cell_id = self.get_cell_id(cell_row, cell_col)
        return cell_row, cell_col, cell_id, cell_qrect

    def get_cell_by_pos(self, pos, size, boarder_size, cell_count):
        pos -= self.pad

        if pos < 0:
            return None

        if pos > self.pad + boarder_size:
            return None

        block_size = self.line_thick + (size + self.line_normal) * self.thick_per_count - self.line_normal
        block_i = pos // block_size
        block_bias = pos % block_size
        line_bias = self.line_thick - self.line_normal
        cell_size = self.line_normal + size
        cell_n = (block_bias - line_bias) // cell_size + 1

        cell = block_i * self.thick_per_count + cell_n
        if cell < 1 or cell > cell_count:
            cell = None

        return cell

    def save_image(self, file):

        print(f'border size: {(self.w, self.h)}')

        im_w = self.w + self.pad * 2
        im_h = self.h + self.pad * 2
        print(f'canvas size: {(im_w, im_h)}')
        im = Image.new('RGB', size=(im_w, im_h), color='white')
        draw = ImageDraw.Draw(im)

        color_black = "#000000"
        color_gray = "#cccccc"

        x0, y0 = self.pad, self.pad

        draw_later_lines = []

        # draw rows
        x1 = x0
        x2 = x0 + self.w - 1
        for i in range(self.row+1):
            n = i + 1
            y = self.get_line_pos(n, self.row_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)

            is_thick = i % self.thick_per_count == 0 or i == self.row
            t = self.line_thick if is_thick else self.line_normal
            c = color_black if is_thick else color_gray

            if is_thick:
                draw_later_lines.append([[(x1, y), (x2, y)], c, t])
            else:
                draw.line([(x1, y), (x2, y)], c, t)
                print(f'[row][{n}] pt1: {(x1, y)}, pt2: {(x2, y)}')

        # draw cols
        y1 = y0
        y2 = y0 + self.h - 1
        for i in range(self.col+1):
            n = i + 1
            x = self.get_line_pos(n, self.col_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)

            is_thick = i % self.thick_per_count == 0 or i == self.col
            t = self.line_thick if is_thick else self.line_normal
            c = color_black if is_thick else color_gray

            if is_thick:
                draw_later_lines.append([[(x, y1), (x, y2)], c, t])
            else:
                draw.line([(x, y1), (x, y2)], c, t)
                print(f'[col][{n}] pt1: {(x, y1)}, pt2: {(x, y1)}')

        if len(draw_later_lines) > 0:
            for i, l in enumerate(draw_later_lines):
                n = i + 1
                draw.line(l[0], l[1], l[2])
                print(f'[thick][{n}] pt1: {l[0][0]}, pt2: {l[0][1]}')

        # row_font = ImageFont.truetype("Ubuntu-M.ttf", self.row_size)
        # col_font = ImageFont.truetype("Ubuntu-M.ttf", self.col_size)
        row_font = ImageFont.truetype("simhei.ttf", self.row_size)
        col_font = ImageFont.truetype("simhei.ttf", self.col_size)
        # draw row text
        for i in range(self.row):
            n = i + 1
            if n % self.thick_per_count == 0 or i == self.row:
                y = self.get_cell_mid_pos(n, self.row_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)
                draw.text((x1-2, y), str(n), anchor="rm", fill=color_black, font=row_font)
                draw.text((x2+2, y), str(n), anchor="lm", fill=color_black, font=row_font)

        # draw col text
        for i in range(self.col):
            n = i + 1
            if n % self.thick_per_count == 0 or i == self.col:
                x = self.get_cell_mid_pos(n, self.col_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)
                draw.text((x, y1-2), str(n), anchor="md", fill=color_black, font=col_font)
                draw.text((x, y2+2), str(n), anchor="ma", fill=color_black, font=col_font)

        color_map = self.color_map
        if color_map is not None:
            for color, cells in color_map.items():
                if not color.startswith('#'):
                    color = '#' + color

                for cell in cells:
                    rect = self.get_cell_rect(cell[0], cell[1], self.row_size, self.col_size, self.pad, self.thick_per_count, self.line_normal, self.line_thick)
                    draw.rectangle(rect, fill=color, width=0)

        im.save(file)
        print(f'Saved image as "{file}"')

    @classmethod
    def convert_hex_to_rgb(self, hex):
        hex = hex.strip('#').strip()
        if len(hex) != 6:
            return None

        r = int(hex[0:2], 16)
        g = int(hex[2:4], 16)
        b = int(hex[4:6], 16)
        return r, g, b

    @classmethod
    def convert_rgb_to_hex(self, r, g, b):
        color = hex(r)[2:].zfill(2)
        color += hex(g)[2:].zfill(2)
        color += hex(b)[2:].zfill(2)
        return color

    @classmethod
    def get_board_size(cls, n, size, thick_per_count, line_normal, line_thick):
        return n * size + (n + 1) * line_normal + (math.ceil(n / thick_per_count) + 1) * (line_thick - line_normal)

    @classmethod
    def get_line_pos(cls, n, size, pad, thick_per_count, line_normal, line_thick):
        i = n - 1
        return pad + i * size + i * line_normal + math.ceil(i / thick_per_count) * (line_thick - line_normal)

    @classmethod
    def get_cell_pos(cls, n, size, pad, thick_per_count, line_normal, line_thick):
        return pad + (n - 1) * size + n * line_normal + math.ceil(n / thick_per_count) * (line_thick - line_normal)

    @classmethod
    def get_cell_mid_pos(cls, n, size, pad, thick_per_count, line_normal, line_thick):
        x = cls.get_cell_pos(n, size, pad, thick_per_count, line_normal, line_thick)
        return x + math.floor(size / 2) - 1

    @classmethod
    def get_cell_rect(cls, row, col, row_size, col_size, pad, thick_per_count, line_normal, line_thick):
        x1 = cls.get_cell_pos(col, col_size, pad, thick_per_count, line_normal, line_thick)
        y1 = cls.get_cell_pos(row, row_size, pad, thick_per_count, line_normal, line_thick)
        x2 = x1 + col_size - 1
        y2 = y1 + row_size - 1
        return (x1, y1, x2, y2)

0x4 如何运行

$ cd draw_stitch_gui
$ python3 -u draw_stitch_gui.py

0x5 效果图

screenshot.png

0x6 完整源码

用鼠标点出像素画工具.7z (61.64 KB, 下载次数: 51)


解压密码:52pojie.cn

免费评分

参与人数 3吾爱币 +8 热心值 +2 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
redhat1019 + 1 谢谢@Thanks!
5Axi + 1 热心回复!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

wkdxz 发表于 2024-4-8 17:51
简单实用的软件,谢谢分享!
atoms 发表于 2024-4-8 19:11
楼主这个真不错 之前我知道一个在线画像素图像的 https://www.pixilart.com/draw  虽然功能很强 但是是英文的
a5323671 发表于 2024-4-8 17:57
jiushiyaole 发表于 2024-4-8 17:58
很方便的工具
qhkm99 发表于 2024-4-8 18:10
谢谢分享工具
1e3e 发表于 2024-4-8 18:43
谢谢分享呀
LightswornSnow 发表于 2024-4-8 19:16
标序号以前还真没考虑过,挺好的想法,参考一下
FHWX 发表于 2024-4-8 19:18
感谢分享!
yuzilin 发表于 2024-4-8 22:17
感谢分享呀~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 23:13

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表