利用python和deepseek生成了一段代码,用于产生单式或复式、单注或多注的双色球号码。
1. 可以选择单式或者复式
2. 可以一次生成多注
3. 可以选择屏蔽已产生的号码中的部分或者全部重新产生一组号码
4. 产生的号码会自动与历史数据(excel)比对,如果存在则重新产生一组号码
5. 支持导出已产生的号码到excel文档
6. 可以计算费用,目前处于注释状态,可以搜索cost_label和"费用"
运行效果如下图:

代码如下:
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 13 08:50:52 2025
@author: oldhen
本代码用于产生双色球号码
1. 可以选择单式或者复式
2. 可以一次生成多注
3. 可以选择屏蔽已产生的号码中的部分或者全部重新产生一组号码
4. 产生的号码会自动与历史数据(excel)比对,如果存在则重新产生一组号码
5. 支持导出已产生的号码到excel文档
6. 可以计算费用,目前处于注释状态,可以搜索cost_label和"费用"
"""
import tkinter as tk
from tkinter import ttk, messagebox
import random
import re
#import requests
#from bs4 import BeautifulSoup
import threading
from datetime import datetime
import pandas as pd
import os
class DoubleColorBallGenerator:
def __init__(self, root):
self.root = root
self.root.title("双色球号码产生器")
self.root.geometry("700x620")
self.root.resizable(False, False)
# 存储历史数据
self.history_data = []
# 存储已生成的号码用于排除
self.generated_reds = set()
self.generated_blues = set()
# 标记是否已经生成过号码
self.has_generated = False
# 存储当前生成的蓝球号码
self.current_blue_balls = []
# 存储当前生成的所有注号码
self.current_bets = []
# 加载历史数据
self.load_history_data()
self.create_widgets()
def get_current_year_suffix(self):
"""获取当前年份的后两位,并组合成期号格式"""
current_year = datetime.now().year
year_suffix = str(current_year)[-2:] # 获取年份后两位
issue_number = f"{year_suffix}152" # 组合成期号,如24152
return issue_number
def save_to_excel(self):
"""将当前生成的号码保存到Excel文件"""
try:
if not self.current_bets:
messagebox.showwarning("提示", "没有号码可保存")
return
# 准备数据
data = []
for i, (red_balls, blue_ball) in enumerate(self.current_bets):
row = {
'注号': i+1,
'红球1': red_balls[0],
'红球2': red_balls[1],
'红球3': red_balls[2],
'红球4': red_balls[3],
'红球5': red_balls[4],
'红球6': red_balls[5],
'蓝球': blue_ball,
'号码组合': f"{' '.join(map(str, red_balls))} + {blue_ball}"
}
data.append(row)
# 创建DataFrame
df = pd.DataFrame(data)
# 保存到Excel
filename = "双色球生成号码.xlsx"
df.to_excel(filename, index=False, engine='openpyxl')
messagebox.showinfo("成功", f"号码已成功保存到 {filename}")
print(f"号码已保存到 {filename},共 {len(data)} 注")
except Exception as e:
messagebox.showerror("错误", f"保存Excel文件失败: {e}")
def load_history_data(self):
"""从Excel文件加载历史开奖数据"""
def load_from_excel():
try:
filename = "双色球历史数据.xlsx"
if not os.path.exists(filename):
print(f"Excel文件 {filename} 不存在")
return
# 读取Excel文件
df = pd.read_excel(filename, engine='openpyxl')
print(f"成功读取Excel文件,共 {len(df)} 条记录")
# 处理每一行数据
for index, row in df.iterrows():
try:
# 提取红球号码
red_balls = []
for i in range(1, 7): # 红球1到红球6
col_name = f'红球{i}'
if col_name in row:
ball_value = row[col_name]
if pd.notna(ball_value):
red_balls.append(int(ball_value))
# 提取蓝球号码
blue_ball = None
if '蓝球' in row and pd.notna(row['蓝球']):
blue_ball = int(row['蓝球'])
# 验证数据完整性
if len(red_balls) == 6 and blue_ball is not None and 1 <= blue_ball <= 16:
self.history_data.append((red_balls, blue_ball))
except (ValueError, TypeError) as e:
print(f"解析第{index+1}行数据时出错: {e}")
continue
#print(f"成功加载 {len(self.history_data)} 期历史数据")
except Exception as e:
print(f"从Excel文件加载历史数据失败: {e}")
# 在新线程中加载数据
thread = threading.Thread(target=load_from_excel)
thread.daemon = True
thread.start()
def create_circle_ball(self, parent, number, color, diameter=50):
"""创建圆形球体"""
canvas = tk.Canvas(parent, width=diameter, height=diameter,
highlightthickness=0, bg=parent.cget('bg'))
# 绘制圆形
canvas.create_oval(2, 2, diameter-2, diameter-2,
fill=color, outline="black", width=1.2)
# 添加数字
canvas.create_text(diameter/2, diameter/2, text=str(number),
font=("Arial", 12, "bold"), fill="white")
return canvas
def create_widgets(self):
# 设置统一的背景色
self.root.configure(bg='#f0f0f0')
# 第一区:结果显示区 - 初始设置较小高度
result_frame = ttk.LabelFrame(self.root, text="双色球号码", padding=10)
result_frame.pack(fill="x", padx=10, pady=5) # 注意:这里使用fill="x"而不是fill="both"
# 创建带滚动条的结果区域
self.canvas = tk.Canvas(result_frame, bg='#f0f0f0', highlightthickness=0, height=150) # 初始高度设为150
scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=self.canvas.yview)
self.scrollable_frame = ttk.Frame(self.canvas)
self.scrollable_frame.bind(
"<Configure>",
lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
)
# 确保scrollable_frame填满整个Canvas宽度
self.canvas_frame = self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
# 绑定Canvas大小变化事件,确保scrollable_frame宽度跟随Canvas变化
def configure_canvas(event):
self.canvas.itemconfig(self.canvas_frame, width=event.width)
self.canvas.bind('<Configure>', configure_canvas)
self.canvas.configure(yscrollcommand=scrollbar.set)
self.canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
# 初始提示文字
self.prompt_label = tk.Label(
self.scrollable_frame,
text="请点击\"随机生成\"按钮",
font=("Arial", 20, "bold"),
fg="gray",
bg='#f0f0f0'
)
self.prompt_label.pack(pady=20)
# 号码显示区域容器
self.bets_display_frame = tk.Frame(self.scrollable_frame, bg='#f0f0f0')
# 导出按钮容器
self.export_button_frame = tk.Frame(self.scrollable_frame, bg='#f0f0f0')
# 第二区:单式/复式选择区
choice_frame = ttk.LabelFrame(self.root, text="投注方式", padding=10)
choice_frame.pack(fill="x", padx=10, pady=5)
self.bet_type = tk.StringVar(value="single")
single_radio = ttk.Radiobutton(choice_frame, text="单式",
variable=self.bet_type, value="single",
command=self.on_bet_type_change)
single_radio.pack(side="left", padx=10)
compound_radio = ttk.Radiobutton(choice_frame, text="复式",
variable=self.bet_type, value="compound",
command=self.on_bet_type_change)
compound_radio.pack(side="left", padx=10)
ttk.Label(choice_frame, text="蓝球个数:").pack(side="left", padx=(20, 5))
self.blue_count_var = tk.StringVar(value="1")
self.blue_count_entry = ttk.Entry(choice_frame, textvariable=self.blue_count_var,
width=5, state="disabled")
self.blue_count_entry.pack(side="left", padx=5)
# 添加注数输入框
ttk.Label(choice_frame, text="注数:").pack(side="left", padx=(20, 5))
self.bet_count_var = tk.StringVar(value="1")
self.bet_count_entry = ttk.Entry(choice_frame, textvariable=self.bet_count_var, width=5)
self.bet_count_entry.pack(side="left", padx=5)
# 第三区:用户个性区
custom_frame = ttk.LabelFrame(self.root, text="必选号码", padding=10)
custom_frame.pack(fill="x", padx=10, pady=5)
# 红球输入
red_frame = tk.Frame(custom_frame)
red_frame.pack(fill="x", pady=5)
ttk.Label(red_frame, text="红球必选(1-33,逗号分隔):").pack(side="left")
self.red_custom_var = tk.StringVar()
self.red_custom_entry = ttk.Entry(red_frame, textvariable=self.red_custom_var, width=30)
self.red_custom_entry.pack(side="left", padx=10)
self.red_custom_entry.bind("<KeyRelease>", self.on_red_input_change)
self.red_custom_entry.bind("<FocusOut>", self.validate_red_input)
# 蓝球输入
blue_frame = tk.Frame(custom_frame)
blue_frame.pack(fill="x", pady=5)
ttk.Label(blue_frame, text="蓝球必选(1-12,逗号分隔):").pack(side="left")
self.blue_custom_var = tk.StringVar()
self.blue_custom_entry = ttk.Entry(blue_frame, textvariable=self.blue_custom_var, width=30)
self.blue_custom_entry.pack(side="left", padx=10)
self.blue_custom_entry.bind("<KeyRelease>", self.on_blue_input_change)
self.blue_custom_entry.bind("<FocusOut>", self.validate_blue_input)
# 初始的生成按钮
button_frame = tk.Frame(self.root, bg='#f0f0f0')
button_frame.pack(pady=10)
self.generate_button = ttk.Button(button_frame, text="随机生成", command=self.on_generate_button_click)
self.generate_button.pack()
# 第四区:排除生成区(初始隐藏)
self.exclude_frame = ttk.LabelFrame(self.root, text="排除生成", padding=10)
# 初始时不显示,等待第一次生成号码后再显示
self.exclude_option = tk.StringVar(value="none")
ttk.Radiobutton(self.exclude_frame, text="不排除",
variable=self.exclude_option, value="none").pack(anchor="w")
ttk.Radiobutton(self.exclude_frame, text="排除已产生的红球",
variable=self.exclude_option, value="red").pack(anchor="w")
ttk.Radiobutton(self.exclude_frame, text="排除已产生的蓝球",
variable=self.exclude_option, value="blue").pack(anchor="w")
ttk.Radiobutton(self.exclude_frame, text="全部排除",
variable=self.exclude_option, value="all").pack(anchor="w")
# 状态栏
self.status_var = tk.StringVar(value="就绪")
status_bar = ttk.Label(self.root, textvariable=self.status_var, relief="sunken")
status_bar.pack(fill="x", side="bottom", padx=10, pady=5)
def on_generate_button_click(self):
"""生成按钮点击事件处理"""
if not self.has_generated:
# 第一次点击,执行随机生成
self.generate_numbers(is_regen=False)
# 显示排除生成区域并更新按钮位置
self.show_exclude_frame()
else:
# 后续点击,执行再次生成
self.generate_numbers(is_regen=True)
def show_exclude_frame(self):
"""显示排除生成区域并更新按钮位置"""
if not self.has_generated:
# 先隐藏初始按钮
self.generate_button.master.pack_forget()
# 显示排除生成区域
self.exclude_frame.pack(fill="x", padx=10, pady=5)
# 在排除生成区域下方添加新的生成按钮
new_button_frame = tk.Frame(self.root, bg='#f0f0f0')
new_button_frame.pack(pady=10)
self.generate_button = ttk.Button(new_button_frame, text="再次生成", command=self.on_generate_button_click)
self.generate_button.pack()
self.has_generated = True
def on_bet_type_change(self):
"""切换单式/复式时的回调"""
if self.bet_type.get() == "compound":
self.blue_count_entry.config(state="normal")
else:
self.blue_count_entry.config(state="disabled")
def on_red_input_change(self, event=None):
"""红球输入变化时的处理"""
# 允许数字和逗号,但不立即修改输入,只做验证
text = self.red_custom_var.get()
# 检查是否有非法字符(除了数字和逗号之外的字符)
if text and not re.match(r'^[\d,\s]*$', text):
# 移除非法字符,但保留逗号和空格
cleaned = re.sub(r'[^\d,\s]', '', text)
# 移除多余的空格
cleaned = re.sub(r'\s+', '', cleaned)
self.red_custom_var.set(cleaned)
def on_blue_input_change(self, event=None):
"""蓝球输入变化时的处理"""
# 允许数字和逗号,但不立即修改输入,只做验证
text = self.blue_custom_var.get()
# 检查是否有非法字符(除了数字和逗号之外的字符)
if text and not re.match(r'^[\d,\s]*$', text):
# 移除非法字符,但保留逗号和空格
cleaned = re.sub(r'[^\d,\s]', '', text)
# 移除多余的空格
cleaned = re.sub(r'\s+', '', cleaned)
self.blue_custom_var.set(cleaned)
def validate_red_input(self, event=None):
"""验证红球输入"""
text = self.red_custom_var.get().strip()
if text:
# 清理输入,移除所有空格
cleaned = re.sub(r'\s+', '', text)
# 分割数字并去重
numbers = []
for num_str in cleaned.split(','):
if num_str.strip():
try:
num = int(num_str.strip())
if 1 <= num <= 33 and num not in numbers:
numbers.append(num)
except ValueError:
continue
# 限制最多6个红球
if len(numbers) > 6:
numbers = numbers[:6]
messagebox.showwarning("提示", "红球最多只能选择6个号码")
# 更新输入框
self.red_custom_var.set(','.join(map(str, numbers)))
def validate_blue_input(self, event=None):
"""验证蓝球输入"""
text = self.blue_custom_var.get().strip()
if text:
# 清理输入,移除所有空格
cleaned = re.sub(r'\s+', '', text)
# 分割数字并去重
numbers = []
for num_str in cleaned.split(','):
if num_str.strip():
try:
num = int(num_str.strip())
if 1 <= num <= 12 and num not in numbers:
numbers.append(num)
except ValueError:
continue
# 限制最多12个蓝球
if len(numbers) > 12:
numbers = numbers[:12]
messagebox.showwarning("提示", "蓝球最多只能选择12个号码")
# 更新输入框
self.blue_custom_var.set(','.join(map(str, numbers)))
def parse_numbers(self, text):
"""解析逗号分隔的数字字符串"""
if not text:
return []
numbers = []
for num_str in text.split(','):
if num_str.strip():
try:
num = int(num_str.strip())
numbers.append(num)
except ValueError:
continue
return numbers
def generate_numbers(self, is_regen=False):
"""生成双色球号码"""
try:
# 获取注数
try:
bet_count = int(self.bet_count_var.get())
if bet_count < 1 or bet_count > 20:
messagebox.showerror("错误", "注数必须在1-20范围内!")
return
except ValueError:
messagebox.showerror("错误", "请输入有效的注数!")
return
# 存储当前生成的所有注
temp_bets = []
# 生成指定注数的号码
for bet_index in range(bet_count):
# 获取必选号码
custom_red = list(set(self.parse_numbers(self.red_custom_var.get())))
custom_blue = list(set(self.parse_numbers(self.blue_custom_var.get())))
# 验证必选号码
if len(custom_red) > 6:
messagebox.showerror("错误", "红球必选号码不能超过6个!")
return
if any(num < 1 or num > 33 for num in custom_red):
messagebox.showerror("错误", "红球号码必须在1-33范围内!")
return
if any(num < 1 or num > 12 for num in custom_blue):
messagebox.showerror("错误", "蓝球号码必须在1-12范围内!")
return
# 获取排除选项
exclude_red = self.exclude_option.get() in ["red", "all"] and is_regen
exclude_blue = self.exclude_option.get() in ["blue", "all"] and is_regen
# 生成红球
available_red = [i for i in range(1, 34) if i not in custom_red]
# 排除已生成的红球
if exclude_red:
available_red = [num for num in available_red if num not in self.generated_reds]
need_red_count = 6 - len(custom_red)
if need_red_count > len(available_red):
messagebox.showerror("错误", "可用的红球号码不足!")
return # 直接返回,不修改当前显示的号码
selected_red = custom_red + random.sample(available_red, need_red_count)
selected_red.sort()
# 生成蓝球
if self.bet_type.get() == "single":
# 单式:1个蓝球
if custom_blue:
selected_blue = custom_blue[0]
else:
available_blue = [i for i in range(1, 13)]
# 排除已生成的蓝球
if exclude_blue:
available_blue = [num for num in available_blue if num not in self.generated_blues]
if not available_blue:
messagebox.showerror("错误", "可用的蓝球号码不足!")
return # 直接返回,不修改当前显示的号码
selected_blue = random.choice(available_blue)
blue_balls = [selected_blue]
else:
# 复式:多个蓝球
try:
blue_count = int(self.blue_count_var.get())
if blue_count < 1 or blue_count > 12:
messagebox.showerror("错误", "蓝球个数必须在1-12范围内!")
return
except ValueError:
messagebox.showerror("错误", "请输入有效的蓝球个数!")
return
available_blue = [i for i in range(1, 13) if i not in custom_blue]
# 排除已生成的蓝球
if exclude_blue:
available_blue = [num for num in available_blue if num not in self.generated_blues]
need_blue_count = blue_count - len(custom_blue)
if need_blue_count > len(available_blue):
messagebox.showerror("错误", "可用的蓝球号码不足!")
return # 直接返回,不修改当前显示的号码
selected_blue_list = custom_blue + random.sample(available_blue, need_blue_count)
selected_blue_list.sort()
blue_balls = selected_blue_list
# 存储当前注的号码
temp_bets.append((selected_red.copy(), blue_balls[0] if self.bet_type.get() == "single" else blue_balls.copy()))
# 记录已生成的号码
if not is_regen:
self.generated_reds.clear()
self.generated_blues.clear()
self.generated_reds.update(selected_red)
self.generated_blues.update(blue_balls)
# 检查是否中过大奖
self.check_prize_history(selected_red, blue_balls)
# 如果所有注都成功生成,才更新显示
self.current_bets = temp_bets
# 清空之前的显示
self.prompt_label.pack_forget()
self.bets_display_frame.pack_forget()
self.export_button_frame.pack_forget()
# 清空显示区域
for widget in self.bets_display_frame.winfo_children():
widget.destroy()
for widget in self.export_button_frame.winfo_children():
widget.destroy()
# 显示新生成的号码
for bet_index, (selected_red, blue_balls) in enumerate(self.current_bets):
# 创建一行显示区域
bet_row_frame = tk.Frame(self.bets_display_frame, bg='#f0f0f0')
bet_row_frame.pack(fill="x", pady=2)
# 添加注号标签
bet_label = tk.Label(bet_row_frame, text=f"第{bet_index+1}注:", font=("Arial", 10), bg='#f0f0f0')
bet_label.pack(side="left", padx=(0, 10))
# 创建红球显示区域
red_balls_frame = tk.Frame(bet_row_frame, bg='#f0f0f0')
red_balls_frame.pack(side="left")
# 显示红球
for i in range(6):
canvas = self.create_circle_ball(red_balls_frame, selected_red[i], "red", diameter=30)
canvas.pack(side="left", padx=1)
# 添加分隔符
separator = tk.Label(bet_row_frame, text="+", font=("Arial", 10), bg='#f0f0f0')
separator.pack(side="left", padx=5)
# 蓝球显示区域
blue_balls_frame = tk.Frame(bet_row_frame, bg='#f0f0f0')
blue_balls_frame.pack(side="left")
# 显示蓝球
if isinstance(blue_balls, list):
for blue_ball in blue_balls:
canvas = self.create_circle_ball(blue_balls_frame, blue_ball, "blue", diameter=30)
canvas.pack(side="left", padx=1)
else:
canvas = self.create_circle_ball(blue_balls_frame, blue_balls, "blue", diameter=30)
canvas.pack(side="left", padx=1)
# 显示号码区域和导出按钮
self.bets_display_frame.pack(fill="x", padx=10, pady=10)
# 添加导出按钮
export_button = ttk.Button(self.export_button_frame, text="导出号码", command=self.save_to_excel)
export_button.pack(pady=10)
self.export_button_frame.pack(fill="x")
# 动态调整窗口大小
#self.adjust_window_size(bet_count)
# 确保内容填满Canvas区域
self.canvas.update_idletasks()
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
self.status_var.set(f"成功生成 {bet_count} 注号码!")
except Exception as e:
messagebox.showerror("错误", f"生成号码时出现错误: {str(e)}")
self.status_var.set("生成失败")
# def adjust_window_size(self, bet_count):
# """根据注数调整窗口大小"""
# # 注数大于7时调整窗口大小为700x900,否则保持700x600
# if bet_count > 7:
# self.root.geometry("700x900")
# else:
# self.root.geometry("700x600")
def check_prize_history(self, red_balls, blue_balls):
"""检查是否在历史数据中已经中过奖"""
# 对于复式投注,检查所有蓝球组合
for blue_ball in blue_balls:
for history_red, history_blue in self.history_data:
if (set(red_balls) == set(history_red) and blue_ball == history_blue):
result = messagebox.askyesno(
"提示",
f"这个号码组合(红球: {', '.join(map(str, red_balls))}, 蓝球: {blue_ball})在历史开奖中已经中过大奖!\n是否重新生成新的号码?"
)
if result:
self.generate_numbers(is_regen=True)
return
def main():
root = tk.Tk()
app = DoubleColorBallGenerator(root)
root.mainloop()
if __name__ == "__main__":
main()
1万+

被折叠的 条评论
为什么被折叠?



