代码解释
1.导入模块
import tkinter as tk
from tkinter import ttk, messagebox
import os, winsound, datetime, time, pytz, requests
from requests.exceptions import RequestException
- tkinter:Python标准库中的GUI库
- ttk:
tkinter
的主题部件,提供了更现代的外观 - messagebox:用于显示消息框
- os:与操作系统交互的模块
- winsound:用于播放Windows系统的声音
- datetime、time:处理日期和时间
- pytz:用于处理时区
- requests:用于发送HTTP请求
- RequestException:
requests
库中的异常处理
2.主类 ClockApp
class ClockApp:
def __init__(self, master):
self.master = master
self.master.title("多功能时钟")
self.master.geometry("600x400")
self.button_frame = ttk.Frame(self.master)
self.button_frame.pack(side="left", fill="y", padx=20, pady=20)
self.main_frame = ttk.Frame(self.master)
self.main_frame.pack(side="right", fill="both", expand=True, padx=20, pady=20)
self.create_buttons()
- master:主窗口
button_frame
和 main_frame
:用于放置按钮和主要内容的框架create_buttons()
:创建功能按钮(计时器、闹钟、秒表、世界时钟)
3.创建功能按钮
def create_buttons(self):
buttons = [("计时器", self.show_timer), ("闹钟", self.show_alarm), ("秒表", self.show_stopwatch), ("世界时钟", self.show_world_clock)]
for text, command in buttons:
btn = ttk.Button(self.button_frame, text=text, command=command, width=20)
btn.pack(pady=10, anchor="w")
- 功能按钮:每个按钮对应一个功能,当点击时触发相应的方法
4.计时器功能
def start_timer(self):
try:
hours = int(self.hours_entry.get() or 0)
minutes = int(self.minutes_entry.get() or 0)
seconds = int(self.seconds_entry.get() or 0)
if hours < 0 or minutes < 0 or seconds < 0 or minutes > 59 or seconds > 59:
raise ValueError
total_seconds = hours * 3600 + minutes * 60 + seconds
if total_seconds == 0:
raise ValueError
except ValueError:
messagebox.showerror("错误", "请输入有效的时间")
return
self.clear_main_frame()
self.timer_label = ttk.Label(self.main_frame, text=f"{hours:02d}:{minutes:02d}:{seconds:02d}", font=("Helvetica", 48))
self.timer_label.pack(anchor="n", pady=30)
self.stop_timer_flag = False
self.count_down(total_seconds)
restart_button = ttk.Button(self.main_frame, text="重新开始", command=self.show_timer)
restart_button.pack(pady=10)
stop_button = ttk.Button(self.main_frame, text="停止", command=self.stop_timer)
stop_button.pack(pady=10)
def count_down(self, remaining_seconds):
if remaining_seconds > 0 and not self.stop_timer_flag:
h, m, s = remaining_seconds // 3600, (remaining_seconds % 3600) // 60, remaining_seconds % 60
self.timer_label.config(text=f"{h:02d}:{m:02d}:{s:02d}")
self.master.after(1000, self.count_down, remaining_seconds - 1)
elif not self.stop_timer_flag:
self.timer_label.config(text="时间到!")
if self.play_sound_var.get():
self.play_reminder_sound()
def stop_timer(self):
self.stop_timer_flag = True
self.timer_label.config(text="计时已停止")
show_timer()
:显示计时器的界面,用户可以输入小时、分钟、秒数,并选择是否播放提醒音start_timer()
:根据输入的时间开始倒计时,调用count_down()
方法count_down()
:实现倒计时,每秒更新一次显示stop_timer()
:停止计时
5.闹钟功能
def show_alarm(self):
self.clear_main_frame()
label = ttk.Label(self.main_frame, text="输入时间,点击下方按钮设置闹钟")
label.pack(pady=10)
time_frame = ttk.Frame(self.main_frame)
time_frame.pack(pady=10)
self.hour_entry = ttk.Entry(time_frame, width=3)
self.hour_entry.pack(side="left")
ttk.Label(time_frame, text=":").pack(side="left")
self.minute_entry = ttk.Entry(time_frame, width=3)
self.minute_entry.pack(side="left")
ttk.Label(time_frame, text=":").pack(side="left")
self.second_entry = ttk.Entry(time_frame, width=3)
self.second_entry.pack(side="left")
start_button = ttk.Button(self.main_frame, text="设置闹钟", command=self.set_alarm)
start_button.pack(pady=10)
self.alarm_status = ttk.Label(self.main_frame, text="")
self.alarm_status.pack(pady=10)
def set_alarm(self):
try:
hour = int(self.hour_entry.get())
minute = int(self.minute_entry.get())
second = int(self.second_entry.get())
if not (0 <= hour < 24 and 0 <= minute < 60 and 0 <= second < 60):
raise ValueError
alarm_time = datetime.time(hour, minute, second)
current_time = datetime.datetime.now().time()
time_diff = datetime.datetime.combine(datetime.date.today(), alarm_time) - datetime.datetime.combine(datetime.date.today(), current_time)
seconds_until_alarm = time_diff.total_seconds()
if seconds_until_alarm < 0:
seconds_until_alarm += 86400
self.alarm_status.config(text=f"闹钟设置为 {alarm_time.strftime('%H:%M:%S')}")
self.master.after(int(seconds_until_alarm * 1000), self.play_alarm)
except ValueError:
messagebox.showerror("错误", "请输入有效的时间(时:分:秒)")
def play_alarm(self):
remind_sound_path = os.path.join(os.getcwd(), "remind.wav")
if os.path.exists(remind_sound_path):
winsound.PlaySound(remind_sound_path, winsound.SND_FILENAME)
self.alarm_status.config(text="闹钟响铃!")
else:
messagebox.showerror("错误", "找不到提醒音频文件")
show_alarm()
:显示设置闹钟的界面,用户可以输入时间set_alarm()
:设置闹钟时间,计算从当前时间到闹钟时间的间隔,并在到达时触发play_alarm()
play_alarm()
:播放闹钟提醒音
6.秒表功能
def show_stopwatch(self):
self.clear_main_frame()
self.stopwatch = Stopwatch(self.main_frame)
show_stopwatch()
:显示秒表界面,使用Stopwatch
类来管理秒表
7.世界时钟功能
def show_world_clock(self):
self.clear_main_frame()
location, user_timezone = self.get_user_location()
self.clock_label = ttk.Label(self.main_frame, font=('Helvetica', 48))
self.clock_label.pack(pady=20)
location_label = ttk.Label(self.main_frame, text=f"当前位置:{location}", font=('Helvetica', 16))
location_label.pack(pady=10)
self.timezone_label = ttk.Label(self.main_frame, text=f"时区:{user_timezone}", font=('Helvetica', 14))
self.timezone_label.pack(pady=5)
self.timezone_var = tk.StringVar(value=user_timezone)
timezone_combo = ttk.Combobox(self.main_frame, textvariable=self.timezone_var, values=pytz.all_timezones)
timezone_combo.pack(pady=10)
timezone_combo.bind('<<ComboboxSelected>>', self.change_timezone)
self.update_world_clock()
def update_world_clock(self):
try:
timezone = pytz.timezone(self.timezone_var.get())
current_time = datetime.datetime.now(timezone)
time_str = current_time.strftime("%H:%M:%S")
self.clock_label.config(text=time_str)
except pytz.exceptions.UnknownTimeZoneError:
self.clock_label.config(text="无效时区")
self.master.after(1000, self.update_world_clock)
def change_timezone(self, event):
timezone = self.timezone_var.get()
self.timezone_label.config(text=f"时区:{timezone}")
def get_user_location(self):
try:
response = requests.get('https://ipapi.co/json/', timeout=5)
response.raise_for_status()
data = response.json()
country = data.get('country_name', 'Unknown')
if country == 'China':
return '中国', 'Asia/Shanghai'
else:
city = data.get('city', 'Unknown')
timezone = data.get('timezone', 'UTC')
return f"{city}, {country}", timezone
except (RequestException, ValueError) as e:
print(f"无法获取位置信息: {e}")
return 'Unknown', 'Asia/Shanghai'
show_world_clock()
:显示世界时钟界面,显示当前时区的时间update_world_clock()
:每秒更新一次显示的时间change_timezone()
:允许用户更改时区get_user_location()
:通过IP地址获取用户的地理位置和时区
8.秒表类 Stopwatch
class Stopwatch:
def __init__(self, master):
self.master = master
self.running = False
self.start_time = 0
self.elapsed_time = 0
self.time_label = ttk.Label(master, text="00:00:00.000", font=("Helvetica", 36))
self.time_label.pack(pady=20)
button_frame = ttk.Frame(master)
button_frame.pack(pady=10)
self.start_button = ttk.Button(button_frame, text="开始", command=self.start_stop)
self.start_button.pack(side=tk.LEFT, padx=5)
self.reset_button = ttk.Button(button_frame, text="重置", command=self.reset)
self.reset_button.pack(side=tk.LEFT, padx=5)
self.lap_button = ttk.Button(button_frame, text="分段", command=self.lap, state=tk.DISABLED)
self.lap_button.pack(side=tk.LEFT, padx=5)
self.lap_frame = ttk.Frame(master)
self.lap_frame.pack(pady=10, fill=tk.BOTH, expand=True)
self.lap_listbox = tk.Listbox(self.lap_frame, width=30)
self.lap_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar = ttk.Scrollbar(self.lap_frame, orient=tk.VERTICAL, command=self.lap_listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.lap_listbox.config(yscrollcommand=scrollbar.set)
self.lap_count = 1
def start_stop(self):
if not self.running:
self.running = True
self.start_time = time.time() - self.elapsed_time
self.update_time()
self.start_button.config(text="暂停")
self.lap_button.config(state=tk.NORMAL)
else:
self.running = False
self.start_button.config(text="开始")
self.lap_button.config(state=tk.DISABLED)
def reset(self):
self.running = False
self.elapsed_time = 0
self.start_button.config(text="开始")
self.lap_button.config(state=tk.DISABLED)
self.time_label.config(text="00:00:00.000")
self.lap_listbox.delete(0, tk.END)
self.lap_count = 1
def update_time(self):
if self.running:
self.elapsed_time = time.time() - self.start_time
self.display_time()
self.master.after(10, self.update_time)
def display_time(self):
mins, secs = divmod(int(self.elapsed_time), 60)
hours, mins = divmod(mins, 60)
milliseconds = int((self.elapsed_time - int(self.elapsed_time)) * 1000)
self.time_label.config(text=f"{hours:02d}:{mins:02d}:{secs:02d}.{milliseconds:03d}")
def lap(self):
lap_time = self.time_label.cget("text")
self.lap_listbox.insert(tk.END, f"分段{self.lap_count}: {lap_time}")
self.lap_listbox.see(tk.END)
self.lap_count += 1
Stopwatch
类管理秒表的启动、停止、重置和分段计时功能
9.启动应用程序
def main():
root = tk.Tk()
app = ClockApp(root)
root.mainloop()
10.应用程序入口
if __name__ == "__main__":
main()
全部代码
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import os
import winsound
import datetime
import time
import pytz
import requests
from requests.exceptions import RequestException
class ClockApp:
def __init__(self, master):
self.master = master
self.master.title("多功能时钟")
self.master.geometry("600x400")
self.button_frame = ttk.Frame(self.master)
self.button_frame.pack(side="left", fill="y", padx=20, pady=20)
self.main_frame = ttk.Frame(self.master)
self.main_frame.pack(side="right", fill="both", expand=True, padx=20, pady=20)
self.create_buttons()
def create_buttons(self):
buttons = [
("计时器", self.show_timer),
("闹钟", self.show_alarm),
("秒表", self.show_stopwatch),
("世界时钟", self.show_world_clock)
]
for text, command in buttons:
btn = ttk.Button(self.button_frame, text=text, command=command, width=20)
btn.pack(pady=10, anchor="w")
def clear_main_frame(self):
for widget in self.main_frame.winfo_children():
widget.destroy()
def show_timer(self):
self.clear_main_frame()
label = ttk.Label(self.main_frame, text="输入时间,格式:小时:分钟:秒")
label.pack(pady=10)
time_frame = ttk.Frame(self.main_frame)
time_frame.pack(pady=10)
self.hours_entry = ttk.Entry(time_frame, width=3)
self.hours_entry.pack(side="left")
ttk.Label(time_frame, text=":").pack(side="left")
self.minutes_entry = ttk.Entry(time_frame, width=3)
self.minutes_entry.pack(side="left")
ttk.Label(time_frame, text=":").pack(side="left")
self.seconds_entry = ttk.Entry(time_frame, width=3)
self.seconds_entry.pack(side="left")
self.play_sound_var = tk.BooleanVar(value=False)
sound_checkbox = ttk.Checkbutton(self.main_frame, text="倒计时结束后播放提醒音", variable=self.play_sound_var)
sound_checkbox.pack(pady=10)
start_button = ttk.Button(self.main_frame, text="开始计时", command=self.start_timer)
start_button.pack(pady=10)
def start_timer(self):
try:
hours = int(self.hours_entry.get() or 0)
minutes = int(self.minutes_entry.get() or 0)
seconds = int(self.seconds_entry.get() or 0)
if hours < 0 or minutes < 0 or seconds < 0 or minutes > 59 or seconds > 59:
raise ValueError
total_seconds = hours * 3600 + minutes * 60 + seconds
if total_seconds == 0:
raise ValueError
except ValueError:
messagebox.showerror("错误", "请输入有效的时间")
return
self.clear_main_frame()
self.timer_label = ttk.Label(self.main_frame, text=f"{hours:02d}:{minutes:02d}:{seconds:02d}", font=("Helvetica", 48))
self.timer_label.pack(anchor="n", pady=30)
self.stop_timer_flag = False
self.count_down(total_seconds)
restart_button = ttk.Button(self.main_frame, text="重新开始", command=self.show_timer)
restart_button.pack(pady=10)
stop_button = ttk.Button(self.main_frame, text="停止", command=self.stop_timer)
stop_button.pack(pady=10)
def count_down(self, remaining_seconds):
if remaining_seconds > 0 and not self.stop_timer_flag:
h, m, s = remaining_seconds // 3600, (remaining_seconds % 3600) // 60, remaining_seconds % 60
self.timer_label.config(text=f"{h:02d}:{m:02d}:{s:02d}")
self.master.after(1000, self.count_down, remaining_seconds - 1)
elif not self.stop_timer_flag:
self.timer_label.config(text="时间到!")
if self.play_sound_var.get():
self.play_reminder_sound()
def stop_timer(self):
self.stop_timer_flag = True
self.timer_label.config(text="计时已停止")
def play_reminder_sound(self):
remind_sound_path = os.path.join(os.getcwd(), "remind.wav")
if os.path.exists(remind_sound_path):
winsound.PlaySound(remind_sound_path, winsound.SND_FILENAME)
else:
messagebox.showerror("错误", "找不到提醒音频文件")
def show_alarm(self):
self.clear_main_frame()
label = ttk.Label(self.main_frame, text="输入时间,点击下方按钮设置闹钟")
label.pack(pady=10)
time_frame = ttk.Frame(self.main_frame)
time_frame.pack(pady=10)
self.hour_entry = ttk.Entry(time_frame, width=3)
self.hour_entry.pack(side="left")
ttk.Label(time_frame, text=":").pack(side="left")
self.minute_entry = ttk.Entry(time_frame, width=3)
self.minute_entry.pack(side="left")
ttk.Label(time_frame, text=":").pack(side="left")
self.second_entry = ttk.Entry(time_frame, width=3)
self.second_entry.pack(side="left")
start_button = ttk.Button(self.main_frame, text="设置闹钟", command=self.set_alarm)
start_button.pack(pady=10)
self.alarm_status = ttk.Label(self.main_frame, text="")
self.alarm_status.pack(pady=10)
def set_alarm(self):
try:
hour = int(self.hour_entry.get())
minute = int(self.minute_entry.get())
second = int(self.second_entry.get())
if not (0 <= hour < 24 and 0 <= minute < 60 and 0 <= second < 60):
raise ValueError
alarm_time = datetime.time(hour, minute, second)
current_time = datetime.datetime.now().time()
time_diff = datetime.datetime.combine(datetime.date.today(), alarm_time) - datetime.datetime.combine(datetime.date.today(), current_time)
seconds_until_alarm = time_diff.total_seconds()
if seconds_until_alarm < 0:
seconds_until_alarm += 86400
self.alarm_status.config(text=f"闹钟设置为 {alarm_time.strftime('%H:%M:%S')}")
self.master.after(int(seconds_until_alarm * 1000), self.play_alarm)
except ValueError:
messagebox.showerror("错误", "请输入有效的时间(时:分:秒)")
def play_alarm(self):
remind_sound_path = os.path.join(os.getcwd(), "remind.wav")
if os.path.exists(remind_sound_path):
winsound.PlaySound(remind_sound_path, winsound.SND_FILENAME)
self.alarm_status.config(text="闹钟响铃!")
else:
messagebox.showerror("错误", "找不到提醒音频文件")
def show_stopwatch(self):
self.clear_main_frame()
self.stopwatch = Stopwatch(self.main_frame)
def get_user_location(self):
try:
response = requests.get('https://ipapi.co/json/', timeout=5)
response.raise_for_status()
data = response.json()
country = data.get('country_name', 'Unknown')
if country == 'China':
return '中国', 'Asia/Shanghai'
else:
city = data.get('city', 'Unknown')
timezone = data.get('timezone', 'UTC')
return f"{city}, {country}", timezone
except (RequestException, ValueError) as e:
print(f"无法获取位置信息: {e}")
return 'Unknown', 'Asia/Shanghai'
def show_world_clock(self):
self.clear_main_frame()
location, user_timezone = self.get_user_location()
self.clock_label = ttk.Label(self.main_frame, font=('Helvetica', 48))
self.clock_label.pack(pady=20)
location_label = ttk.Label(self.main_frame, text=f"当前位置:{location}", font=('Helvetica', 16))
location_label.pack(pady=10)
self.timezone_label = ttk.Label(self.main_frame, text=f"时区:{user_timezone}", font=('Helvetica', 14))
self.timezone_label.pack(pady=5)
self.timezone_var = tk.StringVar(value=user_timezone)
timezone_combo = ttk.Combobox(self.main_frame, textvariable=self.timezone_var, values=pytz.all_timezones)
timezone_combo.pack(pady=10)
timezone_combo.bind('<<ComboboxSelected>>', self.change_timezone)
self.update_world_clock()
def update_world_clock(self):
try:
timezone = pytz.timezone(self.timezone_var.get())
current_time = datetime.datetime.now(timezone)
time_str = current_time.strftime("%H:%M:%S")
self.clock_label.config(text=time_str)
except pytz.exceptions.UnknownTimeZoneError:
self.clock_label.config(text="无效时区")
self.master.after(1000, self.update_world_clock)
def change_timezone(self, event):
timezone = self.timezone_var.get()
self.timezone_label.config(text=f"时区:{timezone}")
class Stopwatch:
def __init__(self, master):
self.master = master
self.running = False
self.start_time = 0
self.elapsed_time = 0
self.time_label = ttk.Label(master, text="00:00:00.000", font=("Helvetica", 36))
self.time_label.pack(pady=20)
button_frame = ttk.Frame(master)
button_frame.pack(pady=10)
self.start_button = ttk.Button(button_frame, text="开始", command=self.start_stop)
self.start_button.pack(side=tk.LEFT, padx=5)
self.reset_button = ttk.Button(button_frame, text="重置", command=self.reset)
self.reset_button.pack(side=tk.LEFT, padx=5)
self.lap_button = ttk.Button(button_frame, text="分段", command=self.lap, state=tk.DISABLED)
self.lap_button.pack(side=tk.LEFT, padx=5)
self.lap_frame = ttk.Frame(master)
self.lap_frame.pack(pady=10, fill=tk.BOTH, expand=True)
self.lap_listbox = tk.Listbox(self.lap_frame, width=30)
self.lap_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar = ttk.Scrollbar(self.lap_frame, orient=tk.VERTICAL, command=self.lap_listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.lap_listbox.config(yscrollcommand=scrollbar.set)
self.lap_count = 1
def start_stop(self):
if not self.running:
self.running = True
self.start_time = time.time() - self.elapsed_time
self.update_time()
self.start_button.config(text="暂停")
self.lap_button.config(state=tk.NORMAL)
else:
self.running = False
self.start_button.config(text="开始")
self.lap_button.config(state=tk.DISABLED)
def reset(self):
self.running = False
self.elapsed_time = 0
self.start_button.config(text="开始")
self.lap_button.config(state=tk.DISABLED)
self.time_label.config(text="00:00:00.000")
self.lap_listbox.delete(0, tk.END)
self.lap_count = 1
def update_time(self):
if self.running:
self.elapsed_time = time.time() - self.start_time
self.display_time()
self.master.after(10, self.update_time)
def display_time(self):
mins, secs = divmod(int(self.elapsed_time), 60)
hours, mins = divmod(mins, 60)
milliseconds = int((self.elapsed_time - int(self.elapsed_time)) * 1000)
self.time_label.config(text=f"{hours:02d}:{mins:02d}:{secs:02d}.{milliseconds:03d}")
def lap(self):
lap_time = self.time_label.cget("text")
self.lap_listbox.insert(tk.END, f"分段{self.lap_count}: {lap_time}")
self.lap_listbox.see(tk.END)
self.lap_count += 1
def main():
root = tk.Tk()
app = ClockApp(root)
root.mainloop()
if __name__ == "__main__":
main()