python游戏开发实战:滚动列表(ScrollList)

一、效果

二、简介

最近在用pygame做《魔塔Online》,需要一个滚动列表。可是pygame关于GUI方面的东西毛都没有,又要自己造轮子了。目前写的这个ScrollList功能比较简单,item只能显示文字,就先凑合着用啦。

三、使用方法

引入ScrollList之后(ScrollList源码在文章最后),用以下代码就能在窗口中创建一个滚动列表啦。

def func(data):
    print(data)


map_list = ScrollList(20, 50, 226, 404, Global().surface_pool[15], Global().surface_pool[16], padding=(10, 2),
                      callback=func)
map_list.add_item(Global().min_font, "作者", data="你好啊,这是附加数据。")
map_list.add_item(Global().min_font, "狡猾的皮球", data=[6, 6, 6])
map_list.add_item(Global().min_font, "QQ871245007", data={"attr": "name"})
map_list.add_item(Global().min_font, "发布时间")
map_list.add_item(Global().min_font, "2018年9月20日")
map_list.add_item(Global().min_font, "6666666")

说明一下ScrollList的构造参数:

:param x: 在窗口中的位置
:param y: 在窗口中的位置
:param w: 宽度
:param h: 高度
:param surface_bg: 列表背景图
:param surface_item: 列表每行的背景图
:param padding: (上下内边距,左右内边距)
:param spacing: 行距,第一个item的底部到下一个item的头部的距离
:param callback: item被点中后的回调函数

其中,callback必须能接受一个data参数,这个data就是下面add_item的时候设置的data,主要用于判断点中的item具体是哪一个。

ScrollList.add_item()的参数:

:param font: 字体
:param text: 显示的文字
:param color: 文字颜色
:param data: 附带数据

其中,font是pygame的Font对象。color是一个代表rgb的三元组。

 

 

做了这些还不够,还得处理一下滚动列表的鼠标按下、移动、弹起事件,如下:

def event_handler(event):
    x, y = pygame.mouse.get_pos()
    if event.type == pygame.MOUSEMOTION:  # 鼠标移动
        map_list.mouse_move(x, y)
    elif event.type == pygame.MOUSEBUTTONDOWN:  # 鼠标按下
        map_list.mouse_down(x, y)
    elif event.type == pygame.MOUSEBUTTONUP:  # 鼠标弹起
        map_list.mouse_up(x, y)

这个event_handler在哪调用,应该不用多说了吧。在你处理事件队列的代码中调用。类似这个:

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        sys.exit()
    #调用事件处理
    event_handler(event)

最后,调用ScrollList的draw函数就可以把滚动列表显示出来啦。

map_list.draw(Global().screen)

draw的参数就是目标surface。

 

四、ScrollList源码

class ScrollList:
    """
    滚动列表
    """

    def __init__(self, x, y, w, h, surface_bg, surface_item, padding=(10, 5), spacing=10, callback=None):
        """
        构造滚动列表对象
        :param x: 在窗口中的位置
        :param y: 在窗口中的位置
        :param w: 宽度
        :param h: 高度
        :param surface_bg: 列表背景图
        :param surface_item: 列表每行的背景图
        :param padding: (上下内边距,左右内边距)
        :param spacing: 行距,第一个item的底部到下一个item的头部的距离
        :param callback: item被点中后的回调函数
        """

        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.surface_bg = surface_bg
        self.surface_item = surface_item
        self.padding = padding
        self.spacing = spacing
        self.item_list = []  # item列表,格式:{"text":"6666","font":font,"data":data},其中data是附带数据,可有可无
        self.callback = callback  # 鼠标触发的单击事件
        self.item_h = self.surface_item.get_height()
        # 缓冲区
        self.surface_buffer = None
        self.offset_y = 0  # 缓冲区偏移量
        # 当前偏移量
        self.current_offset_y = 0
        # 鼠标是否按下
        self.is_mouse_down = False
        # 鼠标按下时的坐标(相对坐标,以滚动列表左上角为原点)
        self.m_x = 0
        self.m_y = 0

    def set_buffer(self):
        """
        创建缓冲区
        """
        # 宽度
        buffer_width = self.surface_bg.get_width() - self.padding[1] * 2
        # 高度
        buffer_height = len(self.item_list) * (self.surface_item.get_height() + self.spacing)
        # 创建缓冲区
        self.surface_buffer = pygame.Surface((buffer_width, buffer_height), flags=pygame.SRCALPHA)
        pygame.Surface.convert(self.surface_buffer)
        self.surface_buffer.fill(pygame.Color(255, 255, 255, 0))

        # 画列表
        for i in range(len(self.item_list)):
            self.surface_buffer.blit(self.surface_item, (0, i * (self.item_h + self.spacing)))
            TextView().draw_text(self.surface_buffer, 15, 22 + i * (self.item_h + self.spacing),
                                 self.item_list[i]['text'],
                                 self.item_list[i]['font'], self.item_list[i]['color'])

    def mouse_move(self, x, y):
        if not self.is_mouse_down:
            return
        # 计算偏移量
        _, d_y = self.get_dxy(x, y)
        self.current_offset_y = d_y - self.m_y
        # 限制拖动范围
        if self.current_offset_y + self.offset_y >= 0:
            self.current_offset_y = 0
            self.offset_y = 0
            # self.is_mouse_down = False

        if self.current_offset_y + self.offset_y <= self.surface_bg.get_width() - self.surface_buffer.get_height():
            self.current_offset_y = 0
            self.offset_y = self.surface_bg.get_width() - self.surface_buffer.get_height()

    def mouse_down(self, x, y):
        if not self.mouse_in_panel(x, y):
            return
        self.is_mouse_down = True
        # 获取相对坐标
        self.m_x, self.m_y = self.get_dxy(x, y)

    def mouse_up(self, x, y):
        d_x, d_y = self.get_dxy(x, y)
        if d_x == self.m_x and d_y == self.m_y:
            # 单纯的点击事件,没有拖动
            try:
                if self.callback is not None:
                    index = (-self.offset_y + self.m_y) // (self.item_h + self.spacing)
                    self.callback(self.item_list[index]['data'])
            except:  # 下标越界
                pass
        else:
            # 拖动事件
            self.offset_y += self.current_offset_y
            self.current_offset_y = 0
        self.is_mouse_down = False

    def mouse_in_panel(self, x, y):
        # 计算相对坐标
        dx, dy = self.get_dxy(x, y)

        return 0 < dx < self.w and 0 < dy < self.h

    def get_dxy(self, x, y):
        # 计算相对坐标
        dx = x - self.x
        dy = y - self.y
        return dx, dy

    def add_item(self, font, text="", color=(233, 115, 115), data=None):
        """
        添加行
        :param font: 字体
        :param text: 显示的文字
        :param color: 文字颜色
        :param data: 附带数据
        """
        item = {
            "text": text,
            "font": font,
            "color": color,
            "data": data
        }
        self.item_list.append(item)
        self.set_buffer()

    def clear_item(self):
        """
        清空列表
        """
        self.item_list = []

    def draw(self, dest_suf):
        # 画背景图
        dest_suf.blit(self.surface_bg, (self.x, self.y))
        # 画item
        dest_suf.blit(self.surface_buffer, (self.x + self.padding[1], self.y + self.padding[0]),
                      (0, -(self.current_offset_y + self.offset_y), self.surface_buffer.get_width(),
                       self.surface_bg.get_height() - self.padding[0] * 2))

五、Demo源码

暂无,以后添加

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值