pygame封裝常用控制元件,第二日,有滑塊的文字顯示域

河北大学-徐小波發表於2024-08-29
#coding=utf-8

import os,sys,re,time
import pygame
import random
from win32api import GetSystemMetrics
from tkinter import messagebox

pygame.init()
pygame.display.set_caption("我的控制元件")

percent = 0.6
screen_width = GetSystemMetrics(0)
screen_height = GetSystemMetrics(1)
window_width = int(screen_width*percent)
window_height = int(screen_height*percent)

dt = 0
clock = pygame.time.Clock()

screen = pygame.display.set_mode((window_width, window_height))

#停止處理輸入法事件
pygame.key.stop_text_input()

def showMsg(msg):
    messagebox.showinfo('提示資訊', msg)

class Button:
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.color = 'gray'
        self.text = '按鈕'
        self.text_color = 'black'
        self.text_size = 12
        self.border_width = 1
        self.border_color = 'black'
        self.font_path = os.path.join(os.path.dirname(sys.argv[0]), 'simsun.ttc')
        self.my_font = pygame.font.Font(self.font_path, self.text_size)
        
    def setColor(self, color):
        self.color = color
        
    def setText(self, text):
        self.text = text
        
    def getText(self):
        return self.text
        
    def setTextColor(self, text_color):
        self.text_color = text_color
        
    def setTextSize(self, text_size):
        self.text_size = text_size
        self.my_font = pygame.font.Font(self.font_path, self.text_size)
        
    def setBorderWidth(self, border_width):
        self.border_width = border_width
        
    def setBorderColor(self, border_color):
        self.border_color = border_color
        
    def draw(self, win):
        
        pygame.draw.rect(win, self.color, (self.x, self.y, self.w, self.h))
        if self.border_width > 0:
            pygame.draw.rect(win, self.border_color, (self.x, self.y, self.w, self.h), self.border_width)
            
        text = self.my_font.render(self.text, True, self.text_color)
        myx = self.x + (self.w - text.get_width()) / 2
        myy = self.y + (self.h - text.get_height()) / 2
        win.blit(text, (myx, myy))
        
    def click(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN:
            if self.x + self.w > event.pos[0] > self.x and self.y + self.h > event.pos[1] > self.y:
                return True
        return False
    
class Label:
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.color = 'white'
        self.text = ''
        self.text_color = 'black'
        self.text_size = 12
        self.border_width = 0
        self.border_color = ''
        self.font_path = os.path.join(os.path.dirname(sys.argv[0]), 'simsun.ttc')
        self.my_font = pygame.font.Font(self.font_path, self.text_size)
        
    def setColor(self, color):
        self.color = color
        
    def setText(self, text):
        self.text = text
        
    def getText(self):
        return self.text
        
    def setTextColor(self, text_color):
        self.text_color = text_color
        
    def setTextSize(self, text_size):
        self.text_size = text_size
        self.my_font = pygame.font.Font(self.font_path, self.text_size)
        
    def setBorderWidth(self, border_width):
        self.border_width = border_width
        
    def setBorderColor(self, border_color):
        self.border_color = border_color
        
    def getCharWH(self):
        padding_percent_width = 0.3
        padding_percent_height = 0.3
        test_text1 = '測試字串'
        test_text2 = self.my_font.render(test_text1, True, self.text_color)
        char_width = test_text2.get_width() / len(test_text1)
        char_height = test_text2.get_height()
        padding_width = char_width * padding_percent_width
        padding_height = char_height * padding_percent_height
        max_field_num = int((self.w - padding_width * 2) / char_width)
        
        return (char_height, padding_width, padding_height, max_field_num)
    
    def getTrueLines(self, max_field_num):
        texts = self.text.split("\n")
        k = 0
        for i,mytext in enumerate(texts):
            while len(mytext) > max_field_num:
                submytext = mytext[0:max_field_num]
                mytext = mytext[max_field_num:]
                k += 1
            k += 1
        return k+1
        
    def draw(self, win):
        (char_height, padding_width, padding_height, max_field_num) = self.getCharWH()
        lineNum = self.getTrueLines(max_field_num)
        if lineNum * char_height > self.h:
            self.h = lineNum * char_height
            
        pygame.draw.rect(win, self.color, (self.x, self.y, self.w, self.h))
        if self.border_width > 0:
            pygame.draw.rect(win, self.border_color, (self.x, self.y, self.w, self.h), self.border_width)
        
        texts = self.text.split("\n")
        k = 0
        for i,mytext in enumerate(texts):
            while len(mytext) > max_field_num:
                submytext = mytext[0:max_field_num]
                
                subtext = self.my_font.render(submytext, True, self.text_color)
                submyx = self.x + padding_width
                submyy = self.y + padding_height + char_height * k
                win.blit(subtext, (submyx, submyy))
                
                mytext = mytext[max_field_num:]
                k += 1
            
            text = self.my_font.render(mytext, True, self.text_color)
            myx = self.x + padding_width
            myy = self.y + padding_height + char_height * k
            win.blit(text, (myx, myy))
            k += 1
    
class TextArea:
    startx = 0
    endx = 0
    
    starty = 0
    endy = 0
    
    padding_width = 0
    padding_height = 0
    char_width = 0
    char_height = 0
    
    max_field_num = 0
    
    collbar_color = 'gray'
    collbar_bgcolor = 'lightgray'
    
    collbar_width_x = 0
    collbar_height_x = 5
    collbar_offset_x = 0
    collbar_percent_x = 0
    dragging_x = False
    
    collbar_width_y = 5
    collbar_height_y = 0
    collbar_offset_y = 0
    collbar_percent_y = 0
    dragging_y = False
    
    line_wrap = False
    
    def __init__(self, x, y, w, h, text, text_size = 12):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.color = 'white'
        self.text = text
        self.text_color = 'black'
        self.text_size = text_size
        self.border_width = 0
        self.border_color = ''
        self.font_path = os.path.join(os.path.dirname(sys.argv[0]), 'simsun.ttc')
        self.my_font = pygame.font.Font(self.font_path, self.text_size)
        
        self.getCharWH()
        
    def setColor(self, color):
        self.color = color
        
    def setText(self, text):
        self.text = text
        self.getCharWH()
        
    def getText(self):
        return self.text
        
    def setTextColor(self, text_color):
        self.text_color = text_color
        
    def setTextSize(self, text_size):
        self.text_size = text_size
        self.my_font = pygame.font.Font(self.font_path, self.text_size)
        self.getCharWH()
        
    def setBorderWidth(self, border_width):
        self.border_width = border_width
        
    def setBorderColor(self, border_color):
        self.border_color = border_color
        
    def getCharWH(self):
        padding_percent_width = 0.3
        padding_percent_height = 0.3
        test_text1 = '測試字串'
        test_text2 = self.my_font.render(test_text1, True, self.text_color)
        
        self.char_width = test_text2.get_width() / len(test_text1)
        self.char_height = test_text2.get_height()
        self.padding_width = self.char_width * padding_percent_width
        self.padding_height = self.char_height * padding_percent_height
        
        self.max_field_num = self.getMaxFieldNum()
        showLineNum = self.getShowLineNum()
        totalLineNum = self.getTotalLineNum()
        print(showLineNum)
        print(totalLineNum)
        print()
        self.collbar_percent_y = showLineNum / totalLineNum

        if totalLineNum * self.char_height + 2 * self.padding_height+self.collbar_height_x > self.h:
            self.collbar_height_y = self.collbar_percent_y * (self.h - self.padding_height * 2-self.collbar_height_x)
        
        showFieldNum = self.getShowFieldNum()
        totalFieldNum = self.getTotalFieldNum()
        self.collbar_percent_x = showFieldNum / totalFieldNum
        if totalFieldNum * self.char_width + 2 * self.padding_width+self.collbar_width_y > self.w:
            self.collbar_width_x = self.collbar_percent_x * (self.w - self.padding_width * 2-self.collbar_width_y)
    
    #計算每行多少個字
    def getMaxFieldNum(self):
        num = 0
        if self.line_wrap == True:
            #折行時按控制元件寬度計算
            num = int((self.w - self.padding_width * 2 - self.collbar_width_y) / self.char_width)
        else:
            #不折行時按最長的行所含字元算
            texts = self.text.split("\n")
            for text in texts:
                if len(text) > num:
                    num = len(text)
        return num
    
    #計算可以一次性展示的行數
    def getShowLineNum(self):
        lostHeight = self.h - 2 * self.padding_height;
        num = int(lostHeight // self.char_height)
        return num
    
    #計算所有行數
    def getTotalLineNum(self):
        texts = self.text.split("\n")
        if self.line_wrap == True:
            k = 0
            for i,mytext in enumerate(texts):
                while len(mytext) > self.max_field_num:
                    submytext = mytext[0:self.max_field_num]
                    mytext = mytext[self.max_field_num:]
                    k += 1
                k += 1
            return k
        else:
            return len(texts)
    
    #計算可以一次性展示的列數
    def getShowFieldNum(self):
        lostWidth = self.w - 2 * self.padding_width - self.collbar_width_y;
        num = int(lostWidth // self.char_width)
        return num
    
    #計算最長的行的列數
    def getTotalFieldNum(self):
        num = 0
        if self.line_wrap == True:
            #折行時按控制元件寬度計算
            num = int((self.w - self.padding_width * 2 - self.collbar_width_y) / self.char_width)
        else:
            #不折行時按最長的行所含字元算
            texts = self.text.split("\n")
            for text in texts:
                if len(text) > num:
                    num = len(text)
        return num
    
    def getTrueLineTexts(self):
        res = []
        texts = self.text.split("\n")
        k = 0
        for i,mytext in enumerate(texts):
            while len(mytext) > self.max_field_num:
                submytext = mytext[0:self.max_field_num]
                res.append(submytext)
                mytext = mytext[self.max_field_num:]
                k += 1
            k += 1
            res.append(mytext)
        return res
    
    def draggingyMe(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN:
            if self.x + self.w > event.pos[0] > self.x and self.y + self.h > event.pos[1] > self.y:
                self.dragging_x = True
                self.startx = event.pos[0]
                self.dragging_y = True
                self.starty = event.pos[1]
        elif event.type == pygame.MOUSEBUTTONUP:
            if self.x + self.w > event.pos[0] > self.x and self.y + self.h > event.pos[1] > self.y:
                self.dragging_x = False
                self.endx = event.pos[0]
                self.dragging_y = False
                self.endy = event.pos[1]
        elif event.type == pygame.MOUSEMOTION:
            if self.x + self.w > event.pos[0] > self.x and self.y + self.h > event.pos[1] > self.y:
                self.endx = event.pos[0]
                self.endy = event.pos[1]
                
        if self.dragging_x and self.collbar_width_x > 0:
            maxoffset = self.w - self.padding_width * 2 - self.collbar_width_y - self.collbar_width_x
            offset = self.endx - self.startx
            if offset <= 0:
                self.collbar_offset_x = 0
            elif offset >= maxoffset:
                self.collbar_offset_x = maxoffset
            else:
                self.collbar_offset_x = offset
        
        if self.dragging_y and self.collbar_height_y > 0:
            maxoffset = self.h - self.padding_height * 2 - self.collbar_height_x - self.collbar_height_y
            offset = self.endy - self.starty
            if offset <= 0:
                self.collbar_offset_y = 0
            elif offset >= maxoffset:
                self.collbar_offset_y = maxoffset
            else:
                self.collbar_offset_y = offset
        
    def draw(self, win):
        pygame.draw.rect(win, self.color, (self.x, self.y, self.w, self.h))
        if self.border_width > 0:
            pygame.draw.rect(win, self.border_color, (self.x, self.y, self.w, self.h), self.border_width)
        
        #右側滑動條
        if self.collbar_height_y > 0:
            pygame.draw.rect(win, self.collbar_bgcolor, (self.x+self.w-self.collbar_width_y-self.padding_width, self.y+self.padding_height, self.collbar_width_y, self.h-2*self.padding_height))
            pygame.draw.rect(win, self.collbar_color, (self.x+self.w-self.collbar_width_y-self.padding_width, self.y+self.padding_height+self.collbar_offset_y, self.collbar_width_y, self.collbar_height_y))
        
        #左側滑動條
        if self.collbar_width_x > 0:
            pygame.draw.rect(win, self.collbar_bgcolor, (self.x+self.padding_width, self.y+self.h-self.padding_height-self.collbar_height_x, self.w-2*self.padding_width-self.collbar_width_y, self.collbar_height_x))
            pygame.draw.rect(win, self.collbar_color, (self.x+self.padding_width+self.collbar_offset_x, self.y+self.h-self.padding_height-self.collbar_height_x, self.collbar_width_x, self.collbar_height_x))
        
        test_texts = self.getTrueLineTexts()
                
        #顯示哪些行
        cutPreLineNum = int(self.collbar_offset_y / self.collbar_percent_y // self.char_height)
        test_texts = test_texts[cutPreLineNum:]
        
        #顯示哪些列
        showFieldNum = self.getShowFieldNum()
        cutPreFieldNum = int(self.collbar_offset_x / self.collbar_percent_x // self.char_width)
        for i,test_text in enumerate(test_texts):
            test_texts[i] = test_text[cutPreFieldNum:cutPreFieldNum+showFieldNum]
        
        texts = '\n'.join(test_texts).split("\n")
        k = 0
        for i,mytext in enumerate(texts):
            while len(mytext) > self.max_field_num:
                submytext = mytext[0:self.max_field_num]
                
                subtext = self.my_font.render(submytext, True, self.text_color)
                submyx = self.x + self.padding_width
                submyy = self.y + self.padding_height + self.char_height * k
                if submyy+self.char_height < self.y+self.h-self.collbar_height_x:
                    win.blit(subtext, (submyx, submyy))
                
                mytext = mytext[self.max_field_num:]
                k += 1
            
            text = self.my_font.render(mytext, True, self.text_color)
            myx = self.x + self.padding_width
            myy = self.y + self.padding_height + self.char_height * k
            if myy+self.char_height < self.y+self.h-self.collbar_height_x:
                win.blit(text, (myx, myy))
            k += 1
    
    
bt = Button(5, 5, 80, 25)
bt.setText('測試按鈕')
bt.setColor('Brown')
bt.setTextColor('Gold')
bt.setBorderColor('Lime')
bt.setBorderWidth(1)

label_text = '''
壹哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈壹
貳哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈貳
叄哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈叄
肆哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈肆
伍哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈伍
陸哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈陸
柒哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈柒
'''.strip()
label = Label(115, 5, 400, 100)
label.setColor('Maroon')
label.setText(label_text)
label.setTextSize(18)
label.setBorderColor('Lime')
label.setBorderWidth(1)

my_text1 = '''
壹哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈壹
貳哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈貳
叄哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈叄
肆哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈肆
伍哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈伍
陸哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈陸
柒哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈柒
'''.strip()
textArea1 = TextArea(535, 5, 400, 145, my_text1, 18)
textArea1.setColor('white')
textArea1.setBorderColor('Lime')
textArea1.setBorderWidth(1)

my_text2 = '''
壹哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈壹
貳哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈貳
叄哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈叄
肆哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈肆
伍哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈伍
陸哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈陸
柒哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈柒
'''.strip()
textArea2 = TextArea(535, 175, 300, 105, my_text2, 14)
textArea2.setColor('white')
textArea2.setBorderColor('Lime')
textArea2.setBorderWidth(1)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            
        textArea1.draggingyMe(event)
        textArea2.draggingyMe(event)
        
        if bt.click(event):
            showMsg("%s被點選了" % bt.getText())
            
    keys_pressed = pygame.key.get_pressed()
    
    #ESC鍵
    if keys_pressed[pygame.K_ESCAPE]:
        running = False
        
    screen.fill("purple")
    
    bt.draw(screen)
    label.draw(screen)
    
    textArea1.draw(screen)
    textArea2.draw(screen)
        
    #更新顯示
    pygame.display.flip()
    #pygame.display.update()
    
    dt = clock.tick(60) / 600
    
pygame.quit()

效果:

相關文章