我用AI搓了一个没什么卵用的计时器
大概就是这样,选择开始时间和结束时间,这两个时间必须在现在日期的前后,第一次使用的时候要注意保存,点击“另存为”保存配置文件,在下一次进入的时候就可以直接运行了。还要注意,在运行前要在电脑终端下载依赖库
pip install tkcalendar
这是一个源文件地址,下载即用:https://47.122.22.218:29439/down/OHRRXX8MilE9.py
源码
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from datetime import datetime, timedelta
from tkcalendar import Calendar
import json
import os
import sys
import atexit
class UltimateTimeProgressApp:
def __init__(self):
# 初始化主窗口
self.root = tk.Tk()
self.root.title("时间进度大江浩版")
self.root.geometry("900x450")
self.root.minsize(800, 400)
# 样式初始化
self.setup_styles()
# 核心变量
self.preset_var = tk.StringVar(value="month")
self.is_running = False
self.last_config_path = None
# 构建界面
self.setup_main_frame()
self.setup_sidebar()
self.setup_main_content()
# 文件管理
self.app_data_path = self.get_app_data_dir()
os.makedirs(self.app_data_path, exist_ok=True)
self.load_last_config()
# 注册退出处理
atexit.register(self.cleanup_resources)
self.root.protocol("WM_DELETE_WINDOW", self.graceful_exit)
self.root.mainloop()
def get_app_data_dir(self):
"""获取跨平台应用数据目录"""
if sys.platform == 'win32':
return os.path.join(os.environ['APPDATA'], 'TimeProgress')
return os.path.join(os.path.expanduser('~'), '.config', 'TimeProgress')
def setup_styles(self):
"""现代化样式配置"""
style = ttk.Style()
style.theme_use('clam')
style.configure('TFrame', background='#F5F5F5')
style.configure('TLabel', background='#F5F5F5', foreground='#333333')
style.configure('TButton', font=('Segoe UI', 10), padding=6)
style.configure('Title.TLabel', font=('Segoe UI', 12, 'bold'))
style.configure('Progress.Horizontal.TProgressbar',
troughcolor='#EEEEEE',
background='#4A90E2',
thickness=25)
def setup_main_frame(self):
"""主框架布局"""
self.main_frame = ttk.Frame(self.root)
self.main_frame.pack(fill=tk.BOTH, expand=True)
def setup_sidebar(self):
"""左侧导航面板"""
sidebar = ttk.Frame(self.main_frame, width=220)
sidebar.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10)
ttk.Label(sidebar, text="时间预设", style='Title.TLabel').pack(pady=15)
presets = [
('本周', 'week'),
('本月', 'month'),
('本季度', 'quarter'),
('本年', 'year'),
('自定义', 'custom')
]
for text, val in presets:
rb = ttk.Radiobutton(sidebar, text=text, variable=self.preset_var,
value=val, command=self.update_preset)
rb.pack(anchor=tk.W, pady=3, padx=5)
def setup_main_content(self):
"""主内容区域"""
content = ttk.Frame(self.main_frame)
content.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)
# 时间选择区域
time_frame = ttk.Frame(content)
time_frame.pack(fill=tk.X, pady=15)
self.start_entry = self.create_date_picker(time_frame, "开始时间", 'start')
self.end_entry = self.create_date_picker(time_frame, "结束时间", 'end')
# 控制按钮
btn_frame = ttk.Frame(content)
btn_frame.pack(fill=tk.X, pady=15)
ttk.Button(btn_frame, text="▶ 启动/暂停", command=self.toggle_progress).pack(side=tk.LEFT)
ttk.Button(btn_frame, text="📂 打开配置", command=self.load_config_dialog).pack(side=tk.LEFT, padx=10)
ttk.Button(btn_frame, text="💾 另存为", command=self.save_config_dialog).pack(side=tk.LEFT)
# 进度显示
self.progress = ttk.Progressbar(content, style='Progress.Horizontal.TProgressbar')
self.progress.pack(fill=tk.X, pady=20)
# 状态信息
status_frame = ttk.Frame(content)
status_frame.pack(fill=tk.X)
self.percent_var = tk.StringVar(value="0.000000%")
ttk.Label(status_frame, textvariable=self.percent_var, font=('Consolas', 14)).pack(side=tk.LEFT)
self.time_remaining_var = tk.StringVar(value="准备就绪")
ttk.Label(status_frame, textvariable=self.time_remaining_var).pack(side=tk.RIGHT)
def create_date_picker(self, parent, label_text, entry_type):
"""创建日期选择组件"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.X, pady=8)
ttk.Label(frame, text=label_text, width=8).pack(side=tk.LEFT)
entry = ttk.Entry(frame, width=24)
entry.pack(side=tk.LEFT, padx=5)
ttk.Button(frame, text="📅 选择",
command=lambda: self.show_calendar(entry_type)).pack(side=tk.LEFT)
return entry
def show_calendar(self, entry_type):
"""显示日历选择窗口"""
top = tk.Toplevel(self.root)
top.grab_set()
top.title("选择日期")
cal = Calendar(top, selectmode='day', date_pattern='yyyy-mm-dd')
cal.pack(padx=20, pady=20)
ttk.Button(top, text="确认",
command=lambda: self.update_entry(entry_type, cal, top)).pack(pady=10)
def update_entry(self, entry_type, calendar, window):
"""更新输入框内容"""
date_str = calendar.get_date()
time_part = "00:00:00" if entry_type == 'start' else "23:59:59"
full_date = f"{date_str} {time_part}"
target_entry = self.start_entry if entry_type == 'start' else self.end_entry
target_entry.delete(0, tk.END)
target_entry.insert(0, full_date)
self.preset_var.set('custom')
window.destroy()
def update_preset(self):
"""处理预设选项变更"""
preset = self.preset_var.get()
now = datetime.now()
if preset == 'week':
start = now - timedelta(days=now.weekday())
elif preset == 'month':
start = now.replace(day=1)
elif preset == 'quarter':
quarter_month = ((now.month - 1) // 3) * 3 + 1
start = now.replace(month=quarter_month, day=1)
elif preset == 'year':
start = now.replace(month=1, day=1)
else:
return
self.start_entry.delete(0, tk.END)
self.start_entry.insert(0, start.strftime("%Y-%m-%d 00:00:00"))
self.end_entry.delete(0, tk.END)
self.end_entry.insert(0, now.strftime("%Y-%m-%d 23:59:59"))
def toggle_progress(self):
"""切换监控状态"""
self.is_running = not self.is_running
if self.is_running:
self.start_progress()
else:
self.time_remaining_var.set("已暂停")
def start_progress(self):
"""开始进度计算"""
try:
start = datetime.strptime(self.start_entry.get(), "%Y-%m-%d %H:%M:%S")
end = datetime.strptime(self.end_entry.get(), "%Y-%m-%d %H:%M:%S")
if end <= start:
raise ValueError("结束时间必须晚于开始时间")
self.total_seconds = (end - start).total_seconds()
self.update_progress()
except Exception as e:
messagebox.showerror("配置错误", f"无效的时间设置:\n{str(e)}")
self.is_running = False
def update_progress(self):
"""更新进度显示"""
if not self.is_running:
return
current = datetime.now()
start = datetime.strptime(self.start_entry.get(), "%Y-%m-%d %H:%M:%S")
elapsed = (current - start).total_seconds()
if elapsed < 0:
self.percent_var.set("0.000000%")
self.time_remaining_var.set("未开始")
elif elapsed >= self.total_seconds:
self.percent_var.set("100.000000%")
self.time_remaining_var.set("已完成")
self.is_running = False
else:
percent = (elapsed / self.total_seconds) * 100
remaining = self.total_seconds - elapsed
self.progress['value'] = percent
self.percent_var.set(f"{percent:.6f}%")
self.time_remaining_var.set(self.format_timedelta(remaining))
if self.is_running:
self.root.after(1000, self.update_progress)
def format_timedelta(self, seconds):
"""格式化剩余时间"""
days, rem = divmod(seconds, 86400)
hours, rem = divmod(rem, 3600)
minutes, sec = divmod(rem, 60)
return f"剩余:{int(days)}天 {int(hours):02}:{int(minutes):02}:{int(sec):02}"
def save_config_dialog(self):
"""保存配置对话框"""
filepath = filedialog.asksaveasfilename(
title="保存配置文件",
defaultextension=".json",
filetypes=[("JSON 配置", "*.json"), ("所有文件", "*.*")],
initialdir=self.app_data_path,
initialfile="time_config.json"
)
if not filepath:
return
config = {
'start': self.start_entry.get(),
'end': self.end_entry.get(),
'preset': self.preset_var.get(),
'window_geometry': self.root.geometry(),
'saved_at': datetime.now().isoformat()
}
try:
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
self.last_config_path = filepath
messagebox.showinfo("保存成功", f"配置已保存到:\n{filepath}")
except Exception as e:
messagebox.showerror("保存失败", f"保存文件失败:\n{str(e)}")
def load_config_dialog(self):
"""加载配置对话框"""
filepath = filedialog.askopenfilename(
title="选择配置文件",
filetypes=[("JSON 配置", "*.json"), ("所有文件", "*.*")],
initialdir=self.app_data_path
)
if filepath:
self.load_config_file(filepath)
def load_config_file(self, filepath):
"""加载配置文件"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
config = json.load(f)
# 恢复窗口尺寸
if 'window_geometry' in config:
self.root.geometry(config['window_geometry'])
# 恢复时间设置
self.start_entry.delete(0, tk.END)
self.start_entry.insert(0, config.get('start', ''))
self.end_entry.delete(0, tk.END)
self.end_entry.insert(0, config.get('end', ''))
# 恢复预设状态
self.preset_var.set(config.get('preset', 'custom'))
self.last_config_path = filepath
messagebox.showinfo("加载成功", f"已加载配置:{os.path.basename(filepath)}")
except json.JSONDecodeError:
messagebox.showerror("配置错误", "文件格式无效,请选择正确的配置文件")
except Exception as e:
messagebox.showerror("加载失败", f"无法读取文件:\n{str(e)}")
def load_last_config(self):
"""自动加载上次配置"""
last_config = os.path.join(self.app_data_path, 'last_config.json')
if os.path.exists(last_config):
try:
with open(last_config, 'r') as f:
config = json.load(f)
if config.get('last_path'):
self.load_config_file(config['last_path'])
except:
self.set_default_times()
else:
self.set_default_times()
def set_default_times(self):
"""设置默认时间范围"""
now = datetime.now()
self.start_entry.insert(0, now.replace(hour=0, minute=0, second=0).strftime("%Y-%m-%d %H:%M:%S"))
self.end_entry.insert(0, now.replace(hour=23, minute=59, second=59).strftime("%Y-%m-%d %H:%M:%S"))
def graceful_exit(self):
"""优雅退出程序"""
if messagebox.askokcancel("退出", "确定要退出程序吗?"):
# 保存最后使用的配置路径
if self.last_config_path:
with open(os.path.join(self.app_data_path, 'last_config.json'), 'w') as f:
json.dump({'last_path': self.last_config_path}, f)
self.root.destroy()
def cleanup_resources(self):
"""清理系统资源"""
pass
if __name__ == "__main__":
try:
UltimateTimeProgressApp()
except Exception as e:
messagebox.showerror("启动失败", f"程序初始化失败:\n{str(e)}")