PyGame - 在记忆中画画

时间:2017-04-23 22:20:48

标签: python graphics bitmap pygame

我在Windows上使用了很多GDI +,并尝试在内存中创建一些位图/图像,以便以后可以绘制它们而无需绘制它们的所有部分。

在GDI +中很容易在内存中绘制位图,但我不知道如何使用PyGame在Python中执行此操作,而且我无法在内存中找到任何教程/文档。

目前正在尝试创建一些按钮。它将是一个纯色矩形,边框和一些文字。不是非常密集地每次画几次3次,但绘制1个图形的强度仍然不高于3.(另外按钮会有几种状态,比如窗口。就像按下或鼠标悬停在按钮上一样)

1 个答案:

答案 0 :(得分:0)

想出来,用pygame做不到,但PIL可以做到!

controls = []

SS_LEFT = 0x0000
SS_RIGHT = 0x0002
BS_BOTTOM = 0x0800
BS_CENTER = 0x0300
BS_DEFPUSHBUTTON = 0x0001
BS_MULTILINE = 0x2000
BS_TOP = 0x0400
BS_VCENTER = 0x0C00
BS_ICON = 0x0040
BS_BITMAP = 0x0080
BS_FLAT = 0x8000
BS_NOTIFY = 0x4000

BUTTON_PROPERTIES = {'bk_color_normal': (51, 51, 51), 'border_color_normal': (51, 51, 51), 'text_color_normal': (241, 241, 241),
                     'bk_color_hot': (16, 16, 16), 'border_color_hot': (16, 16, 16), 'text_color_hot': (241, 241, 241),
                     'bk_color_pressed': (0, 122, 204), 'border_color_pressed': (0, 122, 204), 'text_color_pressed': (241, 241, 241),
                     'bk_color_disabled': (82, 82, 82), 'border_color_disabled': (118, 118, 118), 'text_color_disabled': (124, 124, 124),
                     'px_border': 3}

def create_button(text, x, y, width, height,
                  properties=BUTTON_PROPERTIES,
                  style=BS_CENTER | BS_VCENTER | BS_MULTILINE):
    rect = pygame.Rect(properties['px_border'], properties['px_border'], width - (properties['px_border'] * 2), height - (properties['px_border'] * 2))
    tmp_font = ImageFont.truetype('segoeui.ttf', 12)
    lines_of_string = format_string(text, tmp_font, rect).splitlines()

    bk_normal = Image.new('RGBA', size=(width, height), color=properties['border_color_normal'])
    bk_hot = Image.new('RGBA', size=(width, height), color=properties['border_color_hot'])
    bk_pressed = Image.new('RGBA', size=(width, height), color=properties['border_color_pressed'])
    bk_disabled = Image.new('RGBA', size=(width, height), color=properties['border_color_disabled'])

    image_normal = ImageDraw.Draw(bk_normal)
    image_hot = ImageDraw.Draw(bk_hot)
    image_pressed = ImageDraw.Draw(bk_pressed)
    image_disabled = ImageDraw.Draw(bk_disabled)

    image_normal.rectangle([(properties['px_border'], properties['px_border']),
                            (width - properties['px_border'] - 1, height - properties['px_border'] - 1)],
                           properties['bk_color_normal'])
    image_hot.rectangle([(properties['px_border'], properties['px_border']),
                         (width - properties['px_border'] - 1, height - properties['px_border'] - 1)],
                        properties['bk_color_hot'])
    image_pressed.rectangle([(properties['px_border'], properties['px_border']),
                             (width - properties['px_border'] - 1, height - properties['px_border'] - 1)],
                            properties['bk_color_pressed'])
    image_disabled.rectangle([(properties['px_border'], properties['px_border']),
                              (width - properties['px_border'] - 1, height - properties['px_border'] - 1)],
                             properties['bk_color_disabled'])

    if lines_of_string:
        i = 0
        text_y = 0
        font_height = tmp_font.getsize(text)[1] + 3

        # Get vertical alignment
        if style & BS_VCENTER == BS_VCENTER:
            if style & BS_MULTILINE == BS_MULTILINE:
                # get the number of lines that will fit in the area
                while i < len(lines_of_string):
                    if i * font_height < rect.height:
                        i += 1
                    else:
                        break
                # calculate the starting position of y
                # will center the number of lines it can draw
                text_y = int(rect.y + int(rect.height / 2) - ((i * font_height) / 2))
                # text_y is going to start above the actual start position of y, re-adjust
                if text_y < rect.y:
                    text_y = rect.y
            else:
                lines_of_string = lines_of_string[:1]
                text_y = rect[1] + (rect[3] / 2) - (font_height / 2)
        elif style & BS_BOTTOM == BS_BOTTOM:
            lines_of_string = reversed(lines_of_string)
            text_y = rect[3] - font_height
            font_height *= -1
        elif style & BS_TOP == BS_TOP:
            text_y = rect.y

        for line in lines_of_string:
            #
            if style & SS_RIGHT == SS_RIGHT:
                # check that the text_y value is not above the top of the rect
                if text_y < rect.top:
                    break
            # if the text_y value + the current height needed to draw this line goes below the bottom
            elif text_y + font_height > rect.bottom:
                break

            # get the width of this line
            line_width = tmp_font.getsize(line)[0]

            # draw in the center
            if style & BS_CENTER == BS_CENTER:
                row_x = rect.left + ((rect.width / 2) - (line_width / 2))
                row_y = text_y
            # draw on the right side
            elif style & SS_RIGHT == SS_RIGHT:
                row_x = rect.right - line_width
                row_y = text_y
            # draw on the left side
            else:
                row_x = rect.left
                row_y = text_y

            image_normal.text((row_x, row_y), line, font=tmp_font, fill=properties['text_color_normal'])
            image_hot.text((row_x, row_y), line, font=tmp_font, fill=properties['text_color_hot'])
            image_pressed.text((row_x, row_y), line, font=tmp_font, fill=properties['text_color_pressed'])
            image_disabled.text((row_x, row_y), line, font=tmp_font, fill=properties['text_color_disabled'])
            # adjust the text_y position
            text_y += font_height
    # Add the newly created buttons to the controls list
    # can use surface.blit(controls[id]['hot'], controls[id]['rect']) to draw the button
    controls.append({'draw': True,
                     'state': 'normal',
                     'dbl_click': False,
                     'on_event': None,
                     'on_dbl_click': None,
                     'timer': 0,
                     'dbl_timer': time.clock(),
                     'rect': pygame.Rect(x, y, width, height),
                     'normal': pygame.image.fromstring(bk_normal.tobytes(), bk_normal.size, bk_normal.mode),
                     'hot': pygame.image.fromstring(bk_hot.tobytes(), bk_hot.size, bk_hot.mode),
                     'pressed': pygame.image.fromstring(bk_pressed.tobytes(), bk_pressed.size, bk_pressed.mode),
                     'disabled': pygame.image.fromstring(bk_disabled.tobytes(), bk_disabled.size, bk_disabled.mode)})
    bk_normal.save('normal.png')
    bk_hot.save('hot.png')
    bk_pressed.save('pressed.png')
    bk_disabled.save('disabled.png')
    return len(controls)

def format_string(string, font, rect):
    if not isinstance(string, str):
        string = str(string)

    if str(type(font)).find('pygame') > -1:
        size_func = font.size
    else:
        size_func = font.getsize

    lines_of_string = string.splitlines()

    # string that will hold the newly formatted string
    new_string = ''

    for line in lines_of_string:
        if line == '':
            new_string += "\n"
        else:
            while line:
                i = 0

                # start building this line
                while size_func(line[:i])[0] < rect.width and i < len(line):
                    i += 1

                # i is less than the length of this line
                if i < len(line):
                    # find the last word in this line up until the i position
                    i = line.rfind(' ', 0, i) + 1

                    # no words found, this string is way too long to be drawn in this area
                    if i == 0:
                        return ''
                    else:
                        # append the fitted line to new_string, trimming the trailing ' ' character and add the linefeed
                        new_string += line[:i - 1] + '\n'
                # this whole line fits
                else:
                    i = len(line)
                    new_string += line[:i] + '\n'

                # trim the string we took out of this line
                line = line[i:]
    # return the properly formatted string, complete with newlines
    return new_string