Pygame 實作 GUI(01)Label

前言

GUI ( Graphical User Interface ) 中文稱之爲『圖形用戶界面』。在Python 中,雖有默認的 UI 工具包 Tkinter,但如何與 pygame 融合使用?卻不是簡單易解的事。遊戲中的用戶界面,除了中文輸入外,其他的部份用 pygame 來實作並非難事。既然易用的輪子難尋,索性就自己建造屬於自己的 GUI 系統。

顯示文字

在 pygame 中要顯示文字,代碼如下:

import pygame

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
my_font = pygame.font.Font("simsun.ttc", 64)
text_surface = my_font.render(u"你好!", True, (0, 0, 255))

# 遊戲主循環
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

    screen.blit(text_surface, (100, 100))
    pygame.display.update()

首先要使用 pygame.font.Font() 創建一個 Font 對象,然後利用 Font 對象的 render 方法創建一個 Surface 對象,其上面就有繪製的文本。最後在遊戲的主循環中將此 Surface 對象顯示到屏幕上。

瞭解文字在 pygame 中的顯示方式,就可以設計一個用於顯示文字的控件——Label 控件。

Label 控件

設計一個 Label 類,對應 Label 控件,主要功能就是用來顯示文字。創建時要設定顯示的位置及文字內容,當然使用的字體、大小、字色及底色,也要一併設定。例如:

label_1 = Label(left, top, text, font_info)

其中的 font_info 是一個字典結構,內容如下:

font_info = {}
font_info["font"] = pygame.font.Font('font/msjh.ttc', 36)
font_info["tc"] = (255, 255, 255)    # 文字顏色
font_info["bc"] = (0, 0, 0)          # 背景顏色
font_info["align"] = 0               # 靠左
font_info["valign"] = 0              # 靠上

render 方法

使用render() 將 Label 控件顯示到屏幕上:

label.render(screen)

render() 代碼如下:

def render(self, surface):
    if self.text != "" and self.status >= 0:
        surface.blit(self.text_surface, (self.display_x, self.display_y))

如果沒有文字內容或被設定成隱藏(status < 0)就不顯示,否則就顯示在屏幕的 (display_x, display_y) 位置上。創建 Label 時,給的是 ( left, top ) 位置,但因爲文字可能靠左、居中或靠右,所以經過計算後,真正的顯示位置爲 (display_x, display_y)。

set_align 方法

更改文字顯示時,水平的對齊方式,0 表靠左、1 表居中,而 2 就代表靠右。

label.set_align(2)

set_align() 代碼如下:

def set_align(self, align):
    w, h = self.__get_size()
        
    if align == 2:
        self.display_x = self.left - w
    elif align == 1:
        self.display_x = self.left - int(w / 2)
    else:
        self.display_x = self.left

要判斷居中或靠右的位置時,必須知道文字的 surface 對象的大小,所以呼叫 __get_size() 取得其大小,然後再調整 x 座標。此處只處理水平居中及靠右的問題,所以忽略了垂直 y 座標的調整。

def __get_size(self):
    if self.text_surface is None:
        return (0, 0)
    else:
        return self.text_surface.get_size()    # return (w, h)

set_valign 方法

更改文字顯示時,垂直的對齊方式,0 表靠上、1 表居中,而 2 就代表靠下。

label.set_valign(1)

set_valign() 代碼如下:

def set_valign(self, valign):
    w, h = self.get_size()
    if valign == 2:
        self.display_y = self.pos_y - h
    elif valign == 1:
        self.display_y = self.pos_y - int(h / 2)
    else:
        self.display_y = self.pos_y

set_text 方法

用set_text() 方法來更改文字內容。因爲更改文字時會重新建立Surface 對象,所以就可以順便更改文字顏色及背景顏色。

label.set_text(text, tc=None, bc=None) 

set_text() 代碼如下:

def set_text(self, text, tc=None, bc=None):
    self.text = text

    if tc is not None:
        self.tc = tc

    if bc is not None:
        self.bc = bc

    self.text_surface = self.font.render(self.text, True, self.tc, self.bc)

如果 tc 或 bc 爲 None,表示使用原來的字色及底色。

set_pos 方法

用 set_pos() 更改顯示的位置:

Label.set_pos(left, top)

set_pos() 代碼如下:

def set_pos(self, left, top, align=None, valign=None):
    self.left = left
    self.top = top
    
    if align is not None:
        self.align = align
        
    if valign is not None:
        self.valign = valing
        
    self.set_align(self.align)
    self.set_valign(self.valign)

重新設定 (left, top) 後,要再重新依對齊方式計算顯示的 x, y 位置。

set_hide 方法

Label 控件雖然用來顯示文字,但有時必須暫時隱藏不顯示,所以就用 set_hide 方法來設定顯示或隱藏。

label.set_hide(True)

set_hide() 代碼如下:

def set_hide(self, flag):
    if flag:
        self.status = -1
    else:
        self.status = 0

使用範例:

將所有的 Label 控件用一個 labels 字典來管理,創建的同時,也給定一個名稱,以便將來個別的管理。例如:

labels = {}

labels["label_1"] = Label(x1, y1, text1, font_info)
labels["label_2"] = Label(x2, y2, text2, font_info)
labels["label_3"] = Label(x3, y3, text3, font_info)

顯示所有的 Label 控件的方式如下:

for label in labels.values():
    label.render(screen)

針對某個 label 做處理時,例如:

Labels["label_1"].set_text("重新設定文字內容!")

完整代碼

class Label(object):
    def __init__(self, left, top, text, font_info):
        self.status = 0                      # <0 :- 不顯示。
        self.left = left
        self.top = top
        self.text = text
        self.display_x = left
        self.display_y = top

        if font_info is None:
            self.font = pygame.font.Font('font/msjh.ttc', 20)
            self.tc = (255, 255, 255)
            self.bc = (0, 0, 0)
            self.align = 0
            self.valign = 0
        else:
            self.font = font_info["font"]    # 字型及字體大小
            self.tc = font_info["tc"]
            self.bc = font_info["bc"]
            self.align = font_info.get("align", 0)
            self.valign = font_info.get("valign", 0)

        if text == "":
            self.text_surface = None
        else:
            self.set_text(text)

    def render(self, surface):
        if self.text != "" and self.status >= 0:
            surface.blit(self.text_surface, (self.display_x, self.display_y))

    def set_align(self, align):
        w, h = self.__get_size()
        if align == 2:
            self.display_x = self.left - w
        elif align == 1:
            self.display_x = self.left - int(w / 2)
            
    def set_valign(self, valign):
        w, h = self.get_size()
        if valign == 2:
            self.display_y = self.pos_y - h
        elif valign == 1:
            self.display_y = self.pos_y - int(h / 2)
        else:
            self.display_y = self.pos_y

    def __get_size(self):
        if self.text_surface is None:
            return (0, 0)
        else:
            return self.text_surface.get_size()    # return (w, h)

    def set_text(self, text, tc=None, bc=None):
        self.text = text

        if tc is not None:
            self.tc = tc

        if bc is not None:
            self.bc = bc

        self.text_surface = self.font.render(self.text, True, self.tc, self.bc)
        self.set_align(self.align)
        self.set_valign(self.valign)

    def set_hide(self, flag):
        if flag:
            self.status = -1
        else:
            self.status = 0

後語

整個內容設計完全基於當前個人的使用範圍,雖可再擴充、再通用化,只是當前無此需求,就簡單的到此爲止。

自己造輪子有好有壞。好處是隻要考慮到自己當前的需求就好,相對簡單。使用別人的輪子,那還得先弄懂其使用方式,避免掉進坑洞裏。這兩者所花費的時間一比較,就知道該如何取捨?

至於自己造輪子的壞處,網絡上有太多不該重複造輪子的勸說,就不用多說了。

參考:

用Python和Pygame寫遊戲-從入門到精通
作者:目光博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章