Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/tchMaterial-parser.pyw
Original file line number Diff line number Diff line change
Expand Up @@ -699,11 +699,18 @@ class resource_helper: # 获取网站上资源的数据

def thread_it(func, args: tuple = ()) -> None: # 打包函数到线程
t = threading.Thread(target=func, args=args)
# t.daemon = True
t.daemon = True
t.start()

def ui_call(func, *args, **kwargs) -> None: # 在主线程执行 Tkinter UI 更新
root.after(0, lambda: func(*args, **kwargs))
if app_closing:
return

try:
root.after(0, lambda: (not app_closing) and func(*args, **kwargs))
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lambda function uses (not app_closing) and func(*args, **kwargs) which will return the result of func(*args, **kwargs) when app_closing is False, but will return False when app_closing is True. This is correct for preventing execution, but the lambda will still be queued even if app_closing becomes True between line 706 and line 710. Additionally, this creates a redundant check since line 706-707 already guards against app_closing. Consider simplifying to root.after(0, lambda: func(*args, **kwargs)) since the early return on line 706-707 already handles the app_closing check.

Suggested change
root.after(0, lambda: (not app_closing) and func(*args, **kwargs))
root.after(0, lambda: func(*args, **kwargs))

Copilot uses AI. Check for mistakes.
except Exception:
# 主窗口销毁后,root.after 会抛错,直接忽略即可
pass
Comment on lines +711 to +713
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a bare except Exception: here silently swallows all exceptions, which could hide legitimate errors beyond just the expected case where root has been destroyed. While this may work for the intended use case, it makes debugging more difficult. Consider being more specific about what exception you expect (e.g., except tk.TclError: for Tkinter-related errors when the window is destroyed), or at minimum, log the exception for debugging purposes.

Copilot uses AI. Check for mistakes.

def pick_ui_font_family() -> str: # 选择一个合适的字体
try:
Expand All @@ -722,6 +729,7 @@ def pick_ui_font_family() -> str: # 选择一个合适的字体

session = requests.Session() # 初始化请求
download_states = [] # 初始化下载状态
app_closing = False
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global flag app_closing is accessed and modified across multiple threads without synchronization primitives (e.g., threading.Lock). In Python, while simple boolean operations are generally atomic due to the GIL, there's no guarantee that all threads will immediately see the updated value due to potential memory caching. This could lead to race conditions where background threads continue to call ui_call even after app_closing is set to True. Consider using threading.Event instead, which provides proper memory barriers and thread synchronization. For example: app_closing = threading.Event(), then use app_closing.is_set() to check and app_closing.set() to signal closure.

Copilot uses AI. Check for mistakes.
access_token = None
headers = { "X-ND-AUTH": 'MAC id="0",nonce="0",mac="0"' } # 设置请求头部,包含认证信息,其中 “MAC id” 即为 Access Token,“nonce” 和 “mac” 不可缺省但可为任意非空值
session.proxies = {} # 全局忽略代理
Expand Down Expand Up @@ -778,10 +786,17 @@ def set_icon() -> None: # 设置窗口图标
set_icon() # 设置窗口图标

def on_closing() -> None: # 处理窗口关闭事件
global app_closing

if app_closing:
return

if not all(state["finished"] for state in download_states): # 当正在下载时,询问用户
if not messagebox.askokcancel("提示", "下载任务未完成,是否退出?"):
return

app_closing = True

current_process = psutil.Process(os.getpid()) # 获取自身的进程 ID
child_processes = current_process.children(recursive=True) # 获取自身的所有子进程

Expand All @@ -791,8 +806,10 @@ def on_closing() -> None: # 处理窗口关闭事件
except Exception: # 进程可能已经结束
pass

# 结束自身进程
sys.exit(0)
try:
root.destroy()
except Exception:
pass
Comment on lines +811 to +812
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a bare except Exception: here silently swallows all exceptions during window destruction. While this provides robustness, it could hide legitimate errors. Consider being more specific (e.g., except tk.TclError:) or at minimum, log the exception using print_error(e) to aid debugging, consistent with error handling elsewhere in the codebase (e.g., lines 28-29, 383).

Suggested change
except Exception:
pass
except Exception as e:
print_error(e)

Copilot uses AI. Check for mistakes.

root.protocol("WM_DELETE_WINDOW", on_closing) # 注册窗口关闭事件的处理函数

Expand Down