From 704857ae4c1f86ae341335c29ae64b3f4a63961a Mon Sep 17 00:00:00 2001 From: lxb <1580622474@qq.com> Date: Sun, 11 Jan 2026 18:45:19 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E6=96=B0=E5=A2=9E=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=A0=BC=E4=B9=8B=E5=90=8E=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E9=80=82=E5=BA=94=E9=AB=98=E5=BA=A6=20-=20=E9=80=82=E9=85=8D?= =?UTF-8?q?=E2=80=9C=E5=B0=8F=E9=97=AE=E9=A2=98=E2=80=9D=E7=9A=84=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=98=BE=E7=A4=BA=E5=92=8C=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 11 deletions(-) diff --git a/app.py b/app.py index 14bcaf3..cd73364 100644 --- a/app.py +++ b/app.py @@ -10,10 +10,11 @@ from tkinter import messagebox, simpledialog import customtkinter as ctk from tksheet import Sheet from tkcalendar import Calendar +import tkinter.font as tkfont # ----------------------- CONFIG ----------------------- # region 配置 -VERSION = "v1.0.4" +VERSION = "v1.0.5" DB_PATH = "tasks.db" # 数据库文件路径 TEMPLATES_PATH = "templates.json" # 检索模板文件路径 @@ -48,6 +49,7 @@ TYPE_DISPLAY = { "Bug": {"icon": "🐞", "bg": "#ed7e7e"}, "需求": {"icon": "✨", "bg": "#a0e9c4"}, "其他": {"icon": "📝", "bg": "#c1d9fe"}, + "小问题": {"icon": "🔔", "bg": "#f6a746"}, } STATUS_DISPLAY = { "待处理": {"icon": "⏳", "bg": "#f7e086"}, @@ -72,14 +74,14 @@ PROCESSED_DISPLAY = { # SORT ORDER (Rule A) - used for table-column sorting (integer ranks) SORT_ORDER = { "priority": {"SSS": 8, "SS": 7, "S": 6, "A": 5, "B": 4, "C": 3, "D": 2, "E": 1}, - "type": {"Bug": 3, "需求": 2, "其他": 1}, + "type": {"Bug": 3, "需求": 2, "其他": 1, "小问题" : 1}, "status": {"待处理": 6, "进行中": 5, "保持跟进": 4, "搁置": 3, "取消": 2, "已完成": 1} } # COMPOSITE WEIGHTS (Rule B) - used in composite score calculation COMPOSITE_WEIGHTS = { "priority": {"SSS": 4.0, "SS": 3.0, "S": 2.0, "A": 1.0, "B": 0.8, "C": 0.6, "D": 0.4, "E": 0.2}, - "type": {"Bug": 1.5, "需求": 1.0, "其他": 1.0}, + "type": {"Bug": 1.5, "需求": 1.0, "其他": 1.0, "小问题": 1.0}, "status": {"待处理": 0.9, "进行中": 1.0, "保持跟进": 0.15, "搁置": 0.1, "取消": 0.0, "已完成": 0.0}, # age factor multiplier (per day) "age_factor": 0.05 @@ -679,6 +681,10 @@ class TaskManagerApp(ctk.CTk): # guard to prevent duplicate toggles from multiple bound handlers self._last_header_toggle = None + # 调整单元格宽度所需参数 + self._prev_col_widths = {} + self._resized_column = None + # build UI self._build_ui() self.refresh_table() @@ -753,23 +759,28 @@ class TaskManagerApp(ctk.CTk): self.sheet.grid(row=0, column=0, sticky="nsew") table_frame.grid_rowconfigure(0, weight=1); table_frame.grid_columnconfigure(0, weight=1) - # bind double click using extra_bindings if available + # =============================== + # 各种绑定 try: self.sheet.extra_bindings([("double_click_cell", self._on_double_click_cell)]) except Exception: self.sheet.bind("", self._on_double_click_generic, add="+") - # try binding header clicks via extra_bindings if available (try multiple tksheet event names) + try: self.sheet.extra_bindings([("column_select", self._on_header_click)]) - except Exception: - print('[Error] bind _on_header_click failed!!!') + except Exception as e: + print("[Warn] column_select not supported:", e) + + try: + self.sheet.extra_bindings([("column_width_resize", self._on_column_resize)]) + except Exception as e: + print("[Warn] column_width_resize not supported:", e) + + self.bind_all("", self._on_column_resize_end, add="+") - # header/column clicks - generic - # primary binding directly on sheet self.sheet.bind("", self._on_sheet_click_generic, add="+") - # also listen to ButtonRelease as some versions use release for headers self.sheet.bind("", self._on_sheet_click_generic, add="+") - # global binding as fallback to ensure header clicks are caught even if tksheet consumes the event + self.bind_all("", self._on_root_click, add="+") self.bind_all("", self._on_root_click, add="+") @@ -978,10 +989,14 @@ class TaskManagerApp(ctk.CTk): # try auto row size try: + # self.sheet.refresh() self.sheet.set_all_cell_sizes_to_text() except Exception: pass + # reset column width cache + self._init_column_width_cache() + def _clear_filters(self): self.search_var.set("") self.type_listbox.selection_clear(0, "end") @@ -1107,6 +1122,24 @@ class TaskManagerApp(ctk.CTk): # swallow - no-op pass + def _on_column_resize(self, event): + new_w_list = self.sheet.get_column_widths(0) + for col in range(self.sheet.get_total_columns()): + new_w = new_w_list[col] + old_w = self._prev_col_widths[col] + if new_w != old_w: + self._prev_col_widths[col] = new_w + self._resized_column = col + break + + def _on_column_resize_end(self, event): + if self._resized_column is None: + return + + col = self._resized_column + self._resized_column = None + self._recalc_column_row_heights(col) + # header click generic for sorting def _on_sheet_click_generic(self, event): try: @@ -1499,6 +1532,59 @@ class TaskManagerApp(ctk.CTk): ctk.CTkButton(right, text="Open Selected Task", command=open_task).pack(pady=6) on_select() + # 调整单元格参数 + def _init_column_width_cache(self): + self._prev_col_widths = self.sheet.get_column_widths(0) + + def _calc_text_lines(self, text, col_width, tk_font): + if not text: + return 1 + + lines = 0 + for paragraph in str(text).split("\n"): + current_width = 0 + for ch in paragraph: + ch_width = tk_font.measure(ch) + if current_width + ch_width > col_width: + lines += 1 + current_width = ch_width + else: + current_width += ch_width + lines += 1 # 每个段落至少一行 + + return max(lines, 1) + + def _recalc_column_row_heights(self, col): + font_desc = self.sheet.font() + tk_font = tkfont.Font(font=font_desc) + + line_height = tk_font.metrics("linespace") + + col_widths = self.sheet.get_column_widths(0) + col_num = self.sheet.get_total_columns() + + for row in range(self.sheet.get_total_rows()): + max_height = 0 + + for col in range(col_num): + text = self.sheet.get_cell_data(row, col) + if not text: + continue + + col_width = col_widths[col] + lines = self._calc_text_lines(text, col_width, tk_font) + + height = lines * line_height + 6 + height = min(max(height, 20), 100000) + + if height > max_height: + max_height = height + + self.sheet.row_height(row, max_height) + + self.sheet.refresh() + + # run / refresh def run(self): self.mainloop()