MrrrrrrrDou 发表于 2024-4-20 11:58

Python写的计时软件求助

本帖最后由 MrrrrrrrDou 于 2024-4-20 12:40 编辑

这是我写的一个计时的软件,但是目前有两个问题,
①我已经在 后面的def on_double_click(self, event):中写了当我双击时首先停止之前的计时         # 停止该行已有的倒计时
self.stop_countdown(selected_item),但是实际运行起来并不会,他会和前面的数据打架,什么原因呀?
②如果我关闭程序,他就不会计时了,我希望每次打开他能够判断我现在的时间和刷新时间的差值,然后倒计时,怎么做


下面是完整代码
# 导入所需的库
import tkinter as tk
from tkinter import ttk
import pandas as pd
import datetime
from pathlib import Path
from tkinter import messagebox
from datetime import timedelta
import ttkthemes
import threading
import time

# 创建一个名为Timer的类
class Timer:
    def __init__(self, master):
      # 初始化ThemedStyle
      self.master = master
      self.master.style = ttkthemes.ThemedStyle(master)
      self.master.style.set_theme("adapta")# 设置主题为"adapta"

      self.df = pd.DataFrame(columns=["位置","怪物名称", "倒计时", "刷新计时", "刷新(分钟)", "击杀时间", "备注信息"])      # 使用pandas创建一个DataFrame
      self.load_data()      # 加载数据
      self.tree = ttk.Treeview(self.master, columns=("位置","怪物名称", "倒计时", "刷新计时", "刷新(分钟)", "击杀时间", "备注信息"), show="headings")# 创建一个Treeview对象
      # 设置列宽
      self.tree.column("位置", width=150, anchor="w")
      self.tree.column("怪物名称", width=150, anchor="w")
      self.tree.column("倒计时", width=150, anchor="center")
      self.tree.column("刷新计时", width=150, anchor="center")
      self.tree.column("刷新(分钟)", width=150, anchor="center")
      self.tree.column("击杀时间", width=150, anchor="w")
      self.tree.column("备注信息", width=300, anchor="w")
      # 添加表头文字说明
      self.tree.heading("位置", text="位置")
      self.tree.heading("怪物名称", text="怪物名称")
      self.tree.heading("倒计时", text="倒计时")
      self.tree.heading("刷新计时", text="刷新计时")
      self.tree.heading("刷新(分钟)", text="刷新(分钟)")
      self.tree.heading("击杀时间", text="击杀时间")
      self.tree.heading("备注信息", text="备注信息")

      self.tree.pack()      # 将Treeview添加到窗口
      # 遍历DataFrame中的每一行,并将数据添加到Treeview中
      for index, row in self.df.iterrows():
            self.tree.insert("", "end", values=(row["位置"], row["怪物名称"], row["倒计时"], row["刷新计时"], row["刷新(分钟)"], row["击杀时间"], row["备注信息"]))


      # 添加“添加计时”、“删除计时”和“编辑”按钮
      add_button = tk.Button(self.master, text="添加计时", command=self.add_timer)
      add_button.pack(side=tk.LEFT, padx=5)

      edit_button = tk.Button(self.master, text="编辑计时", command=self.open_edit_dialog)
      edit_button.pack(side=tk.LEFT, padx=5)

      remove_button = tk.Button(self.master, text="删除计时", command=self.delete_timer)
      remove_button.pack(side=tk.LEFT, padx=5)

      self.selected_monster_name = None   # 存储选中行的怪物名称
      self.tree.bind("<ButtonRelease-1>", self.on_tree_select) # 为 Treeview 绑定单击事件
      self.tree.bind("<Double-1>", self.on_double_click) # 为 Treeview 绑定双击事件
      self.active_countdown_threads = {}# 存储当前正在运行的倒计时线程


    def load_data(self):
      data_file = Path('monster_timers.xlsx')
      if data_file.exists():
            self.df = pd.read_excel(data_file, engine='openpyxl', index_col=0, parse_dates=['刷新计时', '击杀时间'])

    def save_data(self):
      data_file = Path('monster_timers.xlsx')
      self.df.to_excel(data_file, index_label='ID')

    def on_tree_select(self, event):
      selected_item = self.tree.selection()
      item_values = self.tree.item(selected_item, "values")
      monster_name = item_values# 获取选中行的“怪物名称”列值

    def open_edit_dialog(self):
      selected_item = self.tree.selection()
      
      if selected_item:# 判断是否选中了树视图中的某一行
            self.edit_timer(self.tree.item(selected_item, "values"))# 传递选中行的怪物名称给edit_timer方法
      else:
            messagebox.showinfo("提示", "请先选择要编辑的计时记录。")# 显示提示信息,告知用户需先选中行

    def add_timer(self):
      add_dialog = tk.Toplevel(self.master)
      add_dialog.title("添加计时")

      monster_position_label = tk.Label(add_dialog, text="位置")
      monster_position_label.grid(row=0, column=0)
      monster_position_entry = tk.Entry(add_dialog)
      monster_position_entry.grid(row=0, column=1)

      monster_name_label = tk.Label(add_dialog, text="怪物名称")
      monster_name_label.grid(row=1, column=0)
      monster_name_entry = tk.Entry(add_dialog)
      monster_name_entry.grid(row=1, column=1)

      refresh_interval_label = tk.Label(add_dialog, text="刷新间隔")
      refresh_interval_label.grid(row=2, column=0)
      refresh_interval_entry = tk.Entry(add_dialog)
      refresh_interval_entry.grid(row=2, column=1)

      note_label = tk.Label(add_dialog, text="备注信息")
      note_label.grid(row=3, column=0)
      note_entry = tk.Entry(add_dialog)
      note_entry.grid(row=3, column=1)

      add_button = tk.Button(add_dialog, text="添加", command=lambda: self.add_row(monster_position_entry.get(),
                                                                                  monster_name_entry.get(),
                                                                                  int(refresh_interval_entry.get()),
                                                                                  note_entry.get()))
      add_button.grid(row=4, column=0)
      cancel_button = tk.Button(add_dialog, text="取消", command=add_dialog.destroy)
      cancel_button.grid(row=4, column=1)

    def add_row(self, monster_position, monster_name, refresh_interval, note):

      # 确保新添加的行不含空或全NaN列
      new_row = {
            "位置": monster_position,
            "怪物名称": monster_name,
            "倒计时": "",
            "刷新计时": "",
            "刷新(分钟)": refresh_interval,
            "击杀时间": " ",
            "备注信息": note
      }

      # 移除值为None、NaN或空字符串的键值对
      for key, value in list(new_row.items()):
            if pd.isnull(value) or value in (None, ""):
                del new_row

      # 创建新的数据行DataFrame,仅包含有效列
      new_df_row = pd.DataFrame(new_row, index=)

      # 检查新行DataFrame是否存在全NaN的列,并在必要时进行清理
      while True:
            has_all_nan_cols = False
            for col in new_df_row.columns:
                if new_df_row.isna().all():
                  new_df_row.drop(col, axis=1, inplace=True)
                  has_all_nan_cols = True
            if not has_all_nan_cols:
                break
      # 合并新行与现有数据集
      self.df = pd.concat(, ignore_index=True, join='outer')


      # 在 Treeview 中插入新行,使用格式化后的刷新计时值
      self.tree.insert("", "end", values=(
            new_row["位置"],
            new_row["怪物名称"],
            "",
            "",
            new_row["刷新(分钟)"],
            "",
            new_row["备注信息"]
      ))

      # 保存数据至文件
      self.save_data()

    def edit_timer(self, monster_name):
      selected_rows = self.df == monster_name].index.tolist()
      if selected_rows:
            selected_index = selected_rows
            selected_row = self.df.loc

            edit_dialog = tk.Toplevel(self.master)
            edit_dialog.title(f"编辑 {monster_name} 计时")

            monster_position_label = tk.Label(edit_dialog, text="位置")
            monster_position_label.grid(row=0, column=0)
            monster_position_entry = tk.Entry(edit_dialog, textvariable=tk.StringVar(value=selected_row["位置"]))
            monster_position_entry.grid(row=0, column=1)

            monster_name_label = tk.Label(edit_dialog, text="怪物名称")
            monster_name_label.grid(row=1, column=0)
            monster_name_entry = tk.Entry(edit_dialog, textvariable=tk.StringVar(value=selected_row["怪物名称"]))
            monster_name_entry.grid(row=1, column=1)

            refresh_interval_label = tk.Label(edit_dialog, text="刷新(分钟)")
            refresh_interval_label.grid(row=2, column=0)
            refresh_interval_entry = tk.Entry(edit_dialog, textvariable=tk.StringVar(value=selected_row["刷新(分钟)"]))
            refresh_interval_entry.grid(row=2, column=1)

            note_label = tk.Label(edit_dialog, text="备注信息")
            note_label.grid(row=3, column=0)
            note_entry = tk.Entry(edit_dialog, textvariable=tk.StringVar(value=selected_row["备注信息"]))
            note_entry.grid(row=3, column=1)

            update_button = tk.Button(edit_dialog, text="更新", command=lambda: self.update_row(selected_index,
                                                                                              monster_position_entry.get(),
                                                                                              monster_name_entry.get(),
                                                                                              refresh_interval_entry.get(),
                                                                                              note_entry.get()))
            update_button.grid(row=4, column=0)
            cancel_button = tk.Button(edit_dialog, text="取消", command=edit_dialog.destroy)
            cancel_button.grid(row=4, column=1)

    def update_row(self,index,monster_position,monster_name,refresh_interval, note):
      self.df.at = str(monster_position)
      self.df.at = str(monster_name)
      self.df.at = int(refresh_interval)
      self.df.at = str(note)
      self.tree.item(self.tree.selection(),values=(self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc))
      self.save_data()

    def delete_timer(self):
      selected_item = self.tree.selection()
      if selected_item:
            item = self.tree.item(selected_item)# 获取选中项目的详细信息
            item_values = item["values"]# 提取选中行的值
            monster_name = item_values# 获取选中行的“怪物名称”列值
            if monster_name:# 检查 monster_name 是否非空
                self.df = self.df != monster_name]# 删除对应行
                self.tree.delete(selected_item)# 从 Treeview 中移除该行

                # 添加以下缺失的代码:保存更新后的数据到文件
                self.save_data()

    def on_double_click(self, event):
      selected_item = self.tree.selection()

      if selected_item:
            # 获取选中行的索引
            selected_index = self.tree.index(selected_item)
            # 更改“倒计时”列的数据类型为 str
            self.df["倒计时"] = self.df["倒计时"].astype(str)         
            # 记录当前时刻作为击杀时间
            current_time = datetime.datetime.now()
            # 计算刷新计时(假设 self.df.loc 已经是整数)
            refresh_minutes = int(self.df.loc)
            refresh_delta = datetime.timedelta(minutes=refresh_minutes)
            refresh_time = current_time + refresh_delta
            # 计算剩余时间(以秒为单位)
            remaining_time = refresh_minutes * 60
            # 格式化击杀时间,仅显示到秒
            formatted_current_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
            # 格式化刷新计时,仅显示到秒
            formatted_refresh_time = refresh_time.strftime('%Y-%m-%d %H:%M:%S')
            # 更新 DataFrame 中的“刷新计时”列
            self.df.at = formatted_refresh_time
            # 更新 DataFrame 中的“击杀时间”列
            self.df.at = formatted_current_time
            # 停止该行已有的倒计时
            self.stop_countdown(selected_item)
            # 创建新的倒计时线程
            countdown_thread = threading.Thread(target=self.countdown, args=(selected_index, remaining_time, selected_item))
            countdown_thread.start()
            self.active_countdown_threads = countdown_thread
            # 更新 Treeview 显示的击杀时间
            self.tree.item(selected_item, values=(self.df.loc,
                                                self.df.loc,
                                                remaining_time,
                                                self.df.loc ,
                                                self.df.loc,
                                                self.df.loc,
                                                self.df.loc))
            # 保存更新后的数据到文件
            self.save_data()

    def countdown(self, selected_index, remaining_time, selected_item):
      def update_countdown(remaining_time):
            hours, remaining_seconds = divmod(remaining_time, 3600)# 将秒数转换为小时和剩余秒数
            minutes, seconds = divmod(remaining_seconds, 60)    # 将剩余秒数转换为分钟和秒数

            self.df.at = f"{hours:02}:{minutes:02}:{seconds:02}"# 以“小时:分钟:秒数”格式存储
            # 更新 Treeview 显示的击杀时间
            self.tree.item(selected_item, values=(self.df.loc,
                                                    self.df.loc,
                                                    f"{hours:02}:{minutes:02}:{seconds:02}",
                                                    self.df.loc ,
                                                    self.df.loc,
                                                    self.df.loc,
                                                    self.df.loc))

            if remaining_time > 0:
                # 递归调用 update_countdown 函数,间隔 1000 毫秒(1秒)
                self.master.after(1000, update_countdown, remaining_time - 1)
      update_countdown(remaining_time)

      # 倒计时结束后,移除该行的倒计时线程
      del self.active_countdown_threads

    def stop_countdown(self, selected_item):
      if selected_item in self.active_countdown_threads:
            thread = self.active_countdown_threads.pop(selected_item)
            thread.join()# 等待线程结束

if __name__ == "__main__":
    root = tk.Tk()
    timer = Timer(root)
    root.mainloop()

QvQsuipian 发表于 2024-4-20 13:49

关于停止之前的计时:你已经在 on_double_click 方法中尝试停止之前的计时,但出现问题的地方是在于如何识别和停止正确的计时线程。在 stop_countdown 方法中,你可以尝试根据选中的怪物名称来查找对应的计时线程,并停止它。

关于每次打开程序都计时:你可以在程序启动时,检查每个怪物的击杀时间和刷新时间,计算出剩余时间,然后启动一个新的倒计时线程。这样,即使关闭程序再打开,仍能保持计时状态。

# 导入所需的库
import tkinter as tk
from tkinter import ttk
import pandas as pd
import datetime
from pathlib import Path
from tkinter import messagebox
from datetime import timedelta
import threading
import time

# 创建一个名为Timer的类
class Timer:
    def __init__(self, master):
      ...
      # 其他初始化代码
      ...

      # 在初始化时启动计时线程
      self.start_timers()

    def start_timers(self):
      # 遍历每个怪物,计算剩余时间并启动计时线程
      for index, row in self.df.iterrows():
            if not pd.isnull(row["击杀时间"]) and not pd.isnull(row["刷新计时"]):
                kill_time = datetime.datetime.strptime(row["击杀时间"], '%Y-%m-%d %H:%M:%S')
                refresh_time = datetime.datetime.strptime(row["刷新计时"], '%Y-%m-%d %H:%M:%S')
                refresh_minutes = int(row["刷新(分钟)"])
                remaining_seconds = (kill_time + timedelta(minutes=refresh_minutes) - datetime.datetime.now()).total_seconds()
                if remaining_seconds > 0:
                  self.start_countdown(index, remaining_seconds)

    def start_countdown(self, selected_index, remaining_time):
      selected_item = self.tree.get_children()
      countdown_thread = threading.Thread(target=self.countdown, args=(selected_index, remaining_time, selected_item))
      countdown_thread.start()
      self.active_countdown_threads = countdown_thread

    def stop_countdown(self, selected_item):
      if selected_item in self.active_countdown_threads:
            thread = self.active_countdown_threads.pop(selected_item)
            thread.join()# 等待线程结束

    def on_double_click(self, event):
      ...
      # 其他代码不变

    def countdown(self, selected_index, remaining_time, selected_item):
      ...
      # 其他代码不变

if __name__ == "__main__":
    ...
    # 其他代码不变

MrrrrrrrDou 发表于 2024-4-20 14:50

QvQsuipian 发表于 2024-4-20 13:49
关于停止之前的计时:你已经在 on_double_click 方法中尝试停止之前的计时,但出现问题的地方是在于如何识 ...

你这是ai回复的吧,我自己问过AI好多遍了,解决不了才过来求助的

yks1985 发表于 2024-4-20 16:28

确实会,等待大佬修复代码

Yifan2007 发表于 2024-4-20 17:34

你试试能不能创建一个多线程,等待用户按键然后再杀进程,没写过python思路应该通用

FitContent 发表于 2024-4-20 17:52

回答第一个问题。



---



经过调试,发现每次双击时,这个字典 `self.active_countdown_threads` 永远是空的,正确情况下该字典应该包含上一次双击时所创建的 Thread 对象。




所以调用 `self.stop_countdown()` 时根本没有正确终止线程,因为上一次双击时所创建的 Thread 对象已经被删除了。

具体情况如下:




解决方法粗暴点,修改 `update_countdown` 的代码,直接休息 1s。

```python
if remaining_time > 0:
    # 递归调用 update_countdown 函数,间隔 1000 毫秒(1秒)
    time.sleep(1)
    return update_countdown(remaining_time - 1)
    # self.master.after(1000, update_countdown, remaining_time - 1)
else:
    # 倒计时结束后,移除该行的倒计时线程
    del self.active_countdown_threads
```



另外,取消线程不要使用 `.join()`,实际上它是在等待线程执行完,而不是立即结束。可以利用线程通信,让另一个线程自动结束。

MrrrrrrrDou 发表于 2024-4-20 18:17

FitContent 发表于 2024-4-20 17:52
回答第一个问题。




这样改完后,我的倒计时不会动了{:1_885:}

Loker 发表于 2024-4-20 18:20

简单弄了一下第一个问题,第二个问题没看懂
# 导入所需的库
import tkinter as tk
from tkinter import ttk
import pandas as pd
import datetime
from pathlib import Path
from tkinter import messagebox
from datetime import timedelta
import ttkthemes
import threading
import time


# 创建一个名为Timer的类
class Timer:
    def __init__(self, master):
      # 初始化ThemedStyle
      self.master = master
      self.master.style = ttkthemes.ThemedStyle(master)
      self.master.style.set_theme("adapta")# 设置主题为"adapta"

      self.df = pd.DataFrame(columns=["位置", "怪物名称", "倒计时", "刷新计时", "刷新(分钟)", "击杀时间",
                                        "备注信息"])# 使用pandas创建一个DataFrame
      self.load_data()# 加载数据
      self.tree = ttk.Treeview(self.master, columns=(
      "位置", "怪物名称", "倒计时", "刷新计时", "刷新(分钟)", "击杀时间", "备注信息"),
                                 show="headings")# 创建一个Treeview对象
      # 设置列宽
      self.tree.column("位置", width=150, anchor="w")
      self.tree.column("怪物名称", width=150, anchor="w")
      self.tree.column("倒计时", width=150, anchor="center")
      self.tree.column("刷新计时", width=150, anchor="center")
      self.tree.column("刷新(分钟)", width=150, anchor="center")
      self.tree.column("击杀时间", width=150, anchor="w")
      self.tree.column("备注信息", width=300, anchor="w")
      # 添加表头文字说明
      self.tree.heading("位置", text="位置")
      self.tree.heading("怪物名称", text="怪物名称")
      self.tree.heading("倒计时", text="倒计时")
      self.tree.heading("刷新计时", text="刷新计时")
      self.tree.heading("刷新(分钟)", text="刷新(分钟)")
      self.tree.heading("击杀时间", text="击杀时间")
      self.tree.heading("备注信息", text="备注信息")

      self.tree.pack()# 将Treeview添加到窗口
      # 遍历DataFrame中的每一行,并将数据添加到Treeview中
      for index, row in self.df.iterrows():
            self.tree.insert("", "end", values=(
            row["位置"], row["怪物名称"], row["倒计时"], row["刷新计时"], row["刷新(分钟)"], row["击杀时间"],
            row["备注信息"]))

      # 添加“添加计时”、“删除计时”和“编辑”按钮
      add_button = tk.Button(self.master, text="添加计时", command=self.add_timer)
      add_button.pack(side=tk.LEFT, padx=5)

      edit_button = tk.Button(self.master, text="编辑计时", command=self.open_edit_dialog)
      edit_button.pack(side=tk.LEFT, padx=5)

      remove_button = tk.Button(self.master, text="删除计时", command=self.delete_timer)
      remove_button.pack(side=tk.LEFT, padx=5)

      self.selected_monster_name = None# 存储选中行的怪物名称
      self.tree.bind("<ButtonRelease-1>", self.on_tree_select)# 为 Treeview 绑定单击事件
      self.tree.bind("<Double-1>", self.on_double_click)# 为 Treeview 绑定双击事件
      global active_countdown_threads# 存储当前正在运行的倒计时线程
      active_countdown_threads = {}

    def load_data(self):
      data_file = Path('monster_timers.xlsx')
      if data_file.exists():
            self.df = pd.read_excel(data_file, engine='openpyxl', index_col=0, parse_dates=['刷新计时', '击杀时间'])

    def save_data(self):
      data_file = Path('monster_timers.xlsx')
      self.df.to_excel(data_file, index_label='ID')

    def on_tree_select(self, event):
      selected_item = self.tree.selection()
      item_values = self.tree.item(selected_item, "values")
      monster_name = item_values# 获取选中行的“怪物名称”列值

    def open_edit_dialog(self):
      selected_item = self.tree.selection()

      if selected_item:# 判断是否选中了树视图中的某一行
            self.edit_timer(self.tree.item(selected_item, "values"))# 传递选中行的怪物名称给edit_timer方法
      else:
            messagebox.showinfo("提示", "请先选择要编辑的计时记录。")# 显示提示信息,告知用户需先选中行

    def add_timer(self):
      add_dialog = tk.Toplevel(self.master)
      add_dialog.title("添加计时")

      monster_position_label = tk.Label(add_dialog, text="位置")
      monster_position_label.grid(row=0, column=0)
      monster_position_entry = tk.Entry(add_dialog)
      monster_position_entry.grid(row=0, column=1)

      monster_name_label = tk.Label(add_dialog, text="怪物名称")
      monster_name_label.grid(row=1, column=0)
      monster_name_entry = tk.Entry(add_dialog)
      monster_name_entry.grid(row=1, column=1)

      refresh_interval_label = tk.Label(add_dialog, text="刷新间隔")
      refresh_interval_label.grid(row=2, column=0)
      refresh_interval_entry = tk.Entry(add_dialog)
      refresh_interval_entry.grid(row=2, column=1)

      note_label = tk.Label(add_dialog, text="备注信息")
      note_label.grid(row=3, column=0)
      note_entry = tk.Entry(add_dialog)
      note_entry.grid(row=3, column=1)

      add_button = tk.Button(add_dialog, text="添加", command=lambda: self.add_row(monster_position_entry.get(),
                                                                                     monster_name_entry.get(),
                                                                                     int(refresh_interval_entry.get()),
                                                                                     note_entry.get()))
      add_button.grid(row=4, column=0)
      cancel_button = tk.Button(add_dialog, text="取消", command=add_dialog.destroy)
      cancel_button.grid(row=4, column=1)

    def add_row(self, monster_position, monster_name, refresh_interval, note):

      # 确保新添加的行不含空或全NaN列
      new_row = {
            "位置": monster_position,
            "怪物名称": monster_name,
            "倒计时": "",
            "刷新计时": "",
            "刷新(分钟)": refresh_interval,
            "击杀时间": " ",
            "备注信息": note
      }

      # 移除值为None、NaN或空字符串的键值对
      for key, value in list(new_row.items()):
            if pd.isnull(value) or value in (None, ""):
                del new_row

      # 创建新的数据行DataFrame,仅包含有效列
      new_df_row = pd.DataFrame(new_row, index=)

      # 检查新行DataFrame是否存在全NaN的列,并在必要时进行清理
      while True:
            has_all_nan_cols = False
            for col in new_df_row.columns:
                if new_df_row.isna().all():
                  new_df_row.drop(col, axis=1, inplace=True)
                  has_all_nan_cols = True
            if not has_all_nan_cols:
                break
      # 合并新行与现有数据集
      self.df = pd.concat(, ignore_index=True, join='outer')

      # 在 Treeview 中插入新行,使用格式化后的刷新计时值
      self.tree.insert("", "end", values=(
            new_row["位置"],
            new_row["怪物名称"],
            "",
            "",
            new_row["刷新(分钟)"],
            "",
            new_row["备注信息"]
      ))

      # 保存数据至文件
      self.save_data()

    def edit_timer(self, monster_name):
      selected_rows = self.df == monster_name].index.tolist()
      if selected_rows:
            selected_index = selected_rows
            selected_row = self.df.loc

            edit_dialog = tk.Toplevel(self.master)
            edit_dialog.title(f"编辑 {monster_name} 计时")

            monster_position_label = tk.Label(edit_dialog, text="位置")
            monster_position_label.grid(row=0, column=0)
            monster_position_entry = tk.Entry(edit_dialog, textvariable=tk.StringVar(value=selected_row["位置"]))
            monster_position_entry.grid(row=0, column=1)

            monster_name_label = tk.Label(edit_dialog, text="怪物名称")
            monster_name_label.grid(row=1, column=0)
            monster_name_entry = tk.Entry(edit_dialog, textvariable=tk.StringVar(value=selected_row["怪物名称"]))
            monster_name_entry.grid(row=1, column=1)

            refresh_interval_label = tk.Label(edit_dialog, text="刷新(分钟)")
            refresh_interval_label.grid(row=2, column=0)
            refresh_interval_entry = tk.Entry(edit_dialog, textvariable=tk.StringVar(value=selected_row["刷新(分钟)"]))
            refresh_interval_entry.grid(row=2, column=1)

            note_label = tk.Label(edit_dialog, text="备注信息")
            note_label.grid(row=3, column=0)
            note_entry = tk.Entry(edit_dialog, textvariable=tk.StringVar(value=selected_row["备注信息"]))
            note_entry.grid(row=3, column=1)

            update_button = tk.Button(edit_dialog, text="更新", command=lambda: self.update_row(selected_index,
                                                                                                monster_position_entry.get(),
                                                                                                monster_name_entry.get(),
                                                                                                refresh_interval_entry.get(),
                                                                                                note_entry.get()))
            update_button.grid(row=4, column=0)
            cancel_button = tk.Button(edit_dialog, text="取消", command=edit_dialog.destroy)
            cancel_button.grid(row=4, column=1)

    def update_row(self, index, monster_position, monster_name, refresh_interval, note):
      self.df.at = str(monster_position)
      self.df.at = str(monster_name)
      self.df.at = int(refresh_interval)
      self.df.at = str(note)
      self.tree.item(self.tree.selection(), values=(self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc,
                                                         self.df.loc))
      self.save_data()

    def delete_timer(self):
      selected_item = self.tree.selection()
      if selected_item:
            item = self.tree.item(selected_item)# 获取选中项目的详细信息
            item_values = item["values"]# 提取选中行的值
            monster_name = item_values# 获取选中行的“怪物名称”列值
            if monster_name:# 检查 monster_name 是否非空
                self.df = self.df != monster_name]# 删除对应行
                self.tree.delete(selected_item)# 从 Treeview 中移除该行

                # 添加以下缺失的代码:保存更新后的数据到文件
                self.save_data()

    def on_double_click(self, event):
      global active_countdown_threads
      selected_item = self.tree.selection()

      if selected_item:
            # 获取选中行的索引
            selected_index = self.tree.index(selected_item)
            # 更改“倒计时”列的数据类型为 str
            self.df["倒计时"] = self.df["倒计时"].astype(str)
            # 记录当前时刻作为击杀时间
            current_time = datetime.datetime.now()
            # 计算刷新计时(假设 self.df.loc 已经是整数)
            refresh_minutes = int(self.df.loc)
            refresh_delta = datetime.timedelta(minutes=refresh_minutes)
            refresh_time = current_time + refresh_delta
            # 计算剩余时间(以秒为单位)
            remaining_time = refresh_minutes * 60
            # 格式化击杀时间,仅显示到秒
            formatted_current_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
            # 格式化刷新计时,仅显示到秒
            formatted_refresh_time = refresh_time.strftime('%Y-%m-%d %H:%M:%S')
            # 更新 DataFrame 中的“刷新计时”列
            self.df.at = formatted_refresh_time
            # 更新 DataFrame 中的“击杀时间”列
            self.df.at = formatted_current_time
            # 停止该行已有的倒计时
            if selected_item in active_countdown_threads:
                self.stop_countdown(selected_item)
                return
            # 创建新的倒计时线程
            countdown_thread = threading.Thread(target=self.countdown,
                                                args=(selected_index, remaining_time, selected_item))
            countdown_thread.start()
            active_countdown_threads = {
                "thread": countdown_thread,
                "flag": True,
                "after_id": None
            }
            # 更新 Treeview 显示的击杀时间
            self.tree.item(selected_item, values=(self.df.loc,
                                                self.df.loc,
                                                remaining_time,
                                                self.df.loc,
                                                self.df.loc,
                                                self.df.loc,
                                                self.df.loc))
            # 保存更新后的数据到文件
            self.save_data()

    def countdown(self, selected_index, remaining_time, selected_item):
      global active_countdown_threads

      def update_countdown(_remaining_time):
            print(active_countdown_threads)
            thread = active_countdown_threads.get(selected_item)
            if thread and not thread["flag"]:
                return
            hours, remaining_seconds = divmod(_remaining_time, 3600)
            minutes, seconds = divmod(remaining_seconds, 60)

            self.df.at = f"{hours:02}:{minutes:02}:{seconds:02}"
            self.tree.item(selected_item, values=(self.df.loc,
                                                self.df.loc,
                                                f"{hours:02}:{minutes:02}:{seconds:02}",
                                                self.df.loc,
                                                self.df.loc,
                                                self.df.loc,
                                                self.df.loc))

            if _remaining_time > 0:
                if thread["after_id"]:
                  self.master.after_cancel(thread["after_id"])
                thread["after_id"] = self.master.after(1000, update_countdown, _remaining_time - 1)

      update_countdown(remaining_time)


      # 倒计时结束后,移除该行的倒计时线程
      # del self.active_countdown_threads

    def stop_countdown(self, selected_item):
      global active_countdown_threads
      print("停止倒计时stop_countdown")
      print(active_countdown_threads)
      if selected_item in active_countdown_threads:
            thread = active_countdown_threads.get(selected_item)
            thread["flag"] = False
            if thread["after_id"]:
                self.master.after_cancel(thread["after_id"])
            thread["after_id"] = None
            thread["thread"].join()
            print("线程已结束")
            active_countdown_threads.pop(selected_item)


if __name__ == "__main__":
    root = tk.Tk()
    timer = Timer(root)
    root.mainloop()

Loker 发表于 2024-4-20 18:23

Loker 发表于 2024-4-20 18:20
简单弄了一下第一个问题,第二个问题没看懂
# 导入所需的库
import tkinter as ...

# 倒计时结束后,移除该行的倒计时线程
del self.active_countdown_threads

这个代码有问题,不能直接删除,会导致每次拿到active_countdown_threads都是空的

MrrrrrrrDou 发表于 2024-4-20 18:26

Loker 发表于 2024-4-20 18:23
# 倒计时结束后,移除该行的倒计时线程
del self.active_countdown_threads



第二个问题简单来说就是,我关掉软件时,我希望倒计时还在进行(事实上不是),当我再次打开软件,我希望倒计时是正确的而且正在运行
页: [1] 2
查看完整版本: Python写的计时软件求助