pygame做界面之表格篇(一)

继续之前的控件按钮(bf_button)、文本框(bf_label)和编辑框(bf_edit)之后,继续增加新控件(bf_table),由于表格的功能比较复杂,可能要分几次编写
大致实现步骤
一:基本展示
二:支持横、列两个方向上的滚动条
三:支持列宽度的自动调整和手动调整

目前一阶段的效果图在这里插入图片描述

用pygame做的表格控件
主要有以下几个文件
syht.otf 字体文件
bf_common.py 控件父类
bf_panel.py 面板类用于控件管理
bf_table.py 表格控件
example_table.py 测试表格功能

以下是各python文件
bf_common.py

# -*- coding=utf-8 -*-
import threading
import pygame
from pygame.locals import MOUSEBUTTONDOWN

class BFControlId(object):
    _instance_lock = threading.Lock()
    def __init__(self):
        self.id = 1
        self.click_id = -1

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(BFControlId, "_instance"):
            BFControlId._instance = BFControlId(*args, **kwargs)
        return BFControlId._instance

    def get_new_id(self):
        self.id += 1
        return self.id

class BFBase(object):
    def __init__(self):
        self.panel = None
        self._visible = True
        self._text_align = TEXT_ALIGN_MIDDLE
        self._font = DEFAULT_SM_FONT
        self.ctl_id = BFControlId().instance().get_new_id()

    def init_font(self):
        pass

    def clear_foucs(self):
        pass

    def clear_hover(self):
        pass

    @property
    def visible(self):
        return self._visible

    @visible.setter
    def visible(self, value):
        self._visible = value

    @property
    def text_align(self):
        return self._text_align

    @text_align.setter
    def text_align(self, value):
        self._text_align = value
        self.init_font()

    @property
    def font(self):
        return self._font

    @font.setter
    def font(self, value):
        self._font = value
        self.init_font()

CLICK_EFFECT_TIME = 100
DEFAULT_FONT = pygame.font.Font(u'syht.otf', 28)
DEFAULT_SM_FONT = pygame.font.Font(u'syht.otf', 20)
def get_default_font(size):
    return pygame.font.Font(u'syht.otf', int(size))

TEXT_ALIGN_LEFT = 1
TEXT_ALIGN_MIDDLE = 2
TEXT_ALIGN_RIGHT = 3

bf_panel.py

# -*- coding=utf-8 -*-
import threading
import pygame

class BFPanel(object):
    def __init__(self):
        self.ctl_list = []

    def add_control(self, ctl):
        ctl.panel = self
        self.ctl_list.append(ctl)

    def clear_foucs(self):
        for ctl in self.ctl_list: ctl.clear_foucs()

    def clear_hover(self):
        for ctl in self.ctl_list: ctl.clear_hover()

    def update(self, event):
        for i in range(len(self.ctl_list)-1, -1, -1):
            ctl = self.ctl_list[i]
            if not ctl.visible: continue
            flag = ctl.update(event)
            if flag:
                break
        # for ctl in self.ctl_list: ctl.update(event)

    def draw(self):
        for ctl in self.ctl_list: ctl.draw()

bf_table.py

# -*- coding=utf-8 -*-
import time,sys,threading,platform
import pygame
from pygame.locals import MOUSEBUTTONDOWN,KEYDOWN,SCRAP_TEXT
from bf_common import BFControlId,BFBase,DEFAULT_FONT,get_default_font

black = (50,50,50)
class ColInfo(object):
    def __init__(self, surface, name, font_image=None, x=0, y=0):
        self.surface = surface
        self.x = x
        self.y = y
        self.name = name
        self.font_image = font_image

    def draw(self):
        if self.surface: self.surface.blit(self.font_image, (self.x, self.y))

class RowItem(object):
    def __init__(self, surface, val, font_image=None, x=0, y=0):
        self.surface = surface
        self.x = x
        self.y = y
        self.val = val
        self.font_image = font_image

    def draw(self):
        if self.surface: self.surface.blit(self.font_image, (self.x, self.y))

class RowInfo(object):
    def __init__(self):
        self.row_items = []
        self.highlight = False
        self.surface = None

    def draw(self):
        if self.highlight and self.surface:
            self.surface.fill((218,245,255))
        for row_item in self.row_items:
            row_item.draw()

CLICK_EFFECT_TIME = 100
class BFTable(BFBase):
    def __init__(self, parent, rect, columns, rows):
        super(BFTable, self).__init__()
        self.x,self.y,self.width,self.height = rect
        self.bg_color = (255,255,255)
        self.parent = parent
        self.surface = parent.subsurface(rect)
        self.in_click = False
        self.click_loss_time = 0
        self.click_event_id = -1
        self.ctl_id = BFControlId().instance().get_new_id()
        self.header_list = []
        self._header_height = 30
        self._col_width = self.width / len(columns)
        self._row_height = (self.height - self._header_height) / len(rows)
        self._min_row_height = 20
        self._max_row_height = 30
        if self._row_height > self._max_row_height: self._row_height = self._max_row_height
        if self._row_height < self._min_row_height: self._row_height = self._min_row_height
        
        self._col_font = get_default_font(self._header_height * 0.5)
        self._row_font = get_default_font(self._row_height * 0.6) 
        self._columns = []
        for i in range(len(columns)):
            col = columns[i]
            col_info = ColInfo(self.surface.subsurface((self._col_width*i,0,self._col_width,self._header_height)), col)
            header_image=self._col_font.render(col, True, black)
            w, h = header_image.get_size()
            col_info.x = (self._col_width - w) / 2
            col_info.y = (self._header_height - h) / 2
            col_info.font_image = header_image
            self._columns.append(col_info) 

        self._rows = []
        for i in range(len(rows)):
            y = self._header_height + self._row_height * i
            row = rows[i]
            row_info = RowInfo()
            row_info.surface = None if y + self._row_height > self.height else self.surface.subsurface((0,y,self.width,self._row_height))
            if i % 2 == 1: row_info.highlight = True
            for j in range(len(row)):
                v = row[j]
                tmp_rect = (self._col_width*j,y,self._col_width,self._row_height)
                tmp_surface = None if y + self._row_height > self.height else self.surface.subsurface(tmp_rect)
                item = RowItem(tmp_surface,v)
                if sys.version_info >= (3,0) or type(v) is not unicode: v = str(v)
                row_image = self._row_font.render(v,True,black)
                w, h = row_image.get_size()
                item.x = (self._col_width - w) / 2
                item.y = (self._row_height - h) / 2
                item.font_image = row_image
                row_info.row_items.append(item)
            self._rows.append(row_info)

    def clear_foucs(self):
        self.in_edit = False

    def update(self, event):
        return False

    def draw(self):
        if not self._visible:
            return

        grid_color = (100,100,100)
        self.surface.subsurface((0,0,self.width,self._header_height)).fill((200,200,200))
        pygame.draw.rect(self.surface, grid_color, (0,0,self.width,self._header_height+self._row_height*len(self._rows)),1)
        pygame.draw.line(self.surface,grid_color,[0,self._header_height],[self.width,self._header_height], 1)
        for col_info in self._columns:
            col_info.draw()
        for row_info in self._rows:
            row_info.draw()
        for i in range(1, len(self._rows)):
            y = self._header_height + i * self._row_height
            pygame.draw.line(self.surface,grid_color,[0,y],[self.width,y], 1)
        for i in range(1, len(self._columns)):
            x = self._col_width * i
            pygame.draw.line(self.surface,grid_color,[x,0],[x,self._header_height+self._row_height * len(self._rows)], 1)
        
        pygame.draw.rect(self.surface, (200,200,200), (0,0,self.width,self.height),1)

example_table.py

# -*- coding=utf-8 -*-
import sys
import pygame

pygame.init()
screen = pygame.display.set_mode((600,420))
pygame.scrap.init()

from bf_panel import BFPanel
from bf_table import BFTable

screencaption = pygame.display.set_caption('bf control')

btn_panel = BFPanel()

headers = (u'编号',u'姓名',u'年龄',u'语文成绩',u'数学成绩',u'英语成绩')
rows = []
rows.append((1,u'王小明',11,99,95,91))
rows.append((2,u'李小红',10,97,88,90))
rows.append((3,u'张小强',11,99,100,100))
rows.append((4,u'王小明',11,99,95,91))
rows.append((5,u'李小红',10,97,88,90))
rows.append((6,u'张小强',11,99,100,100))
rows.append((7,u'王小明',11,99,95,91))
rows.append((8,u'李小红',10,97,88,90))
rows.append((9,u'张小强',11,99,100,100))
rows.append((10,u'王小明',11,99,95,91))
rows.append((11,u'李小红',10,97,88,90))
rows.append((12,u'张小强',11,99,100,100))
table = BFTable(screen, (20,20,560,300), headers, rows)
btn_panel.add_control(table)

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
             pygame.quit()
             exit()
        btn_panel.update(event)

    screen.fill((255,255,255))
    btn_panel.draw()
   
    pygame.display.update() 
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰风漫天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值