Browse Source

- 新增调整单元格之后自动适应高度

- 适配“小问题”的相关显示和计算
master
lxb 3 weeks ago
parent
commit
704857ae4c
  1. 108
      app.py

108
app.py

@ -10,10 +10,11 @@ from tkinter import messagebox, simpledialog
import customtkinter as ctk import customtkinter as ctk
from tksheet import Sheet from tksheet import Sheet
from tkcalendar import Calendar from tkcalendar import Calendar
import tkinter.font as tkfont
# ----------------------- CONFIG ----------------------- # ----------------------- CONFIG -----------------------
# region 配置 # region 配置
VERSION = "v1.0.4" VERSION = "v1.0.5"
DB_PATH = "tasks.db" # 数据库文件路径 DB_PATH = "tasks.db" # 数据库文件路径
TEMPLATES_PATH = "templates.json" # 检索模板文件路径 TEMPLATES_PATH = "templates.json" # 检索模板文件路径
@ -48,6 +49,7 @@ TYPE_DISPLAY = {
"Bug": {"icon": "🐞", "bg": "#ed7e7e"}, "Bug": {"icon": "🐞", "bg": "#ed7e7e"},
"需求": {"icon": "", "bg": "#a0e9c4"}, "需求": {"icon": "", "bg": "#a0e9c4"},
"其他": {"icon": "📝", "bg": "#c1d9fe"}, "其他": {"icon": "📝", "bg": "#c1d9fe"},
"小问题": {"icon": "🔔", "bg": "#f6a746"},
} }
STATUS_DISPLAY = { STATUS_DISPLAY = {
"待处理": {"icon": "", "bg": "#f7e086"}, "待处理": {"icon": "", "bg": "#f7e086"},
@ -72,14 +74,14 @@ PROCESSED_DISPLAY = {
# SORT ORDER (Rule A) - used for table-column sorting (integer ranks) # SORT ORDER (Rule A) - used for table-column sorting (integer ranks)
SORT_ORDER = { SORT_ORDER = {
"priority": {"SSS": 8, "SS": 7, "S": 6, "A": 5, "B": 4, "C": 3, "D": 2, "E": 1}, "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} "status": {"待处理": 6, "进行中": 5, "保持跟进": 4, "搁置": 3, "取消": 2, "已完成": 1}
} }
# COMPOSITE WEIGHTS (Rule B) - used in composite score calculation # COMPOSITE WEIGHTS (Rule B) - used in composite score calculation
COMPOSITE_WEIGHTS = { 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}, "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}, "status": {"待处理": 0.9, "进行中": 1.0, "保持跟进": 0.15, "搁置": 0.1, "取消": 0.0, "已完成": 0.0},
# age factor multiplier (per day) # age factor multiplier (per day)
"age_factor": 0.05 "age_factor": 0.05
@ -679,6 +681,10 @@ class TaskManagerApp(ctk.CTk):
# guard to prevent duplicate toggles from multiple bound handlers # guard to prevent duplicate toggles from multiple bound handlers
self._last_header_toggle = None self._last_header_toggle = None
# 调整单元格宽度所需参数
self._prev_col_widths = {}
self._resized_column = None
# build UI # build UI
self._build_ui() self._build_ui()
self.refresh_table() self.refresh_table()
@ -753,23 +759,28 @@ class TaskManagerApp(ctk.CTk):
self.sheet.grid(row=0, column=0, sticky="nsew") self.sheet.grid(row=0, column=0, sticky="nsew")
table_frame.grid_rowconfigure(0, weight=1); table_frame.grid_columnconfigure(0, weight=1) table_frame.grid_rowconfigure(0, weight=1); table_frame.grid_columnconfigure(0, weight=1)
# bind double click using extra_bindings if available # ===============================
# 各种绑定
try: try:
self.sheet.extra_bindings([("double_click_cell", self._on_double_click_cell)]) self.sheet.extra_bindings([("double_click_cell", self._on_double_click_cell)])
except Exception: except Exception:
self.sheet.bind("<Double-1>", self._on_double_click_generic, add="+") self.sheet.bind("<Double-1>", self._on_double_click_generic, add="+")
# try binding header clicks via extra_bindings if available (try multiple tksheet event names)
try: try:
self.sheet.extra_bindings([("column_select", self._on_header_click)]) self.sheet.extra_bindings([("column_select", self._on_header_click)])
except Exception: except Exception as e:
print('[Error] bind _on_header_click failed!!!') 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("<ButtonRelease-1>", self._on_column_resize_end, add="+")
# header/column clicks - generic
# primary binding directly on sheet
self.sheet.bind("<Button-1>", self._on_sheet_click_generic, add="+") self.sheet.bind("<Button-1>", self._on_sheet_click_generic, add="+")
# also listen to ButtonRelease as some versions use release for headers
self.sheet.bind("<ButtonRelease-1>", self._on_sheet_click_generic, add="+") self.sheet.bind("<ButtonRelease-1>", 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("<Button-1>", self._on_root_click, add="+") self.bind_all("<Button-1>", self._on_root_click, add="+")
self.bind_all("<ButtonRelease-1>", self._on_root_click, add="+") self.bind_all("<ButtonRelease-1>", self._on_root_click, add="+")
@ -978,10 +989,14 @@ class TaskManagerApp(ctk.CTk):
# try auto row size # try auto row size
try: try:
# self.sheet.refresh()
self.sheet.set_all_cell_sizes_to_text() self.sheet.set_all_cell_sizes_to_text()
except Exception: except Exception:
pass pass
# reset column width cache
self._init_column_width_cache()
def _clear_filters(self): def _clear_filters(self):
self.search_var.set("") self.search_var.set("")
self.type_listbox.selection_clear(0, "end") self.type_listbox.selection_clear(0, "end")
@ -1107,6 +1122,24 @@ class TaskManagerApp(ctk.CTk):
# swallow - no-op # swallow - no-op
pass 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 # header click generic for sorting
def _on_sheet_click_generic(self, event): def _on_sheet_click_generic(self, event):
try: try:
@ -1499,6 +1532,59 @@ class TaskManagerApp(ctk.CTk):
ctk.CTkButton(right, text="Open Selected Task", command=open_task).pack(pady=6) ctk.CTkButton(right, text="Open Selected Task", command=open_task).pack(pady=6)
on_select() 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 # run / refresh
def run(self): def run(self):
self.mainloop() self.mainloop()

Loading…
Cancel
Save