Python设计不重复随机点名程序(第12节)


我们在日常工作和生活中,经常需要进行随机抽取一个结果的操作,例如点名、抽奖、抽查等。Python的random模块提供了丰富的随机抽取函数,可以轻松实现随机点名的功能。Python随机点名程序可以在很多场合使用,比如课堂、会议或团队活动中,随机点名是提升公平性最常用的方法之一。

在上一节教程中,已经简单介绍了如何通过random模块和Tkinter库的GUI图形界面实现随机选择一个或多个结果的程序。然而,在有些情况下,随机点名要做到不重复的效果,比如在课堂上老师们抽查学生,随机抽完所有名单后就会提示点名结束。本节教程将介绍如何通过Python的random模块和Tkinter库设计不重复随机点名程序

1、不重复随机点名程序介绍

我们的目标是开发一个具有图形用户界面(GUI)的程序,用户可以通过界面导入姓名文件(注意:姓名文件必须为txt文件),姓名文件成功导入后,点击界面的“随机点名”或“开始点名”开始随机点名,程序默认不重复随机点名,当名单里所有的人都被点过一次后,程序会自动从头开始没有按顺序循环点名。具体实现步骤如下:

  • 新建txt文本文档,写入需随机点名的姓名,一个姓名占一行,保存后关闭文件。

  • 进入随机点名主页面,点击右上角的“设置”按钮,可以进入设置页面。

  • 在设置页面点击“读取新的姓名文件 或 重新读取姓名”按钮,接着点击“选择文件”按钮,选择您所保存的txt姓名文件。

  • 最后,点击设置窗口的“保存”按钮,程序会回到随机点名主窗口。

  • 现在可以使用随机点名了, 点击“随机点名”按钮将会马上随机显示一个姓名。如果点击“开始点名”按钮,界面显示的姓名将不断变换,需点击“暂停点名”按钮后才能停止变换,最终随机显示一个姓名,类似我们平常生活中的抽奖效果。

  • 运行该程序后,会默认在当前脚本文件所在的同一目录中创建一个“config.json”配置文件。该程序默认使用Python的json模块保存程序的配置文件。当程序被反复使用时,会默认加载json配置文件,每次不用手动更改一些配置参数,包括页面的字体样式、大小、已加载的姓名文件路径、窗口颜色设置等。

程序的主要功能

随机不重复点名:每次点击“随机点名”按钮默认不会重复点名,并显示当前点名进度,包括剩余姓名和总姓名数,注意,该功能仅在“姓名去除重复”功能开启下使用。

记录累计抽取人数:在程序主窗口左下角会统计累计抽取的人数,点击“清零”按钮可清零累计的抽取人数。

姓名去除重复:读取姓名文件时会默认勾选“姓名去除重复”选项。

字号调整:程序主页面左上角的“+”和“-”按钮可调整显示的姓名字号大小,字号设置最大值为500。也可以在程序的设置页面通过滑块调整字体大小。

颜色设置: 可以根据自己的喜好,随意调整主窗口的字体颜色、背景颜色和按钮颜色。

字体调整:可以调整主窗口和设置窗口的文字字体样式。

设置窗口透明度:可以在设置窗口中的“主窗口透明度”滑块设置主窗口和设置窗口的透明度,范围0.0~1.0(数值越小,透明度越高)。

窗口置顶:可以通过设置窗口的“主窗口置于顶层”按钮,使主窗口保持在最顶层显示。

2、不重复随机点名程序界面演示

Python设计不重复随机点名程序

Python设计不重复随机点名程序

3、代码实现

(1)导入必要的库

# GUI应用图形界面
import tkinter as tk
# 导入ttk模块,下拉菜单控件和分割线会用到ttk模块
from tkinter import ttk
# 显示对话框、滚动条、文件操作、颜色选择和字体
from tkinter import messagebox, scrolledtext, filedialog, colorchooser, font
# 用于精准地获取错误异常
from traceback import format_exc
# 用于从列表里随机抽取和打乱列表
from random import sample, shuffle
# 导入系统操作、时间、多线程、JSON数据处理、打开网页等功能
import os, sys, time, threading, json, webbrowser

这里导入了tkinter库用于创建GUI应用图形界面,tkinter的filedialog和messagebox用于文件选择和消息提示。利用random库生成随机序列,保证点名的随机性。sys库用于处理与Python解释器的交互。os库用于文件和文件夹操作。json模块可以保存程序的设置选项到JSON文件内,下次打开程序时会默认加载JSON文件保存的设置选项。

(2)不重复随机点名程序的完整代码如下所示

动手练一练:

# GUI应用图形界面
import tkinter as tk
# 导入ttk模块,下拉菜单控件和分割线会用到ttk模块
from tkinter import ttk
# 显示对话框、滚动条、文件操作、颜色选择和字体
from tkinter import messagebox, scrolledtext, filedialog, colorchooser, font
# 用于精准地获取错误异常
from traceback import format_exc
# 用于从列表里随机抽取和打乱列表
from random import sample, shuffle
# 导入系统操作、时间、多线程、JSON数据处理、打开网页等功能
import os, sys, time, threading, json, webbrowser

# 获取当前脚本文件所在的目录
default_directory = os.path.dirname(os.path.abspath(__file__))
# 将当前脚本文件所在的目录设置为工作目录
os.chdir(default_directory)

file = 'names.txt' # 定义默认文件名为“names.txt”
names = []  # 定义一个空列表,用于存放已读取的姓名
background = "#e8e7e7" # 定义默认背景色
button_bg = "#d5d3d2" # 定义默认按钮背景色
name_color = "#000000" # 定义默认姓名颜色
have_names = [] # 定义一个空列表,用于存放未抽取的姓名
process = False # 抽取过程显示
fonts = ["默认字体"] # 定义初始化字体
json_config_file = 'config.json' # 定义json数据文件名称
# 初始化数据
data = {
    'total': 0, # 累计人数
    'font': fonts[0], # 字体
    'font_size': 100, # 字号
    'file': 'names.txt', # 姓名文件
    'history_files': [], # 历史姓名文件,最多保存15个
    'background': "#e8e7e7", # 背景色
    'button_bg': "#d5d3d2", # 按钮背景色
    'name_color': '#000000', # 姓名颜色
    '-alpha': 1.0, # 透明度
    '-topmost': False, # 是否置于顶层
    'encoding': "自动", # 文件默认编码
    'unrepeat': True, # 是否去除重复姓名
    'add_1': False, # 是否打开加“+1”模式
    'add_1_tip': True, # 是否开启“+1”提示
    'show_process': True, # 是否显示“随机点名”过程动画
    'window_auto_center': True, # 是否启用点名结束窗口自动居中
    'hide_unnecessary': False, # 主窗口隐藏不必要的
    'no_repeat_roll_call': True, #  不重复点名
    'names': [], # 读取的姓名
    'have_names': [], # 未抽取的姓名
    'software_name': 'No Repeat Random Roll Call', #程序名
    'program_path': sys.argv[0], # 程序路径
    'author': 'Pyhint制作', # 制作人
    'time': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), # 时间
}
default_config = data.copy() # 设置数据默认值

# 编码说明:UTF兼容ISO8859-1和ASCII,GB18030兼容GBK,GBK兼容GB2312,GB2312兼容ASCIl
CODES = ['UTF-8', "GB18030", "BIG5", "UTF-16"]
# UTF-8编码BOM前缀字节
UTF_8_BOM = b"\xef\xbb\xbf"

# 定义函数,用于获取字符编码类型
def name_string_coding(x):
    for code in CODES:
        try:
            x.decode(encoding=code)
            if "UTF-8" == code and x.startswith(UTF_8_BOM):
                return "UTF-8-SIG"
            else:
                return code
        except Exception:
            continue
    return "未知字符编码类型"

# 定义函数,用于获取文件编码类型
def name_file_coding(files):
    coding = "UTF-8"
    with open(files, "rb") as a:
        coding = name_string_coding(a.read())
    return coding

# 定义函数,用于保存数据到JSON文件,如果JSON文件不存在则自动创建
def save_json_data(data, json_config_file):
    # 将数据写入JSON文件
    with open(json_config_file, 'w') as f:
        json.dump(data, f, indent=4)
    return data

# 定义函数,用于读取JSON数据
def read_json_data(data, json_config_file):
    # 判断JSON文件是否存在
    if os.path.isfile(json_config_file):
        with open(json_config_file, 'r') as f:
            try: 
                # 尝试把json转换成Python的数据类型
                new_data = json.load(f)
                return new_data
            except:
                error_window = tk.Tk()
                # 隐藏窗口,防止弹出对话框时出现白框
                error_window.withdraw()
                # 无法读取文件,则弹出提示信息
                messagebox.showerror(
                    title = '数据损坏,无法读取!',
                    message='数据损坏,无法读取!\n已恢复默认数据')
                # 销毁临时窗口
                error_window.destroy()
                del error_window
                save_json_data(data, json_config_file)
            return data
    else:
        save_json_data(data, json_config_file)
        return data

# 定义函数,用于改变数据
def data_change(new_data, old_data):
    # 复制数据
    data = new_data.copy()
    # 获取旧数据的所有键
    old_keys = list(old_data.keys())

    # 如果没有则加上
    for key in old_keys:
        if key not in data:
            data[key] = old_data[key]
    return data

# 定义函数,用于显示“+1”功能介绍
def show_add_1_help():
    info = """“+1”功能介绍:
开启“+1”功能后, 在抽取姓名时,会在姓名后增加一个“+1”按钮
在指定姓名后点击“+1”按钮,则会增加该姓名被点到的概率,会在已有名单中追加这个人的名字个数
比如本来姓名文件里面只有一个姓名“张三”,现在会在抽取姓名过程中加上“张三1”,“张三2”,以此类推
点“+1”按钮后默认会显示“+1”提示,可以在设置页面中关闭“+1”提示
注意:“+1”的状态不会被保存,“+1”功能在程序保存设置或重新导入姓名数据时会被重置。
"""
    information_show(information=info, title="“+1”功能说明")

# 定义函数,用于显示提示信息
def information_show(information="", title="信息"):
    global window_information
    global scrolledtext_information

    # 保存提示信息
    def file_save(information=information, title=title):
        filename = tk.filedialog.asksaveasfilename(
            title='请选择你要保存的地方', filetypes=[('TXT', '*.txt'), ('All Files', '*')],
            initialfile='%s' % title,
            defaultextension = 'txt',
            )
        if filename == '':
            return False
        else:
            with open(filename, 'w') as f:
                f.write(information)
            return True
    try:
        window_information.deiconify()
        window_information.title(title)
        scrolledtext_information.delete(0.0, tk.END)
        scrolledtext_information.insert(tk.END, information)
    except:
        window_information = tk.Tk()
        window_information.title(title)
        # 带有滚动条的文本框设置
        scrolledtext_information = tk.scrolledtext.ScrolledText(
            window_information,
            width=70,
            height=30,
            undo=True
            )
        scrolledtext_information.pack(expand=tk.YES, fill=tk.BOTH, padx=5, pady=5)
        scrolledtext_information.insert(tk.INSERT, information)

        save_frame = tk.Frame(window_information)
        save_frame.pack()

        save_button = tk.Button(
                save_frame,
                text="保存为文本文档(*.txt)",
                command=lambda:file_save(information=scrolledtext_information.get('1.0', tk.END).rstrip()))
        save_button.pack(side=tk.RIGHT, padx=5,pady=5)

        close_button = tk.Button(
                save_frame,
                text="关闭",
                command=window_information.destroy)
        close_button.pack(side=tk.RIGHT, padx=5,pady=5)

        # 将当前内容复制到剪贴板
        def copy_to_clipboard():
            window_information.clipboard_clear()
            window_information.clipboard_append(scrolledtext_information.get('1.0', tk.END).rstrip())

        copy_button = tk.Button(save_frame, text="复制内容到剪贴板", command=copy_to_clipboard)
        copy_button.pack(side=tk.LEFT, padx=5,pady=5)

        window_information.mainloop()

# 定义函数,当点击“保存”按钮时,用于保存配置数据
def save_config():
    b = True
    info = """保存设置时出错!\n请检查输入是否合规!姓名文件是否存在!
若您无法解决此问题,请登录https://www.pyhint.com查看教程内容

以下是可能出错的原因:
"""
    error_information = "\n\n详细报错如下:\n"
    global data
    global background_set
    global button_bg_set
    global encoding_set_combobox
    if read_file:
        try:
            name_file = name_file_combobox.get()
            encoding = encoding_set_combobox.get()
            if not os.path.exists(name_file):
                pass
            else:
                if encoding == "自动":
                    encoding = name_file_coding(name_file)
                open_file = open(name_file, 'r', encoding=encoding)
                names = open_file.readlines()
                open_file.close()
                # 删除每行多余的回车符号
                for i in range(len(names)):
                    if i != len(names) - 1:
                        names[i] = names[i][:-1]
                    else:
                        if names[i][-1] == "\n":
                            names[i] = names[i][:-1]
                # 删除多余的空行
                for i in range(len(names)):
                    while i < len(names) and names[i] == "":
                        del names[i]
                # 实现去除重复姓名功能,会自动排序
                if var_unrepeat_Checkbutton.get():
                    names = list(set(names))
                shuffle(names) # 打乱姓名列表
                # 保存姓名文件路径到下拉文件列表中,最多保存15个
                if name_file in data['history_files']:
                    data['history_files'].remove(name_file)
                if name_file != data['file']:
                    if data['file'] not in data['history_files']:
                        if len(data['history_files']) >= 15:
                            data['history_file'].pop()
                        data['history_files'].insert(0, data['file'])
                    else:
                        data['history_files'].remove(data['file'])
        except UnicodeDecodeError:
            b = False
            string = (f'用“{encoding_set_combobox.get()}”编码解码文件“{name_file_combobox.get()}”失败!\n'
                    "您可以更换其它编码试一下,推荐“自动”、“UTF-8”、“UTF-8-SIG”、“GB18030”\n"
                    )
            info += string
            error_information += string + "报错:\n" + format_exc() +"\n"
        except LookupError:
            b = False
            string = (f'不支持用“{encoding_set_combobox.get()}”编码解码文件“{name_file_combobox.get()}”失败!\n'
                    "可能是编码拼写错误,请检查编码拼写\n"
                    )
            info += string
            error_information += string + "报错:\n" + format_exc() +"\n"
        except:
            b = False
            string = '读取文件“%s”失败!\n' % name_file_combobox.get()
            info += string
            error_information += string + "报错:\n" + format_exc() +"\n"
    try:
        background_set['bg'] = input_background.get()
        data['background'] = input_background.get()
    except:
        b = False
        string = '无效的背景颜色“%s”!\n' % input_background.get()
        info += string
        error_information += string + "报错:\n" + format_exc() +"\n"
    try:
        button_bg_set['bg'] = vbutton_bg.get()
        data['button_bg'] = vbutton_bg.get()
    except:
        b = False
        string = '无效的按钮颜色“%s”!\n' % vbutton_bg.get()
        info += string
        error_information += string + "报错:\n" + format_exc() +"\n"
    try:
        name_color_show_Frame['bg'] = vname_color.get()
        data['name_color'] = vname_color.get()
    except:
        b = False
        string = '无效的姓名颜色“%s”!\n' % vname_color.get()
        info += string
        error_information += string + "报错:\n" + format_exc() +"\n"

    if b:
        data['background'] = input_background.get()
        data['button_bg'] = vbutton_bg.get()
        data['name_color'] = vname_color.get()
        data['font_size'] = int(var_font_size_Scale.get())
        data['-alpha'] = float(var_opacity_Scale.get())
        data['-topmost'] = topmost_button.get()
        data['encoding'] = encoding_set_combobox.get()
        data['font'] = font_set_combobox.get()
        data['add_1']  = var_add_1_Checkbutton.get()
        data['add_1_tip'] = var_add_1_tip_Checkbutton.get()
        data['show_process'] = var_show_process_Checkbutton.get()
        data['window_auto_center'] = var_window_auto_center_Checkbutton.get()
        data['hide_unnecessary'] = var_hide_unnecessary_Checkbutton.get()
        data['no_repeat_roll_call'] = setting_no_repeat_roll_call
        if read_file:
            data['unrepeat'] = var_unrepeat_Checkbutton.get()
            data['file'] = name_file
            data['names'] = names
            data['have_names'] = data['names'].copy()
        else:
            data['have_names'] = setting_have_names
        # 保存数据
        save_json_data(data, json_config_file)
        set_up_window.destroy()  # 销毁设置窗口
    else:
        # 保存设置出错时,弹出提示信息
        information_show(information=info+error_information, title="保存设置时出错!")

# 定义函数,用于判断设置中“+1”按钮是否显示
def pack_packforget_preview_add_1_button():
    global var_add_1_Checkbutton
    global add_1_name_preview_button
    global add_1_tip_Checkbutton
    if var_add_1_Checkbutton.get():
        add_1_name_preview_button.pack(side=tk.LEFT)
        add_1_tip_Checkbutton.pack(side=tk.RIGHT)
    else:
        add_1_name_preview_button.pack_forget()
        add_1_tip_Checkbutton.pack_forget()

# 定义函数,用于恢复默认设置
def restore_default():
    global file
    global background
    global button_bg
    global name_color
    global background_set
    global button_bg_set
    global name_color_show_Frame

    file = default_config['file']
    background = default_config['background']
    button_bg = default_config['button_bg']
    name_color = default_config['name_color']

    background_set['bg'] = default_config['background']
    button_bg_set['bg'] = default_config['button_bg']
    name_color_show_Frame['bg'] = default_config['name_color']

    name_file_combobox.set(default_config['file'])
    input_background.set(default_config['background'])
    vbutton_bg.set(default_config['button_bg'])
    vname_color.set(default_config['name_color'])
    var_font_size_Scale.set(default_config['font_size'])
    var_opacity_Scale.set(str(default_config['-alpha']))
    topmost_button.set(default_config['-topmost'])
    var_unrepeat_Checkbutton.set(default_config['unrepeat'])
    encoding_set_combobox.set(default_config['encoding'])
    font_set_combobox.set(default_config['font'])
    set_up_window.wm_attributes("-alpha", default_config['-alpha'])
    preview_data["font"]= (default_config['font'], default_config['font_size'])
    name_preview_button["font"]= (default_config['font'], 20)
    var_add_1_Checkbutton.set(default_config['add_1'])
    var_add_1_tip_Checkbutton.set(default_config['add_1_tip'])
    var_show_process_Checkbutton.set(default_config['show_process'])
    var_window_auto_center_Checkbutton.set(default_config['window_auto_center'])
    var_hide_unnecessary_Checkbutton.set(default_config['hide_unnecessary'])
    var_no_repeat_roll_call.set(default_config['no_repeat_roll_call'])
    first_read()
    reset_setting_command()
    reset_command()

# 定义函数,用于首次启动时程序时读姓名
def first_read():
    global data
    try:
        name_file = data['file']
        if not os.path.exists(name_file):
            pass
        else:
            if data['encoding'] == "自动":
                encoding = name_file_coding(name_file)
            else:
                encoding = data['encoding']
            f = open(name_file, 'r',encoding=encoding)
            names = f.readlines()
            f.close()
            # 删掉每行多余的回车
            for i in range(len(names)):
                if i != len(names) - 1:
                    names[i] = names[i][:-1]
                else:
                    if names[i][-1] == "\n":
                        names[i] = names[i][:-1]
            # 删掉多余的空行
            for i in range(len(names)):
                while i < len(names) and names[i] == "":
                    del names[i]
             # 去除重复姓名
            if data['unrepeat']:
                names = list(set(names))
            shuffle(names) # 打乱姓名列表
            data['names'] = names
    except:
        pass

# 定义函数,用于改变背景颜色
def change_background_color(color):
    global background_set
    global preview_data
    global preview_frame
    global name_preview_frame
    try:
        background_set['bg'] = color
        preview_data['bg'] = color
        preview_frame['bg'] = color
        name_preview_frame['bg'] = color
    except:
        pass
    return True

# 定义函数,用于改变按钮颜色
def change_button_bg_color(color):
    global button_bg_set
    global name_preview_button
    global add_1_example_Button
    try:
        button_bg_set['bg'] = color
        name_preview_button['bg'] = color
        add_1_name_preview_button['bg'] = color
    except:
        pass
    return True

# 定义函数,用于改变姓名颜色
def change_name_color_show(color):
    global name_color_show_Frame
    global preview_data
    try:
        name_color_show_Frame['bg'] = color
        preview_data['fg'] = color
    except:
        pass
    return True

def colorchooser_change_background_color():
    color = colorchooser.askcolor(data['background'])
    if color[1] != None:
        input_background.set(color[1])

def colorchooser_change_button_bg_color():
    color = colorchooser.askcolor(data['button_bg'])
    if color[1] != None:
        vbutton_bg.set(color[1])

def colorchooser_change_name_color():
    color = colorchooser.askcolor(data['name_color'])
    if color[1] != None:
        vname_color.set(color[1])

def no_repeat_roll_call():
    if var_no_repeat_roll_call.get():
        data['no_repeat_roll_call'] = True
        reset_Button.pack(side=tk.LEFT)
        names_remaining.pack(side=tk.LEFT)
    else:
        data['no_repeat_roll_call'] = False
        reset_Button.pack_forget()
        names_remaining.pack_forget()

    # 窗口大小自适应
    window.update()
    width = window.winfo_reqwidth()
    height = window.winfo_reqheight()
    if data['window_auto_center']:
        # 启用窗口自动居中
        screen_width = window.winfo_screenwidth()  # 获取显示屏宽度
        screen_height = window.winfo_screenheight()  # 获取显示屏高度
        window.geometry("%dx%d+%d+%d" % (width, height, (screen_width - width) / 2, (screen_height - height) / 2) )
    else:
        window.geometry("%dx%d" % (width, height))

# 定义函数,用于显示“不重复点名”部分信息
def setting_no_repeat_roll_call_command():
    global setting_no_repeat_roll_call
    if var_no_repeat_roll_call.get():
        setting_no_repeat_roll_call = True
        reset_Button.pack(side=tk.LEFT)
        names_remaining.pack(side=tk.LEFT)
    else:
        setting_no_repeat_roll_call = False
        reset_Button.pack_forget()
        names_remaining.pack_forget()

# 定义函数,用于增加页面显示的字号
def increase_font_size():
    global data
    if data['font_size'] <= 495:
        name_set['font'] = (data['font'], data['font_size']+5)
        data['font_size'] += 5

        # 窗口大小自适应
        window.update()
        width = window.winfo_reqwidth()
        height = window.winfo_reqheight()
        if data['window_auto_center']:
            # 启用窗口自动居中
            screen_width = window.winfo_screenwidth()  # 获取显示屏宽度
            screen_height = window.winfo_screenheight()  # 获取显示屏高度
            window.geometry("%dx%d+%d+%d" % (width, height, (screen_width - width) / 2, (screen_height - height) / 2) )
        else:
            window.geometry("%dx%d" % (width, height))

# 定义函数,用于减少页面显示的字号
def reduce_font_size():
    global data
    if data['font_size'] > 5:
        name_set['font'] = (data['font'], data['font_size']-5)
        data['font_size'] -= 5

        # 窗口大小自适应
        window.update()
        width = window.winfo_reqwidth()
        height = window.winfo_reqheight()
        if data['window_auto_center']:
            # 启用窗口自动居中
            screen_width = window.winfo_screenwidth()  # 获取显示屏宽度
            screen_height = window.winfo_screenheight()  # 获取显示屏高度
            window.geometry("%dx%d+%d+%d" % (width, height, (screen_width - width) / 2, (screen_height - height) / 2) )
        else:
            window.geometry("%dx%d" % (width, height))

# “点名”页面的复位函数
def reset_command():
    global data
    names_remaining['text'] = "剩余姓名:"+str(len(data['names']))+'/'+ str(len(data['names']))
    data['have_names'] = data['names'].copy()

# 设置页面的复位函数
def reset_setting_command():
    global setting_have_names
    names_remaining['text'] = "剩余姓名:"+str(len(data['names']))+'/'+ str(len(data['names']))
    setting_have_names = data['names'].copy()

# 定义函数,用于显示“随机点名”过程动画
def show_process():
    global data
    roll_call_button['state'] = tk.DISABLED   # 禁用
    roll_call_start['state'] = tk.DISABLED   # 禁用
    # 随机点名过程动画
    if data['show_process']:
        # 判断如果没有勾选“不重复点名”
        if not data['no_repeat_roll_call']:
            if len(data['names']) > 7:
                names_choose = sample(data['names'], 7) # 随机抽取7个名字
            else:
                names_choose = data['names']
        else:
            if len(data['have_names']) > 7:
                names_choose = sample(data['have_names'], 7) # 随机抽取7个名字
            else:
                names_choose = data['have_names']
        # 显示抽取过程动画
        for i in range(len(names_choose)):
            name_set['text'] = names_choose[i]
            time.sleep((i + 1) / 35)

    # 如果没有勾选“不重复点名”
    if not data['no_repeat_roll_call']:
        name = sample(data['names'], 1)[0] # 随机抽取1个名字
    else:
        # 如果勾选“不重复点名”,则不重复点名
        name = sample(data['have_names'], 1)[0] # 随机抽取1个名字
        data['have_names'].remove(name)

        if(len(data['have_names']) == 0):
            data['have_names'] = data['names'].copy()

        names_remaining['text'] = '剩余姓名:'+str(len(data['have_names']))+'/'+ str(len(data['names']))

    name_set['text'] = name
    data['total'] += 1
    total_Label['text'] = '累计抽取人数:' + str(data['total'])

    roll_call_button['state'] = tk.NORMAL     # 重新激活
    roll_call_start['state'] = tk.NORMAL     # 重新激活

    # 窗口大小自适应
    window.update()
    width = window.winfo_reqwidth()
    height = window.winfo_reqheight()
    if data['window_auto_center']:
        # 启用窗口自动居中
        screen_width = window.winfo_screenwidth()  # 获取显示屏宽度
        screen_height = window.winfo_screenheight()  # 获取显示屏高度
        window.geometry("%dx%d+%d+%d" % (width, height, (screen_width - width) / 2, (screen_height - height) / 2) )
    else:
        window.geometry("%dx%d" % (width, height))

    sys.exit() # 退出线程

# 点击“随机点名”按钮触发的函数
def random_roll_call():
    if len(data['names']) == 0:
        messagebox.showerror(title = '没有指定姓名文件',
            message='没有指定姓名文件!\n或姓名文件被删除!\
\n或姓名文件为空\n请先指明姓名文件!')
    elif len(data['names']) == 1:
        name_set['text'] = data['names'][0]
        data['total'] += 1
        total_Label['text'] = '累计抽取人数:' + str(data['total'])
    else:
        # 开始抽取线程
        threading.Thread(target=show_process).start()

# 定义函数,用于清零统计
def clearing_set():
    global data
    data['total'] = 0
    total_Label['text'] = '累计抽取人数:0'

# 定义滚动点名线程函数
def roll_call_begins():
    name_index = 0
    if(not data['no_repeat_roll_call']): # 如果“不重复点名”未勾选
        while process:
            if(name_index == len(data['names'])):
                name_index = 0
            name_set['text'] = data['names'][name_index]
            name_index += 1
            time.sleep(0.02)
    else:
        only_one = False
        if(len(data['have_names']) == 1):
            name_set['text'] = data['have_names'][0]
            data['have_names'] = data['names'].copy()
            start_pause() # 只剩1人时停止滚动点名
            only_one = True
        while process:
            if(name_index == len(data['have_names'])):
                name_index = 0
            name_set['text'] = data['have_names'][name_index]
            name_index += 1
            time.sleep(0.02)
        if(not only_one):
            data['have_names'].remove(name_set['text'])
        names_remaining['text'] = '剩余姓名:'+str(len(data['have_names']))+'/'+ str(len(data['names']))

    data['total'] += 1
    total_Label['text'] = '累计抽取人数:' + str(data['total'])

# 定义函数,用于显示点名过程画面
def start_pause():
    global process
    global data

    if len(data['names']) == 0:
        messagebox.showerror(title = '没有指定姓名文件',
                            message='没有指定姓名文件!\n或姓名文件被删除!\n请先指明姓名文件!')
    elif len(data['names']) == 1:
        name_set['text'] = data['names'][0]
        data['total'] += 1
        total_Label['text'] = '累计抽取人数:' + str(data['total'])
    else:
        if process:
            roll_call_button['state'] = tk.NORMAL # 重新激活
            process = False
            roll_call_start['text'] = '开始点名'

            # 窗口大小自适应
            window.update()
            width = window.winfo_reqwidth()
            height = window.winfo_reqheight()
            if data['window_auto_center']:
                # 启用窗口自动居中
                screen_width = window.winfo_screenwidth()  # 获取显示屏宽度
                screen_height = window.winfo_screenheight()  # 获取显示屏高度
                window.geometry("%dx%d+%d+%d" % (width, height, (screen_width - width) / 2, (screen_height - height) / 2) )
            else:
                window.geometry("%dx%d" % (width, height))
        else:
            process = True
            roll_call_button['state'] = tk.DISABLED # 禁用
            roll_call_start['text'] = '暂停点名'
            # 开始滚动点名
            threading.Thread(target=roll_call_begins).start()

# 定义函数,用于设置“重复姓名”
def auto_disabled_no_repeat_roll_call_Frame():
    if var_unrepeat_Checkbutton.get():
        no_repeat_Checkbutton['state'] = tk.NORMAL     # 重新激活
        reset_Button['state'] = tk.NORMAL     # 重新激活
        names_remaining['state'] = tk.NORMAL     # 重新激活
    else:
        no_repeat_Checkbutton['state'] = tk.DISABLED   # 禁用
        reset_Button['state'] = tk.DISABLED   # 禁用
        names_remaining['state'] = tk.DISABLED   # 禁用

# 定义函数,用于显示设置窗口
def set_up():
    global window
    global set_up_window
    global file
    global background
    global button_bg
    global background_set
    global button_bg_set
    global name_color_show_Frame
    global process

    global setting_have_names
    global setting_no_repeat_roll_call
    setting_have_names = data['have_names']
    setting_no_repeat_roll_call = data['no_repeat_roll_call']
    global read_file
    read_file = False
    process = False

    file = data['file']
    background = data['background']
    window.destroy()  # 销毁主窗口

    set_up_window=tk.Tk()  # 设置窗口
    set_up_window.title("随机点名工具——设置页面")
    set_up_window.resizable(0,0)  # 禁止调节窗口大小

    global input_background
    global vbutton_bg
    global vname_color

    input_background = tk.StringVar()
    vbutton_bg = tk.StringVar()
    vname_color = tk.StringVar()

    input_background.set(data['background'])
    vbutton_bg.set(data['button_bg'])
    vname_color.set(data['name_color'])

    # 定义函数,用于打开指定文件
    def file_choice():
        filename = filedialog.askopenfilename(
            title = '请选择包含姓名的txt后缀名文件',
            filetypes=[('TXT', '*.txt'), 
            ('All Files', '*')],
            )
        if filename != '':
            name_file_combobox.set(filename)
        roll_call_setting()

    # 定义函数,当选择下拉文件路径时,获取姓名文件名单
    def on_combobox_select(event):
        selected_value = event.widget.get()
        with open(selected_value, 'r', encoding='utf-8') as file:
            names_text.delete(1.0, tk.END)  # 清空Text控件内容
            for line in file:
                names_text.insert(tk.END, line)  # 将文件内容逐行插入Text控件

    # 定义函数,用于显示设置页面
    def roll_call_setting():
        global read_file
        label1.pack(side=tk.LEFT)
        scrollbar.pack(side="right", fill="y")
        names_text.pack(padx=20, pady=10, fill="both", expand=True)
        name_file_combobox.pack(expand=tk.YES, fill=tk.X, side=tk.LEFT)    # 姓名文件输入框
        choice_button.pack(side=tk.RIGHT) # 选择文件
        label_coding.pack(side=tk.LEFT)    # 姓名文件编码
        encoding_set_combobox.pack(expand=tk.YES, fill=tk.X, side=tk.LEFT)    # 文件编码输入框
        unrepeat_Checkbutton['state'] = tk.NORMAL     # 重新激活
        roll_call_setting_Button.pack_forget()
        no_repeat_roll_call_help_Label.pack_forget()
        read_file = True
        name_file = name_file_combobox.get()
        with open(name_file, 'r', encoding='utf-8') as file:
            names_text.delete(1.0, tk.END)  # 清空Text控件内容
            for line in file:
                names_text.insert(tk.END, line)  # 将文件内容逐行插入Text控件

    # 设置左边的文件读取框架
    win_menu = tk.Frame(set_up_window)
    win_menu.pack(side=tk.LEFT, expand=tk.YES, fill=tk.Y, padx=5, pady=5)

    roll_call_setting_Button = tk.Button(win_menu,
                text="读取新的姓名文件 或 重新读取姓名",
               bg = "#4cc0fe",
               font=(data['font'], 14),
               command=roll_call_setting,
               )
    roll_call_setting_Button.pack(expand=tk.YES, fill=tk.X, padx=5)

    label1 = tk.Label(win_menu, text="姓名文件内容:", font=(data['font'], 12))

    # 设置下拉文件选择
    global name_file_combobox
    name_file_combobox = ttk.Combobox(win_menu, width=30)
    name_file_combobox['value'] = [data['file']] + data['history_files']
    name_file_combobox.set(data["file"])
    name_file_combobox.bind('<<ComboboxSelected>>', on_combobox_select)

    # 姓名文件选择按钮
    choice_button = tk.Button(win_menu, text="选择\n文件", font=(data['font'], 12), command=file_choice)
    # 创建垂直滚动条
    scrollbar = tk.Scrollbar(win_menu, orient="vertical")
    # 创建输入框ScrolledText小部件,用于输入参与点名的所有名单,并配置滚动条
    names_text = scrolledtext.ScrolledText(win_menu, yscrollcommand=scrollbar.set, height=10, width=30, font=(data['font'], 12))

    # 将滚动条与ScrolledText小部件关联
    scrollbar.config(command=names_text.yview)

    # 设置右边的预览框架
    name_preview = tk.Frame(set_up_window)
    name_preview.pack(side=tk.LEFT, expand=tk.YES, fill=tk.Y)

    global preview_frame
    preview_frame = tk.LabelFrame(name_preview, text='预览', padx=5, pady=5, bg = data['background'], font=(data['font'], 12))
    preview_frame.pack(side=tk.RIGHT)

    global name_preview_frame
    name_preview_frame = tk.Frame(preview_frame, bg = data['background'])
    name_preview_frame.pack(padx=5, pady=5)

    global preview_data
    preview_data = tk.Label(name_preview_frame,
                               text="姓名",
                               bg = data['background'],
                               fg = data['name_color'],
                               font=(data['font'], data['font_size']),
                               )
    preview_data.pack(side=tk.LEFT)

    global add_1_name_preview_button
    add_1_name_preview_button= tk.Button(name_preview_frame,
                                text="+1",
                                bg = data['button_bg'],
                                font=(data['font'], 12),
                                command=show_add_1_help
                                )

    if data['add_1']:
        add_1_name_preview_button.pack(side=tk.LEFT)

    # 定义函数,用于更改预览姓名
    def change_preview_name():
        update_preview_name = tk.simpledialog.askstring(title = '更改预览姓名',prompt='请输入新的预览姓名:',initialvalue = preview_data['text'])
        while update_preview_name == '':
            update_preview_name = tk.simpledialog.askstring(title = '更改预览姓名',prompt='预览姓名不能为空!\n请重新输入新的预览姓名:',initialvalue = preview_data['text'])
        preview_data['text'] = update_preview_name
    global name_preview_button
    name_preview_button = tk.Button(preview_frame,
                            text="按钮",
                            bg = data['button_bg'],
                            font=(data['font'], 20),
                            command=change_preview_name
                            )
    name_preview_button.pack(padx=20, pady=20)

    # 创建右横分割线
    separator = ttk.Separator(name_preview, orient=tk.VERTICAL)
    separator.pack(side=tk.RIGHT, padx=5, pady=5, fill=tk.Y)

    # 创建左横分割线
    separator1 = ttk.Separator(name_preview, orient=tk.VERTICAL)
    separator1.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.Y)

    win = tk.Frame(name_preview)
    win.pack(side=tk.LEFT)

    frame_coding = tk.Frame(win)
    frame_coding.pack(expand=tk.YES, fill=tk.X, padx=5, pady=5)

    label_coding = tk.Label(frame_coding, text="姓名文件编码:", font=(data['font'], 12))

    global encoding_set_combobox
    encoding_set_combobox = ttk.Combobox(frame_coding, width=15)
    encoding_set_combobox['value'] = ["自动", "ANSI","UTF-8", "UTF-8-SIG",
                                  "UTF-16", "UTF-16 LE", "UTF-16 BE",
                                  "GB18030", "GB2312", "BIG5"]
    encoding_set_combobox.set(data["encoding"])

    frame2 = tk.Frame(win)
    frame2.pack(expand=tk.YES, fill=tk.X, padx=5, pady=5)

    label2 = tk.Label(frame2, text="背景颜色:", font=(data['font'], 12))
    label2.pack(side=tk.LEFT)

    # 验证背景颜色
    change_background_color_CMD = set_up_window.register(change_background_color)
    enter_background = tk.Entry(frame2,
               textvariable=input_background,
               width=10,
               validate='key', # 当输入框被编辑时启用验证
               validatecommand=(change_background_color_CMD, '%P'),
               )
    enter_background.pack(side=tk.LEFT)

    background_set = tk.Frame(frame2, bg = data['background'], width=135, height=25)
    background_set.pack(expand=tk.YES, fill=tk.BOTH, side=tk.LEFT)

    colorchooser_Button = tk.Button(frame2,
        text="选择",
        command = colorchooser_change_background_color,
        font=(data['font'], 12),
        )
    colorchooser_Button.pack(side=tk.LEFT)

    frame3 = tk.Frame(win)
    frame3.pack(expand=tk.YES, fill=tk.X, padx=5, pady=5)
    button_bg_color_Label = tk.Label(frame3, text="按钮颜色:", font=(data['font'], 12))
    button_bg_color_Label.pack(side=tk.LEFT)

    # 验证输入的按钮颜色
    change_button_bg_color_CMD = set_up_window.register(change_button_bg_color)
    ebutton_bg = tk.Entry(frame3,
               textvariable=vbutton_bg,
               width=10,
               validate='key', # 当输入框被编辑时启用验证
               validatecommand=(change_button_bg_color_CMD, '%P'),
               )
    ebutton_bg.pack(side=tk.LEFT)

    button_bg_set = tk.Frame(frame3,
               bg = data['button_bg'],
               width=135,
               height=25,
               )
    button_bg_set.pack(expand=tk.YES, fill=tk.BOTH, side=tk.LEFT)

    colorchooser_button_bg_color_Button = tk.Button(frame3,
        text="选择",
        command = colorchooser_change_button_bg_color,
        font=(data['font'], 12),
        )
    colorchooser_button_bg_color_Button.pack(side=tk.LEFT)

    name_color_Frame = tk.Frame(win)
    name_color_Frame.pack(expand=tk.YES, fill=tk.X, padx=5, pady=5)
    name_color_show_Label = tk.Label(name_color_Frame, text="姓名颜色:", font=(data['font'], 12))
    name_color_show_Label.pack(side=tk.LEFT)

    # 验证输入的字体颜色
    change_name_color_show_CMD = set_up_window.register(change_name_color_show)
    name_color_Entry = tk.Entry(name_color_Frame,
               textvariable=vname_color,
               width=10,
               validate='key', # 当输入框被编辑时启用验证
               validatecommand=(change_name_color_show_CMD, '%P'),
               )
    name_color_Entry.pack(side=tk.LEFT)

    name_color_show_Frame = tk.Frame(name_color_Frame,
               bg = data['name_color'],
               width=135,
               height=25,
               )
    name_color_show_Frame.pack(expand=tk.YES, fill=tk.BOTH, side=tk.LEFT)

    colorchooser_name_color_Button = tk.Button(name_color_Frame,
        text="选择",
        command = colorchooser_change_name_color,
        font=(data['font'], 12),
        )
    colorchooser_name_color_Button.pack(side=tk.LEFT)

    frame4 = tk.Frame(win)
    frame4.pack(expand=tk.YES, fill=tk.X, padx=5, pady=5)
    global topmost_button
    topmost_button = tk.BooleanVar()
    topmost_button.set(data['-topmost'])
    topmost_checkbutton = tk.Checkbutton(frame4, 
                     text='主窗口置于顶层', 
                     variable=topmost_button, 
                     onvalue=True, 
                     offvalue=False,
                     font=(data['font'], 12),
                     )
    topmost_checkbutton.pack(side=tk.LEFT)

    global var_unrepeat_Checkbutton
    var_unrepeat_Checkbutton = tk.BooleanVar()
    var_unrepeat_Checkbutton.set(data['unrepeat'])
    unrepeat_Checkbutton = tk.Checkbutton(frame4, 
                     text='姓名去除重复', 
                     variable=var_unrepeat_Checkbutton, 
                     onvalue=True, 
                     offvalue=False,
                     font=(data['font'], 12),
                     command=auto_disabled_no_repeat_roll_call_Frame,
                     )
    unrepeat_Checkbutton.pack(side=tk.RIGHT)
    unrepeat_Checkbutton['state'] = tk.DISABLED   # 禁用
    add_1_Frame = tk.Frame(win)
    add_1_Frame.pack(expand=tk.YES, fill=tk.X, padx=5, pady=5)
    global var_add_1_Checkbutton
    var_add_1_Checkbutton = tk.BooleanVar()
    var_add_1_Checkbutton.set(data['add_1'])
    add_1_Checkbutton = tk.Checkbutton(add_1_Frame, 
                     text='增加被点到的概率“+1”', 
                     variable=var_add_1_Checkbutton, 
                     onvalue=True, 
                     offvalue=False,
                     font=(data['font'], 12),
                     command=pack_packforget_preview_add_1_button
                     )
    add_1_Checkbutton.pack(side=tk.LEFT)

    show_process_add_1_tip_Frame = tk.Frame(win)
    show_process_add_1_tip_Frame.pack(expand=tk.YES, fill=tk.X, padx=5, pady=5)
    global var_show_process_Checkbutton
    var_show_process_Checkbutton = tk.BooleanVar()
    var_show_process_Checkbutton.set(data['show_process'])
    show_process_Checkbutton = tk.Checkbutton(show_process_add_1_tip_Frame, 
                     text='“随机点名”抽取动画', 
                     variable=var_show_process_Checkbutton, 
                     onvalue=True, 
                     offvalue=False,
                     font=(data['font'], 12),
                     )
    show_process_Checkbutton.pack(side=tk.LEFT)
    global var_add_1_tip_Checkbutton
    var_add_1_tip_Checkbutton = tk.BooleanVar()
    var_add_1_tip_Checkbutton.set(data['add_1_tip'])
    global add_1_tip_Checkbutton
    add_1_tip_Checkbutton = tk.Checkbutton(show_process_add_1_tip_Frame, 
                     text='“+1”提示', 
                     variable=var_add_1_tip_Checkbutton, 
                     onvalue=True, 
                     offvalue=False,
                     font=(data['font'], 12),
                     )
    add_1_tip_Checkbutton.pack(side=tk.RIGHT)
    if(not data['add_1']):
        add_1_tip_Checkbutton.pack_forget()

    window_auto_center_Frame = tk.Frame(win)
    window_auto_center_Frame.pack(expand=tk.YES, fill=tk.X, padx=5, pady=5)
    global var_window_auto_center_Checkbutton
    var_window_auto_center_Checkbutton = tk.BooleanVar()
    var_window_auto_center_Checkbutton.set(data['window_auto_center'])
    window_auto_center_Checkbutton = tk.Checkbutton(window_auto_center_Frame, 
                     text='点名后窗口自动居中', 
                     variable=var_window_auto_center_Checkbutton, 
                     onvalue=True, 
                     offvalue=False,
                     font=(data['font'], 12),
                     )
    window_auto_center_Checkbutton.pack(side=tk.LEFT)
    global var_hide_unnecessary_Checkbutton
    var_hide_unnecessary_Checkbutton = tk.BooleanVar()
    var_hide_unnecessary_Checkbutton.set(data['hide_unnecessary'])
    hide_unnecessary_Checkbutton = tk.Checkbutton(window_auto_center_Frame, 
                     text='主窗口隐藏不必要的', 
                     variable=var_hide_unnecessary_Checkbutton, 
                     onvalue=True, 
                     offvalue=False,
                     font=(data['font'], 12),
                     command=lambda:messagebox.showinfo("随机点名——提示","设置隐藏后,可以在主窗口点击鼠标右键打开“设置”页面!") if var_hide_unnecessary_Checkbutton.get() else None,
                     )
    hide_unnecessary_Checkbutton.pack(side=tk.RIGHT)

    global no_repeat_roll_call_Frame
    no_repeat_roll_call_Frame = tk.Frame(win)
    no_repeat_roll_call_Frame.pack(expand=tk.YES, fill=tk.X ,padx=5, pady=5)

    global var_no_repeat_roll_call
    var_no_repeat_roll_call = tk.BooleanVar()
    var_no_repeat_roll_call.set(data['no_repeat_roll_call'])

    global no_repeat_Checkbutton
    no_repeat_Checkbutton = tk.Checkbutton(no_repeat_roll_call_Frame, 
                     bg = data['background'],
                     text='不重复点名', 
                     variable=var_no_repeat_roll_call, 
                     onvalue=1, 
                     offvalue=0,
                     font=(data['font'], 12),
                     command=setting_no_repeat_roll_call_command,
                     )
    no_repeat_Checkbutton.pack(side=tk.LEFT)

    global reset_Button
    reset_Button = tk.Button(no_repeat_roll_call_Frame,
           text="复位",
           bg=data['button_bg'],
           font=(data['font'], 12),
           command=reset_setting_command,
           )
    reset_Button.pack(side=tk.LEFT)
    if(not data['no_repeat_roll_call']):
        reset_Button.pack_forget()

    global names_remaining
    names_remaining = tk.Label(no_repeat_roll_call_Frame,
        text='剩余姓名:'+str(len(data['have_names']))+'/'+str(len(data['names'])),
        bg=data['background'],
        font=(data['font'], 12),
        )
    names_remaining.pack(side=tk.LEFT)
    if(not data['no_repeat_roll_call']):
        names_remaining.pack_forget()

    global no_repeat_roll_call_help_Label
    no_repeat_roll_call_help_Label = tk.Label(no_repeat_roll_call_Frame,
        text="需勾选姓名去除重复才能使用",
        fg="red",
        bg=data['background'],
        font=(data['font'], 12),
        )
    no_repeat_roll_call_help_Label.pack(side=tk.RIGHT)
    if(data['unrepeat']):
        no_repeat_roll_call_help_Label.pack_forget()

    auto_disabled_no_repeat_roll_call_Frame()

    font_set_frame = tk.Frame(win)
    font_set_frame.pack(expand=tk.YES, fill=tk.X ,padx=5, pady=5)
    font_set_label = tk.Label(font_set_frame, text="字体:", font=(data['font'], 12))
    font_set_label.pack(side=tk.LEFT)
    global font_set_combobox
    font_set_combobox = ttk.Combobox(font_set_frame, width=20)
    font_set_combobox['value'] = fonts
    font_set_combobox.set(data["font"])
    font_set_combobox.pack(expand=tk.YES, fill=tk.X, side=tk.LEFT)
    font_set_combobox.configure(state="readonly")
    font_example_label = tk.Label(font_set_frame, text="字体示例example", font=(data['font'], 12))
    font_example_label.pack(side=tk.RIGHT)
    def change_example_font(event):
        font_example_label["font"] = (font_set_combobox.get(), 12)
        preview_data["font"] = (font_set_combobox.get(), int(var_font_size_Scale.get()))
        name_preview_button["font"] = (font_set_combobox.get(), 20)
    font_set_combobox.bind("<<ComboboxSelected>>", change_example_font)

    font_size_Frame = tk.Frame(win)
    font_size_Frame.pack(expand=tk.YES, fill=tk.X ,padx=5, pady=5)
    font_size_Label = tk.Label(font_size_Frame, text="字体\n大小", font=(data['font'], 12))
    font_size_Label.pack(side=tk.LEFT)
    global var_font_size_Scale
    var_font_size_Scale = tk.StringVar()
    def f_example_font_size(now_font_size):
        preview_data['font'] = (font_set_combobox.get(), now_font_size)
    font_size_Scale = tk.Scale(font_size_Frame, # 滑块
        from_ = 1,  # 设置最小值
        to = 500,   # 设置最大值
        orient = tk.HORIZONTAL,  # 设置横向
        resolution=1,  # 设置步长
        tickinterval = 100,  # 设置刻度
        length = 300,  # 设置像素
        variable = var_font_size_Scale,  # 绑定变量
        command = f_example_font_size,   # 改变时调用的函数
        )
    font_size_Scale.pack(expand=tk.YES, fill=tk.X, side=tk.LEFT)
    var_font_size_Scale.set(str(data['font_size']))

    opacity_Frame = tk.Frame(win) # 透明度
    opacity_Frame.pack(expand=tk.YES, fill=tk.X ,padx=5, pady=5)
    opacity_Label = tk.Label(opacity_Frame, text="主窗口\n透明度", font=(data['font'], 12))
    opacity_Label.pack(side=tk.LEFT)    # 固定窗口位置
    global var_opacity_Scale
    var_opacity_Scale = tk.StringVar()
    opacity_Scale = tk.Scale(opacity_Frame,  # 滑块
        from_ = 0.0,  # 设置最小值
        to = 1.0,  # 设置最大值
        orient = tk.HORIZONTAL,  # 设置横向
        resolution=0.01,  # 设置步长
        tickinterval = 0.2,  # 设置刻度
        length = 290,  # 设置像素
        variable = var_opacity_Scale,  # 绑定变量
        command = lambda nowopacity_Scale: set_up_window.wm_attributes("-alpha", nowopacity_Scale) 
        )
    opacity_Scale.pack(expand=tk.YES, fill=tk.X, side=tk.LEFT)
    var_opacity_Scale.set(str(data['-alpha']))

    foot_button = tk.Frame(win)
    foot_button.pack(padx=5,pady=5)
    save_config_button = tk.Button(foot_button,
               text="保存",
               font=(data['font'], 14),
               command=save_config,
               )
    save_config_button.pack(side=tk.LEFT,padx=5,pady=5)
    cancel_button = tk.Button(foot_button,
               text="取消",
               font=(data['font'], 14),
               command=set_up_window.destroy,
               )
    cancel_button.pack(side=tk.LEFT,padx=5,pady=5)
    restore_default_button = tk.Button(foot_button,
               text="恢复默认",
               font=(data['font'], 14),
               command=restore_default,
               )
    restore_default_button.pack(side=tk.LEFT,padx=5,pady=5)

    set_up_window.wm_attributes("-alpha", data['-alpha'])
    set_up_window.mainloop()  # 运行主循环
    root()

# 显示主窗口函数
def root():
    global window
    window=tk.Tk()
    global fonts
    fonts = sorted([i for i in font.families() if not i.startswith("@")],reverse=True)
    global data
    if data["font"] == "默认字体":
        data["font"] = fonts[0]
    global default_config
    if default_config["font"] == "默认字体":
        default_config["font"] = fonts[0]
    window.title("不重复随机点名工具")

    # 设置默认为“不重复点名”
    global var_no_repeat_roll_call
    var_no_repeat_roll_call = tk.BooleanVar()
    var_no_repeat_roll_call.set(data['no_repeat_roll_call'])

    win = tk.Frame(window, bg = data['background'])
    win.pack(fill=tk.X)

    win_menu = tk.Frame(win, bg = data['background'])
    win_menu.pack(fill=tk.X)

    win_menu_left = tk.Frame(win_menu, bg = data['background'])
    win_menu_left.pack(side=tk.LEFT)
    if(data['hide_unnecessary']):
        win_menu_left.pack_forget()

    Font_size_change = tk.Label(win_menu_left, text="字号更改:", bg = data['background'], font=(data['font'], 12))
    Font_size_change.pack(side=tk.LEFT, pady=5)

    font_size_increase_button = tk.Button(win_menu_left,
               text="+",
               bg = data['button_bg'],
               font=(data['font'], 12),
               command=increase_font_size
               )
    font_size_increase_button.pack(side=tk.LEFT, pady=5)

    font_size_reduce_button = tk.Button(win_menu_left,
               text="-",
               bg = data['button_bg'],
               font=(data['font'], 12),
               command=reduce_font_size
               )
    font_size_reduce_button.pack(side=tk.LEFT, padx=5, pady=5)

    no_repeat_Checkbutton = tk.Checkbutton(win_menu_left, 
                     bg = data['background'],
                     text='不重复点名', 
                     variable=var_no_repeat_roll_call, 
                     onvalue=1, 
                     offvalue=0,
                     font=(data['font'], 12),
                     command=no_repeat_roll_call,
                     )
    no_repeat_Checkbutton.pack(side=tk.LEFT, padx=5, pady=5)
    if not data['unrepeat']: # 如果姓名可以重复
        no_repeat_Checkbutton.pack_forget()
        data['no_repeat_roll_call'] = False

    global reset_Button
    reset_Button = tk.Button(win_menu_left,
           text="复位",
           bg=data['button_bg'],
           font=(data['font'], 12),
           command=reset_command,
           )
    reset_Button.pack(side=tk.LEFT, padx=5, pady=5)
    if(not data['no_repeat_roll_call']):
        reset_Button.pack_forget()

    global names_remaining
    names_remaining = tk.Label(win_menu_left,
        text='剩余姓名:'+str(len(data['have_names']))+'/'+str(len(data['names'])),
        bg=data['background'],
        font=(data['font'], 12),
        )
    names_remaining.pack(side=tk.LEFT, padx=5, pady=5)
    if(not data['no_repeat_roll_call']):
        names_remaining.pack_forget()

    win_menu_right = tk.Frame(win_menu, bg = data['background'])
    win_menu_right.pack(side=tk.RIGHT)

    set_up_button = tk.Button(win_menu_right,
        text="设置",
        bg = data['button_bg'],
        font=(data['font'], 12),
        command=set_up,
        )
    set_up_button.pack(side=tk.RIGHT, padx=5, pady=5)

    frame2 = tk.Frame(win, bg = data['background'])
    frame2.pack()

    fname = tk.Frame(frame2,bg=data['background'])
    fname.pack()
    global name_set
    name_set = tk.Label(fname, 
        text='',
        bg = data['background'],
        fg = data['name_color'],
        font=(data['font'], data['font_size']),
        )
    name_set.pack(side=tk.LEFT)

    # 点击“+1”按钮触发的函数
    def add_1():
        global data
        now_name = name_set['text']
        if now_name == '':
            messagebox.showinfo(title='不能“+1”', message="没有抽取姓名,不能使用“+1”功能")
        else:
            while ( len(now_name) != 0 ) and ( now_name[-1] in "0123456789"):
                now_name = now_name[:-1]
            num  = 0
            while True:
                num += 1
                if now_name + str(num) not in data['names']:
                    data['names'].append(now_name + str(num))
                    data['have_names'].append(now_name + str(num))
                    shuffle(data['names']) # 打乱姓名列表
                    shuffle(data['have_names']) # 打乱姓名列表
                    break
            global names_remaining
            names_remaining['text'] = '剩余姓名:'+str(len(data['have_names']))+'/'+str(len(data['names']))
            if data['add_1_tip']:
                messagebox.showinfo(title=f"“{now_name}”被“+1”", message=f"已提高“{now_name}”被抽到的概率\n目前是“{now_name+str(num)}”")

    global add_1_Button
    add_1_Button = tk.Button(fname,
                         text="+1",
                         font=(data['font'], 12),
                         bg = data['button_bg'],
                         command=add_1
                         )
    add_1_Button.pack(side=tk.LEFT, padx=10)
    add_1_Button.pack_forget()
    if data['add_1']:
        add_1_Button.pack(side=tk.LEFT, padx=10)

    frame2_button = tk.Frame(frame2, bg = data['background'])
    frame2_button.pack()
    global roll_call_button
    roll_call_button = tk.Button(frame2_button,
               text="随机点名",
               bg = data['button_bg'],
               font=(data['font'], 20),
               command=random_roll_call,
               )
    roll_call_button.pack(side=tk.LEFT, padx=5, pady=5) 

    global roll_call_start
    roll_call_start = tk.Button(frame2_button,
               text="开始点名",
               bg = data['button_bg'],
               font=(data['font'], 20),
               command=start_pause,
               )
    roll_call_start.pack(side=tk.LEFT, padx=5, pady=5)

    frame3 = tk.Frame(win, bg = data['background'])
    frame3.pack(fill=tk.X)
    if(data['hide_unnecessary']):
        frame3.pack_forget()

    clearing_set_button = tk.Button(frame3,
               text="清零",
               bg = data['button_bg'],
               font=(data['font'], 12),
               command=clearing_set,
               )
    clearing_set_button.pack(side=tk.LEFT, padx=5, pady=5)

    global total_Label
    total_Label = tk.Label(frame3,
        text="累计抽取人数:"+str(data['total']),
        bg = data['background'],
        font=(data['font'], 12),
        )
    total_Label.pack(side=tk.LEFT, padx=5, pady=5)

    load_name_button = tk.Button(frame3,
         text="手动加载名单",
         bg = data['button_bg'],
         font=(data['font'], 12),
         command=set_up,
         )
    load_name_button.pack(side=tk.LEFT, padx=5, pady=5)

    global names_total
    names_total = tk.Label(frame3,
        text=f"一共加载了{str(len(data['names']))}个姓名",
        bg=data['background'],
        font=(data['font'], 12),
        )
    names_total.pack(side=tk.LEFT, padx=5, pady=5)
    if(not data['no_repeat_roll_call']):
        names_total.pack_forget()

    # 创建“帮助”按钮
    help_button = tk.Button(frame3,
         text="帮助",
         bg = data['button_bg'],
         font=(data['font'], 12),
         command=lambda:webbrowser.open("https://www.pyhint.com/article/145.html"),
         )
    help_button.pack(side=tk.RIGHT, padx=5, pady=5)

    # 关闭程序触发的函数
    def window_closing():
        if messagebox.askokcancel("退出", "你确定要退出吗?"):
            window.destroy()

    # 设置主窗口居中
    name_set["text"] = ""
    window.update()
    screen_width = window.winfo_screenwidth()  # 获取显示屏宽度
    screen_height = window.winfo_screenheight()  # 获取显示屏高度
    width = window.winfo_reqwidth()
    height = window.winfo_reqheight()
    window.geometry("%dx%d+%d+%d" % (width, height, (screen_width - width) / 2, (screen_height - height) / 2) )
    window.wm_attributes("-alpha", data['-alpha'])        # 透明度(0.0~1.0)
    window.wm_attributes("-topmost", data['-topmost'])    # 永远处于顶层
    window.bind("<Button-3>", lambda event : set_up())    # 绑定鼠标右键打开设置页面
    window.protocol("WM_DELETE_WINDOW", window_closing)  # 绑定关闭事件
    window.mainloop()  # 运行主循环

# 初始化程序,如果“names.txt”文件不存在,则创建“names.txt”文件
def read_name_file():
    global data
    try:
        name_file = data['file']
        if not os.path.exists(name_file):
            # 使用with语句自动打开和关闭文件,如果文件不存在则创建
            with open(name_file, 'w', encoding='utf-8') as file:
                # 使用writelines()方法一次性写入多行内容
                file.writelines(["张三\n", "李四\n", "王五\n", "赵六\n"])
                names = file.readlines()
            data['names'] = names
    except:
        pass

# 程序入口函数
def main():
    global data
    # 如果“names.txt”文件不存在,则创建“names.txt”文件
    read_name_file()

    # 读数据
    data = read_json_data(data, json_config_file)
    data = data_change(data, default_config)
    data['program_path'] = sys.argv[0] # 更新程序路径
    data['software_name'] = default_config['software_name'] # 保持程序名为当前程序名

    if(len(data['names']) == 0):
        first_read() # 第一次启动程序时读取姓名文件
    if(len(data['have_names']) == 0):
        data['have_names'] = data['names'].copy()
    root() # 显示主窗口

    data['time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) # 更新时间
    save_json_data(data, json_config_file)   # 保存数据

# 当前模块直接被执行
if __name__ == '__main__':
    main()