一、效果

python滚动条和框架 pygame滚动条_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源码

暂无,以后添加