我们在日常工作和生活中,经常需要进行随机抽取一个结果的操作,例如点名、抽奖、抽查等。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、不重复随机点名程序界面演示
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()