当前位置 博文首页 > Python制作一个随机抽奖小工具的实现

    Python制作一个随机抽奖小工具的实现

    作者:可以叫我才哥 时间:2021-08-02 18:13

    目录
    • 1. 核心功能设计
    • 2. GUI设计与实现
    • 3. 功能实现
      • 3.1 读取人员名单
      • 3.2. 随机抽奖
      • 3.3. 保存中奖名单
      • 3.4. GUI交互逻辑

    最近在工作中面向社群玩家组织了一场活动,需要进行随机抽奖,参考之前小明大佬的案例,再结合自己的需求,做了一个简单的随机抽奖小工具。

    今天我就来顺便介绍一下这个小工具的制作过程吧!

    先看效果:

    1. 核心功能设计

    针对随机抽奖的小工具,需要可以导入参与抽奖的人员名单,然后选择不同的奖励类型进行随机抽取获奖名单并导出。

    那么,简单进行需求拆解,大致梳理出以下核心功能:

    名单导入

    为了避免出现重名情况,这里我们约定以下几点:

    ①导入参与抽奖的人员名单文件(xlsx类型文件)

    ②数据第一列为ID,第二列为name

    参考格式案例

    案例

    奖项类型选择

    奖项类型是指一等奖、二等奖这类标识语,这里我们内置了特等奖-六等奖共7个选项供选取

    本轮人数

    本轮人数是指每次抽奖时一次性抽取的获奖人数,默认值为5

    ①当填入的数字超过剩余未获奖人数时,会进行提示并显示未获奖人数

    ②当填入的数字为0表示轮空,也需要手动结束

    ③当填入的数字为负数时,点击抽奖无响应

    ④当填入的非数字时,会进行提示需要输入正确数字

    抽奖时轮播区域

    用于显示抽奖中随机滚动参与本轮抽奖的人员名单

    人员名单

    当选择正确的人员名单文件后,这里会自动显示人员信息列表

    中奖记录

    记录每次抽取的奖项类型及获奖名单

    开始抽奖

    ①开始抽奖时,会先判断抽奖设置是否满足条件,否则会有相关提示

    ②抽奖中点击开始抽奖会提示正在抽奖中

    结束

    ①非抽奖状态下点击结束无响应

    ②抽奖中点击结束将显示本次抽奖结果

    重置

    ①重置会清掉历史抽奖记录(含本地文件,如有必要建议对中奖名单留档)

    ②抽奖中点击重置会提示正在抽奖中

    ③非抽奖状态点击重置会提示该操作会删除历史记录,是否确认

    基本功能点确认后,我们就开始进行GUI设计。

    2. GUI设计与实现

    基于功能点,我们用axure简单进行UI布局设计,然后再通过GUI开发库进行设计,这里依旧采用的是pysimplegui,主要是简单方便。

    UI布局设计-axure

    基于GUI设计,我们编码如下:

    nameList_column = [
        [sg.Text('人员名单:')],
        [sg.Listbox(values=[], size=(20, 10), key='nameList')],
    ]
    result_column = [
        [sg.Text('中奖记录:')],
        [sg.Multiline('', size=(48, 10), key='result', text_color='DeepPink')],
    ]
    
    # 主题设置
    sg.theme('SystemDefaultForReal')
    
    # 布局设置
    layout = [[sg.Text('选择参与抽奖人员名单文件:', font=('微软雅黑', 12)), sg.InputText('', key='_file', size=(50, 1), font=('微软雅黑', 10), enable_events=True), sg.FileBrowse('打开', file_types=(('Text Files', '*.xlsx'),), size=(10, 1), font=('微软雅黑', 11))],
              [sg.Frame(layout=[
                  [sg.Text('本轮奖项:', font=('微软雅黑', 12)), sg.Combo(['特等奖', '一等奖', '二等奖', '三等奖', '四等奖', '五等奖', '六等奖'], font=('微软雅黑', 10), default_value='特等奖', size=(15, 5), key='_type'),
                   sg.Text('本轮人数:', font=('微软雅黑', 12)), sg.InputText('5', key='_num', size=(38, 1), font=('微软雅黑', 10))],
              ],
                  title='抽奖设置', title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='请进行抽奖设置后再开始抽奖')],
              [sg.Multiline(size=(48, 5), font=(
                  '微软雅黑', 18), text_color='Blue', key='luckyName', justification='center')],
              [sg.Column(nameList_column), sg.Column(result_column)],
              [sg.Text('操作说明:', font=('微软雅黑', 12))],
              [sg.Text('①先选择参与抽奖的人员名单xlsx文件,人员名单文件包含ID和name两个字段\n②获奖名单将存在小工具所在文件夹,重置会删除历史记录文件', font=('微软雅黑', 10)),
               sg.Text('', font=('微软雅黑', 12), size=(5, 1)),
               sg.Button('开始抽奖', font=('微软雅黑', 12), button_color='Orange'),
               sg.Button('结束', font=('微软雅黑', 12), button_color='red'),
               sg.Button('重置', font=('微软雅黑', 12), button_color='red'), ],
              ]
    
    # 创建窗口
    window = sg.Window('抽奖小工具,作者@微信公众号:可以叫我才哥', layout,
                       font=('微软雅黑', 12), default_element_size=(50, 1))
    

    其包含的控件如下:

    • Text 文本
    • InputText 输入文本框
    • FileBrowse 文件浏览
    • Multiline 多行文本框
    • Combo 下拉框
    • Listbox 列表
    • Button 按钮

    需要注意的是这里有个Frame组件,用于layout嵌套,可以很好地模块化UI布局。

    3. 功能实现

    在本案例中,需要实现三个功能,分别是:读取人员名单、随机抽奖以及保存中奖名单。

    3.1 读取人员名单

    这里采用的是openpyxl读取表格数据并获得某几列的值,由于存在表头,所以最后不需要表头

    def nameList(window):
        fileName = values['_file']
        try:
            wb = openpyxl.load_workbook(fileName)
            active_sheet = wb.active
            names = [cell_object.value for cell_object in list(active_sheet.columns)[1]][1:]
            ids = [cell_object.value for cell_object in list(active_sheet.columns)[0]][1:]
            names = [name+'_'+str(id_) for name, id_ in zip(names, ids)]
            window['nameList'].update(names)
            return names
        except:
            sg.popup('请选择正确格式的的人员名单文件', title='提示',)
    

    3.2. 随机抽奖

    由于我们需要一次随机抽取的人数存在多个,所以这里用的是random.sample(),需要注意的是传入的参数中names是需要去掉已中奖名单

    def Result(window, names):
        global is_run, luckyNames
        _type = values['_type']                # 本轮奖项类型
        _num = int(values['_num'])             # 本轮人数
    
        while True:
            randomName = random.sample(names, k=_num)
            luckyName = '   '.join(randomName)
            window['luckyName'].update(luckyName)
    
            if not is_run:
                headers = ['奖项', '名单']
                toCsv(headers, [_type]*len(randomName), randomName, lucky)
                luckyNames = luckyNames + _type+' : '+luckyName+'\n\n'
                window['result'].update(luckyNames)
                return
            time.sleep(0.088)
    

    3.3. 保存中奖名单

    这里我们用的是csv库的方法,追加存储

    def toCsv(headers, col1, col2, file):
        # 存在则追加,不存在则新建
        if os.path.exists(lucky):
            with open(lucky, 'a', encoding='utf_8_sig', newline='') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerows(zip(col1, col2))
        else:
            with open(lucky, 'w', encoding='utf_8_sig', newline='') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerow(headers)
                writer.writerows(zip(col1, col2))
    

    完成核心功能函数后,我们再进行GUI交互逻辑的实现。

    3.4. GUI交互逻辑

    这里有两个全局变量,其中一个用于记录当前抽奖状态,另外一个用于存储当前已经获奖的人员信息。关于交互逻辑的详情,大家可以结合核心功能需求及以下代码了解。

    # 初始状态
    is_run = False
    luckyNames = ''
    
    # 事件循环
    while True:
        event, values = window.read()
        if event in (None, '关闭程序'):
            break
        if event == '_file':
            nameList(window)
    
        if event == '开始抽奖':
            if is_run:
                sg.popup('抽奖进行中,无需重复操作......', title='提示')
                continue
            try:
                names = nameList(window)               # 人员名单
                _num = int(values['_num'])             # 本轮人数
                lucky = '中奖名单.csv'                 # 中奖名单
                if os.path.exists(lucky):
                    with open('中奖名单.csv', 'r', encoding='utf_8_sig') as f:
                        reader = csv.reader(f)
                        selectedNames = set([i[1] for i in reader][1:])
                    names_set = set(names)-selectedNames
                else:
                    names_set = set(names)
                if len(names_set) >= _num:
                    is_run = True
                    _thread.start_new_thread(Result, (window, names_set))
                else:
                    sg.popup(
                        f'请选择正确本轮抽奖人数(当前 {len(names_set)} 个未中奖人数)', title='提示')
            except:
                sg.popup('请选择正确本轮抽奖人数(别超过总人数哦)', title='提示')
        elif event == '结束':
            is_run = False
        elif event == '重置':
            if is_run:
                sg.popup('抽奖进行中,请等待抽奖结束后重置...', title='提示')
                continue
            yes_no = sg.popup_yes_no(
                '重置会清楚历史数据,是否执行此操作??', text_color='red', title='提示')
            if yes_no == 'Yes':
                try:
                    os.remove(lucky)
                    luckyNames = ''
                    window['result'].update(luckyNames)
                    window['luckyName'].update(luckyNames)
                    sg.popup('抽奖历史记录已被重置......', title='提示')
                except:
                    sg.popup('无抽奖历史记录......', title='提示')
    window.close()
    

    基于此,我们就完成了随机抽奖小工具的制作。

    启动页如下:

    最后,大家感兴趣就可以将代码打包成exe可执行文件了,我这边打包下来大概10MB左右大小。

    jsjbwy
    下一篇:没有了