From ff89cb976436f6c651e2d13edd019d764a3d70e9 Mon Sep 17 00:00:00 2001 From: jimmy-sketch Date: Sat, 22 Nov 2025 08:18:26 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=AE=E7=AA=97?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E7=A4=BA=E6=96=87=E5=AD=97=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E6=B7=B1=E8=89=B2/=E6=B5=85=E8=89=B2=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E5=AD=97=E4=BD=93=E9=A2=9C=E8=89=B2=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/notification_service.py | 107 ++++++++++++++++++ app/tools/result_display.py | 31 ++++- 2 files changed, 134 insertions(+), 4 deletions(-) diff --git a/app/common/notification/notification_service.py b/app/common/notification/notification_service.py index 620a5d78..9523d578 100644 --- a/app/common/notification/notification_service.py +++ b/app/common/notification/notification_service.py @@ -36,6 +36,37 @@ def update_content(self, widgets): self.layout.addWidget(widget) self.content_widgets.append(widget) + # 确保新添加的 BodyLabel 可见:根据主题强制设置前景色 + try: + from app.tools.personalised import is_dark_theme + from qfluentwidgets import qconfig + from qfluentwidgets import BodyLabel as QFBodyLabel + + fg = "#ffffff" if is_dark_theme(qconfig) else "#000000" + + def apply_fg_to(w): + # 如果是直接的 BodyLabel,设置样式 + if isinstance(w, QFBodyLabel): + existing = w.styleSheet() or "" + if "color:" not in existing: + # 保留已有样式,追加颜色 + w.setStyleSheet(existing + f" color: {fg};") + else: + # 遍历子控件查找 BodyLabel + for child in w.findChildren(QFBodyLabel): + existing = child.styleSheet() or "" + if "color:" not in existing: + child.setStyleSheet(existing + f" color: {fg};") + + for w in self.content_widgets: + try: + apply_fg_to(w) + except Exception: + pass + except Exception: + # 忽略主题检测错误,保持原样 + pass + class NotificationWindowTemplate(PageTemplate): """通知窗口页面模板""" @@ -69,6 +100,14 @@ def __init__(self, parent=None): # 设置UI self.setup_ui() + # 订阅主题变化,确保切换主题时更新文字颜色 + try: + from qfluentwidgets import qconfig + + qconfig.themeChanged.connect(self._on_theme_changed) + except Exception: + pass + # 设置窗口圆角 self.setBorderRadius(15) @@ -258,6 +297,18 @@ def apply_settings(self, settings=None): # 设置透明度(背景和字体透明度统一) self.setWindowOpacity(transparency) + # 设置倒计时标签颜色,确保与背景对比 + try: + from app.tools.personalised import is_dark_theme + from qfluentwidgets import qconfig + + fg = "#ffffff" if is_dark_theme(qconfig) else "#000000" + existing = self.countdown_label.styleSheet() or "" + if "color:" not in existing: + self.countdown_label.setStyleSheet(existing + f" color: {fg};") + except Exception: + pass + # 根据设置定位窗口 self.position_window(settings) @@ -287,6 +338,51 @@ def update_countdown_display(self): self.countdown_timer.stop() self.countdown_label.setText("连续点击3次关闭窗口") + def _on_theme_changed(self): + """主题切换时更新浮窗内文字和背景颜色""" + try: + from app.tools.personalised import is_dark_theme + from qfluentwidgets import qconfig + + fg = "#ffffff" if is_dark_theme(qconfig) else "#000000" + + # 更新所有 BodyLabel 子控件颜色 + for lbl in self.findChildren(BodyLabel): + try: + existing = lbl.styleSheet() or "" + parts = [ + p.strip() + for p in existing.split(";") + if p.strip() and not p.strip().startswith("color:") + ] + parts.append(f"color: {fg} !important") + lbl.setStyleSheet("; ".join(parts) + ";") + except Exception: + continue + + # 更新倒计时标签颜色 + try: + existing = self.countdown_label.styleSheet() or "" + parts = [ + p.strip() + for p in existing.split(";") + if p.strip() and not p.strip().startswith("color:") + ] + parts.append(f"color: {fg} !important") + self.countdown_label.setStyleSheet("; ".join(parts) + ";") + except Exception: + pass + + # 更新背景与拖动线样式 + try: + self.update_background_style() + self.update_drag_line_style() + self.update_drag_line_container_style() + except Exception: + pass + except Exception: + pass + def _get_screen_from_settings(self, settings): """根据设置获取屏幕""" screen = QApplication.primaryScreen() @@ -540,6 +636,11 @@ def start_show_animation(self, settings=None): # 立即更新倒计时显示(显示"正在抽取中") self.update_countdown_display() + # 确保颜色与当前主题同步 + try: + self._on_theme_changed() + except Exception: + pass def on_animation_finished(self): """动画完成后的处理""" @@ -616,6 +717,12 @@ def update_content(self, student_labels, settings=None): for label in student_labels: self.content_layout.addWidget(label) + # 确保颜色与当前主题同步 + try: + self._on_theme_changed() + except Exception: + pass + # 调整窗口大小以适应内容 self.adjustSize() diff --git a/app/tools/result_display.py b/app/tools/result_display.py index 204db1b8..2b94a9ba 100644 --- a/app/tools/result_display.py +++ b/app/tools/result_display.py @@ -191,9 +191,21 @@ def _apply_label_style(label, font_size, animation_color): style_sheet = f"font-size: {font_size}pt; " if animation_color == 1: - style_sheet += f"color: {ResultDisplayUtils._generate_vibrant_color()};" + style_sheet += f"color: {ResultDisplayUtils._generate_vibrant_color()} !important;" elif animation_color == 2: - style_sheet += f"color: {fixed_color};" + style_sheet += f"color: {fixed_color} !important;" + else: + try: + from app.tools.personalised import is_dark_theme + from qfluentwidgets import qconfig + + default_color = ( + "#ffffff" if is_dark_theme(qconfig) else "#000000" + ) + style_sheet += f"color: {default_color} !important;" + except Exception: + # 兜底使用黑色 + style_sheet += "color: #000000 !important;" widget.setStyleSheet(style_sheet) else: @@ -203,9 +215,20 @@ def _apply_label_style(label, font_size, animation_color): "roll_call_settings", "animation_fixed_color" ) if animation_color == 1: - style_sheet += f"color: {ResultDisplayUtils._generate_vibrant_color()};" + style_sheet += ( + f"color: {ResultDisplayUtils._generate_vibrant_color()} !important;" + ) elif animation_color == 2: - style_sheet += f"color: {fixed_color};" + style_sheet += f"color: {fixed_color} !important;" + else: + try: + from app.tools.personalised import is_dark_theme + from qfluentwidgets import qconfig + + default_color = "#ffffff" if is_dark_theme(qconfig) else "#000000" + style_sheet += f"color: {default_color} !important;" + except Exception: + style_sheet += "color: #000000 !important;" label.setStyleSheet(style_sheet) From edcff82b52de4f04df5b61014991b70ee045ca9a Mon Sep 17 00:00:00 2001 From: jimmy-sketch Date: Sat, 22 Nov 2025 11:55:34 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=E5=A4=84=E7=90=86=E6=8E=89=E8=8B=A5?= =?UTF-8?q?=E5=B9=B2try-expect-pass=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/notification_service.py | 80 +++++-- app/page_building/page_template.py | 13 +- app/page_building/window_template.py | 8 +- app/tools/history.py | 7 +- app/view/another_window/remaining_list.py | 199 +++++++++++++----- app/view/main/roll_call.py | 17 +- .../floating_window_management.py | 18 +- .../sidebar_tray_management.py | 29 ++- app/view/settings/settings.py | 32 ++- main.py | 78 ++++++- 10 files changed, 365 insertions(+), 116 deletions(-) diff --git a/app/common/notification/notification_service.py b/app/common/notification/notification_service.py index 9523d578..27e8f840 100644 --- a/app/common/notification/notification_service.py +++ b/app/common/notification/notification_service.py @@ -61,8 +61,10 @@ def apply_fg_to(w): for w in self.content_widgets: try: apply_fg_to(w) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error applying fg to content widget: {}", e) except Exception: # 忽略主题检测错误,保持原样 pass @@ -105,8 +107,10 @@ def __init__(self, parent=None): from qfluentwidgets import qconfig qconfig.themeChanged.connect(self._on_theme_changed) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error connecting themeChanged signal (ignored): {}", e) # 设置窗口圆角 self.setBorderRadius(15) @@ -237,7 +241,10 @@ def update_background_style(self): ) self.update_drag_line_style() self.update_drag_line_container_style() - except: + except Exception as e: + from loguru import logger + + logger.exception("Error updating background style (fallback used): {}", e) # 如果无法获取主题信息,默认使用白色背景和深色拖动线 background_color = "#ffffff" self.background_widget.setStyleSheet( @@ -266,7 +273,12 @@ def update_drag_line_container_style(self): self.drag_line_container.setStyleSheet( f"background-color: {background_color}; border-top-left-radius: 15px; border-top-right-radius: 15px; border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;" ) - except: + except Exception as e: + from loguru import logger + + logger.exception( + "Error updating drag line container style (fallback used): {}", e + ) # 如果无法获取主题信息,默认使用白色背景 self.drag_line_container.setStyleSheet( "background-color: #ffffff; border-top-left-radius: 15px; border-top-right-radius: 15px; border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;" @@ -306,8 +318,10 @@ def apply_settings(self, settings=None): existing = self.countdown_label.styleSheet() or "" if "color:" not in existing: self.countdown_label.setStyleSheet(existing + f" color: {fg};") - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error setting countdown label color: {}", e) # 根据设置定位窗口 self.position_window(settings) @@ -357,7 +371,12 @@ def _on_theme_changed(self): ] parts.append(f"color: {fg} !important") lbl.setStyleSheet("; ".join(parts) + ";") - except Exception: + except Exception as e: + from loguru import logger + + logger.exception( + "Error applying color to label child (continuing): {}", e + ) continue # 更新倒计时标签颜色 @@ -370,7 +389,12 @@ def _on_theme_changed(self): ] parts.append(f"color: {fg} !important") self.countdown_label.setStyleSheet("; ".join(parts) + ";") - except Exception: + except Exception as e: + from loguru import logger + + logger.exception( + "Error applying countdown label color (ignored): {}", e + ) pass # 更新背景与拖动线样式 @@ -378,9 +402,17 @@ def _on_theme_changed(self): self.update_background_style() self.update_drag_line_style() self.update_drag_line_container_style() - except Exception: + except Exception as e: + from loguru import logger + + logger.exception( + "Error updating background/drag line styles (ignored): {}", e + ) pass - except Exception: + except Exception as e: + from loguru import logger + + logger.exception("Error in _on_theme_changed (ignored): {}", e) pass def _get_screen_from_settings(self, settings): @@ -639,8 +671,10 @@ def start_show_animation(self, settings=None): # 确保颜色与当前主题同步 try: self._on_theme_changed() - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error syncing theme on show (ignored): {}", e) def on_animation_finished(self): """动画完成后的处理""" @@ -662,9 +696,10 @@ def on_animation_finished(self): try: if not (self.windowFlags() & Qt.WindowDoesNotAcceptFocus): self.activateWindow() - except Exception: - # 保险兜底:如果出现问题则不激活窗口 - pass + except Exception as e: + from loguru import logger + + logger.exception("Error activating window (ignored): {}", e) # 更新倒计时显示 self.update_countdown_display() @@ -720,8 +755,10 @@ def update_content(self, student_labels, settings=None): # 确保颜色与当前主题同步 try: self._on_theme_changed() - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error syncing theme on update_content (ignored): {}", e) # 调整窗口大小以适应内容 self.adjustSize() @@ -780,7 +817,10 @@ def get_notification_title(self): return get_content_name_async( "notification_settings", "notification_result" ) - except: + except Exception as e: + from loguru import logger + + logger.exception("Error getting notification title (fallback used): {}", e) # 如果无法获取多语言文本,则使用默认文本 return "通知结果" diff --git a/app/page_building/page_template.py b/app/page_building/page_template.py index d3eebcc1..3f272274 100644 --- a/app/page_building/page_template.py +++ b/app/page_building/page_template.py @@ -10,6 +10,7 @@ from PySide6.QtGui import * from PySide6.QtCore import * from PySide6.QtNetwork import * +import loguru from qfluentwidgets import * from app.tools.variable import * @@ -108,10 +109,12 @@ def create_content(self): self.content_created = True elapsed = time.perf_counter() - start - logger.debug(f"创建内容组件 {content_name} 耗时: {elapsed:.3f}s") + loguru.logger.debug(f"创建内容组件 {content_name} 耗时: {elapsed:.3f}s") except Exception as e: elapsed = time.perf_counter() - start - logger.error(f"创建内容组件失败 ({elapsed:.3f}s): {e}") + from loguru import logger + + logger.exception(f"创建内容组件失败 ({elapsed:.3f}s): {e}") def create_empty_content(self, message="该页面正在开发中,敬请期待!"): """创建空页面内容""" @@ -437,8 +440,10 @@ def load_all_pages(self, interval_ms: int = 50, max_per_tick: int = 5): ] ), ) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error scheduling batch page loads (ignored): {}", e) def on_current_index_changed(self, index: int): """堆叠窗口索引改变时的处理""" diff --git a/app/page_building/window_template.py b/app/page_building/window_template.py index 2b799676..729536e2 100644 --- a/app/page_building/window_template.py +++ b/app/page_building/window_template.py @@ -169,7 +169,13 @@ def _apply_current_theme(self) -> None: self.default_page.setStyleSheet( "background-color: transparent;" ) - except: + except Exception as e: + from loguru import logger + + logger.exception( + "Error detecting dark mode with darkdetect (fallback to light): {}", + e, + ) # 如果检测失败,使用浅色主题 self.setStyleSheet("background-color: #ffffff;") self.default_page.setStyleSheet("background-color: transparent;") diff --git a/app/tools/history.py b/app/tools/history.py index be4b583f..869ebb78 100644 --- a/app/tools/history.py +++ b/app/tools/history.py @@ -450,7 +450,12 @@ def calculate_weight(students_data: list, class_name: str) -> list: current_time = datetime.now() days_diff = (current_time - last_time).days time_factor = min(1.0, days_diff / 30.0) * 0.5 - except: + except Exception as e: + from loguru import logger + + logger.exception( + "Error calculating time factor for student weights: {}", e + ) time_factor = 0.0 else: time_factor = 0.0 diff --git a/app/view/another_window/remaining_list.py b/app/view/another_window/remaining_list.py index 166f6916..c3f1dac8 100644 --- a/app/view/another_window/remaining_list.py +++ b/app/view/another_window/remaining_list.py @@ -174,12 +174,17 @@ def run(self): # 发送结果回主线程 self.finished.emit(filtered_students) - except Exception: + except Exception as e: + from loguru import logger + + logger.exception("Error loading students in StudentLoader.run: {}", e) # 出错时返回空列表 try: self.finished.emit([]) - except Exception: - pass + except Exception as inner_e: + logger.exception( + "Error emitting finished signal with empty list: {}", inner_e + ) class RemainingListPage(QWidget): @@ -209,7 +214,10 @@ def __init__(self, parent=None): # 减少每次创建卡片时的重复开销 try: self._font_family = load_custom_font() - except Exception: + except Exception as e: + from loguru import logger + + logger.exception("Failed to load custom font: {}", e) self._font_family = None # 预先设置为空;init_ui 中会尝试异步预取模板文本 self._student_info_text = None @@ -296,8 +304,10 @@ def load_student_data(self): and self._loading_thread.isRunning() ): return - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error loading remaining list data: {}", e) students_file = self.get_students_file() # 使用 StudentLoader 在后台处理 I/O 和筛选 @@ -327,8 +337,10 @@ def _on_students_loaded(self, students_list): # 清理线程引用 if hasattr(self, "_loading_thread"): self._loading_thread = None - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error handling student group processing: {}", e) def update_ui(self): """更新UI显示""" @@ -434,18 +446,28 @@ def update_layout(self): # 恢复更新 try: self.setUpdatesEnabled(True) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error processing student in StudentLoader: {}", e) try: if top_win is not None: top_win.setUpdatesEnabled(True) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error re-enabling updates on top window (ignored): {}", e + ) try: # 触发一次完整刷新 self.update() - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error calling update() after layout update (ignored): {}", e + ) def _calculate_columns(self, width: int) -> int: """根据窗口宽度和卡片尺寸动态计算列数""" @@ -462,7 +484,10 @@ def _calculate_columns(self, width: int) -> int: # 至少显示1列,且不超过一个合理上限 return max(1, min(int(max_cols), 6)) - except Exception: + except Exception as e: + from loguru import logger + + logger.exception("Error calculating columns (fallback to 1): {}", e) return 1 def _start_incremental_render(self): @@ -505,8 +530,12 @@ def run(self): try: if getattr(self.reporter, "cancel_requested", False): break - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error checking reporter cancel flag (ignored): {}", e + ) batch = [] for _ in range(self.batch_size): if not self.students: @@ -539,31 +568,56 @@ def run(self): try: if getattr(self.reporter, "cancel_requested", False): break - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error checking reporter cancel flag before emit (ignored): {}", + e, + ) try: self.reporter.batch_ready.emit(batch) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error emitting batch_ready (ignored): {}", e + ) try: self.reporter.finished.emit() - except Exception: - pass - except Exception: + except Exception as e: + from loguru import logger + + logger.exception("Error emitting finished (ignored): {}", e) + except Exception as e: + from loguru import logger + + logger.exception("Unhandled error in StudentRenderTask.run: {}", e) try: self.reporter.finished.emit() - except Exception: - pass + except Exception as inner_e: + from loguru import logger + + logger.exception( + "Error emitting finished after exception: {}", inner_e + ) # 请求取消之前正在运行的渲染任务(如果存在) try: if self._rendering and self._render_reporter is not None: try: self._render_reporter.cancel_requested = True - except Exception: - pass - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error requesting cancel on previous render reporter (ignored): {}", + e, + ) + except Exception as e: + from loguru import logger + + logger.exception("Error in RemainingListPage initialization: {}", e) self._render_reporter = reporter task = StudentRenderTask( @@ -587,8 +641,13 @@ def _on_batch_ready(self, reporter, batch: list): try: if getattr(reporter, "cancel_requested", False): return - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error checking reporter cancel_requested flag in _on_batch_ready (ignored): {}", + e, + ) if not batch: return @@ -611,8 +670,10 @@ def _on_batch_ready(self, reporter, batch: list): try: if card.parent() is not None and card.parent() is not self: card.setParent(None) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error resetting card parent (ignored): {}", e) self.cards.append(card) self._cards_set.add(key) @@ -628,8 +689,12 @@ def _on_batch_ready(self, reporter, batch: list): try: if self.grid_layout.indexOf(card) != -1: continue - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error checking grid_layout.indexOf (ignored): {}", e + ) row = i // columns col = i % columns @@ -642,14 +707,27 @@ def _on_batch_ready(self, reporter, batch: list): if existing_widget is not None and existing_widget is not card: try: self.grid_layout.removeWidget(existing_widget) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error removing existing widget from grid (ignored): {}", + e, + ) try: existing_widget.hide() - except Exception: - pass - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error hiding existing widget (ignored): {}", e + ) + except Exception as e: + from loguru import logger + + logger.exception( + "Error handling existing widget in grid (ignored): {}", e + ) try: self.grid_layout.addWidget(card, row, col) @@ -660,16 +738,23 @@ def _on_batch_ready(self, reporter, batch: list): for col in range(columns): self.grid_layout.setColumnStretch(col, 1) - except Exception: - logger.exception("增量渲染时布局更新失败") + except Exception as e: + from loguru import logger + + logger.exception("增量渲染时布局更新失败: {}", e) def _on_render_finished(self, reporter): """后台渲染完成后的槽,接收 reporter 用于忽略过期任务""" try: if getattr(reporter, "cancel_requested", False): return - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error checking reporter cancel_requested in _on_render_finished (ignored): {}", + e, + ) self._rendering = False self._pending_students = [] @@ -683,8 +768,12 @@ def _finalize_render(self): if self._render_timer is not None: self._render_timer.stop() self._render_timer = None - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error stopping render timer in _finalize_render (ignored): {}", e + ) self._rendering = False @@ -705,14 +794,20 @@ def _clear_grid_layout(self): if widget: try: self.grid_layout.removeWidget(widget) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error removing widget from grid during clear (ignored): {}", e + ) widget.hide() # 清空已记录的已添加卡片集合 try: self._cards_set.clear() - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error clearing cards set (ignored): {}", e) def create_student_card(self, student: Dict[str, Any]) -> CardWidget: """创建学生卡片 diff --git a/app/view/main/roll_call.py b/app/view/main/roll_call.py index f5da28f7..b89d0e8f 100644 --- a/app/view/main/roll_call.py +++ b/app/view/main/roll_call.py @@ -414,8 +414,12 @@ def start_draw(self): self.start_button.setEnabled(True) try: self.start_button.clicked.disconnect() - except: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error disconnecting start_button clicked (ignored): {}", e + ) self.draw_random() animation = readme_settings_async("roll_call_settings", "animation") autoplay_count = readme_settings_async("roll_call_settings", "autoplay_count") @@ -468,8 +472,13 @@ def stop_animation(self): self.is_animating = False try: self.start_button.clicked.disconnect() - except: - pass + except Exception as e: + from loguru import logger + + logger.exception( + "Error disconnecting start_button clicked during stop_animation (ignored): {}", + e, + ) self.start_button.clicked.connect(lambda: self.start_draw()) half_repeat = readme_settings_async("roll_call_settings", "half_repeat") diff --git a/app/view/settings/custom_settings/floating_window_management.py b/app/view/settings/custom_settings/floating_window_management.py index 49756ad7..055bbc2f 100644 --- a/app/view/settings/custom_settings/floating_window_management.py +++ b/app/view/settings/custom_settings/floating_window_management.py @@ -85,8 +85,10 @@ def _create_deferred(self, name: str): if placeholder is None: try: self.vBoxLayout.addWidget(real_widget) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error handling floating window action: {}", e) setattr(self, name, real_widget) return @@ -119,8 +121,10 @@ def _create_deferred(self, name: str): except Exception: try: self.vBoxLayout.addWidget(real_widget) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error in floating window sub-action: {}", e) setattr(self, name, real_widget) return @@ -131,8 +135,10 @@ def _create_deferred(self, name: str): try: self.vBoxLayout.addWidget(real_widget) setattr(self, name, real_widget) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error reading floating window settings: {}", e) # ================================================== diff --git a/app/view/settings/custom_settings/sidebar_tray_management.py b/app/view/settings/custom_settings/sidebar_tray_management.py index 6575631a..8395faa3 100644 --- a/app/view/settings/custom_settings/sidebar_tray_management.py +++ b/app/view/settings/custom_settings/sidebar_tray_management.py @@ -57,8 +57,10 @@ def make_placeholder(attr_name: str): try: for i, name in enumerate(list(self._deferred_factories.keys())): QTimer.singleShot(120 * i, lambda n=name: self._create_deferred(n)) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error reading tray management settings: {}", e) def _create_deferred(self, name: str): factories = getattr(self, "_deferred_factories", {}) @@ -77,8 +79,10 @@ def _create_deferred(self, name: str): if placeholder is None: try: self.vBoxLayout.addWidget(real_widget) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error handling tray action: {}", e) setattr(self, name, real_widget) return @@ -110,20 +114,27 @@ def _create_deferred(self, name: str): except Exception: try: self.vBoxLayout.addWidget(real_widget) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error in tray sub-action: {}", e) setattr(self, name, real_widget) return try: layout.addWidget(real_widget) setattr(self, name, real_widget) - except Exception: + except Exception as e: try: self.vBoxLayout.addWidget(real_widget) setattr(self, name, real_widget) - except Exception: - pass + except Exception as inner_e: + from loguru import logger + + logger.exception( + "Error adding real_widget as fallback in sidebar_tray_management: {}", + inner_e, + ) class sidebar_management_window(GroupHeaderCardWidget): diff --git a/app/view/settings/settings.py b/app/view/settings/settings.py index da6df086..962b81b0 100644 --- a/app/view/settings/settings.py +++ b/app/view/settings/settings.py @@ -252,20 +252,26 @@ def make_about_factory(iface=self.aboutInterface): # 在窗口显示后启动针对非 pivot 页面的后台预热(分批创建) try: QTimer.singleShot(300, lambda: self._background_warmup_non_pivot()) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error during settings warmup: {}", e) # 连接堆叠窗口切换信号,在首次切换到占位时创建真实页面 try: self.stackedWidget.currentChanged.connect(self._on_stacked_widget_changed) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error creating deferred page: {}", e) # 在窗口显示后启动后台预热,分批创建其余页面,避免一次性阻塞 try: QTimer.singleShot(300, lambda: self._background_warmup_pages()) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error scheduling background warmup pages: {}", e) def _on_stacked_widget_changed(self, index: int): """当导航切换到某个占位页时,按需创建真实页面内容""" @@ -295,8 +301,10 @@ def _on_stacked_widget_changed(self, index: int): QTimer.singleShot( 50, lambda rp=real_page: rp.load_all_pages() ) - except Exception: - pass + except Exception as e: + from loguru import logger + + logger.exception("Error in deferred page creation step: {}", e) logger.debug(f"设置页面已按需创建: {name}") except Exception as e: logger.error(f"延迟创建设置页面 {name} 失败: {e}") @@ -326,7 +334,13 @@ def _background_warmup_pages( ] pivot = [n for n in names if meta.get(n, {}).get("is_pivot", False)] ordered = non_pivot + pivot - except Exception: + except Exception as e: + from loguru import logger + + logger.exception( + "Error ordering deferred factories (fallback to original order): {}", + e, + ) ordered = names # 仅预热有限数量的页面,避免一次性占用主线程 diff --git a/main.py b/main.py index ac9bd4b3..428cf162 100644 --- a/main.py +++ b/main.py @@ -9,6 +9,7 @@ from PySide6.QtCore import * from PySide6.QtWidgets import * from PySide6.QtNetwork import * +import loguru from qfluentwidgets import * from loguru import logger @@ -214,7 +215,9 @@ def update_widget_fonts(widget, font, font_family): updated = True return updated except Exception as e: - logger.error(f"更新控件字体时发生异常: {e}") + from loguru import logger + + logger.exception("更新控件字体时发生异常: {}", e) return False @@ -233,9 +236,11 @@ def start_main_window(): main_window.show() try: elapsed = time.perf_counter() - app_start_time - logger.debug(f"主窗口创建并显示完成,启动耗时: {elapsed:.3f}s") - except Exception: - pass + loguru.logger.debug(f"主窗口创建并显示完成,启动耗时: {elapsed:.3f}s") + except Exception as e: + from loguru import logger + + logger.exception("Error calculating elapsed startup time (ignored): {}", e) except Exception as e: logger.error(f"创建主窗口失败: {e}", exc_info=True) @@ -393,17 +398,70 @@ def main_async(): print(f"应用程序启动失败: {e}") try: logger.error(f"应用程序启动失败: {e}", exc_info=True) - except: - pass + except Exception as log_e: + try: + from loguru import logger as _logger + + _logger.exception("Failed to log startup error: {}", log_e) + except Exception as inner_log_e: + try: + from loguru import logger + + logger.exception("Failed to log logging failure: {}", inner_log_e) + except Exception as final_e: + try: + import sys + + print( + f"Failed to log logging failure: {final_e}", file=sys.stderr + ) + except Exception as e: + try: + import sys + + print( + f"Failed to print logging failure: {e}", file=sys.stderr + ) + except Exception as final_e: + try: + import sys + + print( + f"Final logging fallback failed: {final_e}", + file=sys.stderr, + ) + except Exception as e: + try: + import sys + + print( + f"Final logging fallback failed to print: {e}", + file=sys.stderr, + ) + except Exception as eee: + try: + import sys + + sys.stderr.write( + f"Final logging fallback failed to print: {eee}\n" + ) + except Exception: + _ = None # 程序异常退出时释放共享内存 try: shared_memory.detach() - except: - pass + except Exception as detach_e: + from loguru import logger + + logger.exception( + "Error detaching shared memory during shutdown: {}", detach_e + ) # 关闭本地服务器 try: if local_server: local_server.close() - except: - pass + except Exception as close_e: + from loguru import logger + + logger.exception("Error closing local server during shutdown: {}", close_e) sys.exit(1) From 316d6b8ddc33f6130906b3e7a61e2f45082058c6 Mon Sep 17 00:00:00 2001 From: jimmy-sketch Date: Sat, 22 Nov 2025 15:22:00 +0800 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20=E6=A0=BC=E5=BC=8F=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-unified.yml | 42 +++++++------- build_nuitka.py | 19 +++++-- build_pyinstaller.py | 21 ++++--- pyproject.toml | 2 +- uv.lock | 86 ++++++++++++++++++++++++++++- 5 files changed, 133 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build-unified.yml b/.github/workflows/build-unified.yml index 11b71951..45704315 100644 --- a/.github/workflows/build-unified.yml +++ b/.github/workflows/build-unified.yml @@ -186,18 +186,18 @@ jobs: # Windows PyInstaller 构建 - name: 运行 Windows PyInstaller 构建 if: | - matrix.platform == 'windows' && - matrix.packager == 'pyinstaller' && - (github.event_name != 'workflow_dispatch' || + matrix.platform == 'windows' && + matrix.packager == 'pyinstaller' && + (github.event_name != 'workflow_dispatch' || (inputs.build_target == 'all' || inputs.build_target == 'windows') && (inputs.packager == 'pyinstaller' || inputs.packager == 'both')) && - (github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_dispatch' || (contains(github.event.head_commit.message, '打包') || contains(github.event.head_commit.message, 'pi') || contains(github.event.head_commit.message, 'both') || contains(github.event.head_commit.message, 'all') || contains(github.event.head_commit.message, 'win'))) && - (github.event_name != 'push' || + (github.event_name != 'push' || !startsWith(github.ref, 'refs/tags/v') || (startsWith(github.ref_name, 'v') && contains(github.ref_name, '.') && !contains(github.ref_name, '-'))) run: | @@ -210,18 +210,18 @@ jobs: # Windows Nuitka 构建 - name: 运行 Windows Nuitka 构建 if: | - matrix.platform == 'windows' && - matrix.packager == 'nuitka' && - (github.event_name != 'workflow_dispatch' || + matrix.platform == 'windows' && + matrix.packager == 'nuitka' && + (github.event_name != 'workflow_dispatch' || (inputs.build_target == 'all' || inputs.build_target == 'windows') && (inputs.packager == 'nuitka' || inputs.packager == 'both')) && - (github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_dispatch' || (contains(github.event.head_commit.message, '打包') || contains(github.event.head_commit.message, 'nk') || contains(github.event.head_commit.message, 'both') || contains(github.event.head_commit.message, 'all') || contains(github.event.head_commit.message, 'win'))) && - (github.event_name != 'push' || + (github.event_name != 'push' || !startsWith(github.ref, 'refs/tags/v') || (startsWith(github.ref_name, 'v') && contains(github.ref_name, '.') && !contains(github.ref_name, '-'))) run: | @@ -234,18 +234,18 @@ jobs: # Linux 构建 - name: 运行 Linux 构建 if: | - matrix.platform == 'linux' && - matrix.packager == 'pyinstaller' && - (github.event_name != 'workflow_dispatch' || + matrix.platform == 'linux' && + matrix.packager == 'pyinstaller' && + (github.event_name != 'workflow_dispatch' || (inputs.build_target == 'all' || inputs.build_target == 'linux') && (inputs.packager == 'pyinstaller' || inputs.packager == 'both')) && - (github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_dispatch' || (contains(github.event.head_commit.message, '打包') || contains(github.event.head_commit.message, 'pi') || contains(github.event.head_commit.message, 'both') || contains(github.event.head_commit.message, 'all') || contains(github.event.head_commit.message, 'linux'))) && - (github.event_name != 'push' || + (github.event_name != 'push' || !startsWith(github.ref, 'refs/tags/v') || (startsWith(github.ref_name, 'v') && contains(github.ref_name, '.') && !contains(github.ref_name, '-'))) run: | @@ -258,7 +258,7 @@ jobs: # Windows 打包操作 - name: Windows 打包操作 if: | - matrix.platform == 'windows' && + matrix.platform == 'windows' && (github.event_name != 'workflow_dispatch' || inputs.package == 'true') run: | echo "开始 Windows 打包操作..." @@ -303,7 +303,7 @@ jobs: # Linux 打包操作 - name: Linux 打包操作 if: | - matrix.platform == 'linux' && + matrix.platform == 'linux' && (github.event_name != 'workflow_dispatch' || inputs.package == 'true') run: | echo "开始 Linux 打包操作..." @@ -344,7 +344,7 @@ jobs: - name: 上传应用程序 if: | - github.event_name != 'pull_request' && + github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || inputs.package == 'true') uses: actions/upload-artifact@v4.4.2 with: @@ -355,9 +355,9 @@ jobs: release: needs: [builder_matrix] if: | - (startsWith(github.ref, 'refs/tags/v') && + (startsWith(github.ref, 'refs/tags/v') && contains(github.ref_name, '.') && !contains(github.ref_name, '-') && - (github.event_name != 'workflow_dispatch' || inputs.release == 'true')) || + (github.event_name != 'workflow_dispatch' || inputs.release == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.release == 'true') runs-on: ubuntu-latest permissions: @@ -501,4 +501,4 @@ jobs: name: SecRandom 新版本 - ${{ github.ref_name }} fail_on_unmatched_files: true env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/build_nuitka.py b/build_nuitka.py index 25d45f81..69cbe238 100644 --- a/build_nuitka.py +++ b/build_nuitka.py @@ -9,8 +9,9 @@ # 设置Windows控制台编码为UTF-8 if sys.platform == "win32": import io - sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') - sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') + + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8") from packaging_utils import ( ADDITIONAL_HIDDEN_IMPORTS, @@ -99,7 +100,10 @@ def get_nuitka_command(): module_flags, package_flags = _gather_module_and_package_flags() cmd = [ - "uv", "run", "-m", "nuitka", + "uv", + "run", + "-m", + "nuitka", "--standalone", "--onefile", "--enable-plugin=pyside6", @@ -199,7 +203,14 @@ def main(): # 执行打包 try: - result = subprocess.run(cmd, check=True, cwd=PROJECT_ROOT, capture_output=True, text=True, encoding='utf-8') + result = subprocess.run( + cmd, + check=True, + cwd=PROJECT_ROOT, + capture_output=True, + text=True, + encoding="utf-8", + ) print("\n" + "=" * 60) print("打包成功!") print("=" * 60) diff --git a/build_pyinstaller.py b/build_pyinstaller.py index 750f9a13..aa3eb18b 100644 --- a/build_pyinstaller.py +++ b/build_pyinstaller.py @@ -10,8 +10,9 @@ # 设置Windows控制台编码为UTF-8 if sys.platform == "win32": import io - sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') - sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') + + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8") from packaging_utils import ( ADDITIONAL_HIDDEN_IMPORTS, @@ -57,12 +58,7 @@ def main(): _print_packaging_summary() # 使用uv run执行PyInstaller命令 - cmd = [ - "uv", "run", "-m", "PyInstaller", - "Secrandom.spec", - "--clean", - "--noconfirm" - ] + cmd = ["uv", "run", "-m", "PyInstaller", "Secrandom.spec", "--clean", "--noconfirm"] # 打印命令 print("\n执行命令:") @@ -71,7 +67,14 @@ def main(): # 执行打包 try: - result = subprocess.run(cmd, check=True, cwd=PROJECT_ROOT, capture_output=True, text=True, encoding='utf-8') + result = subprocess.run( + cmd, + check=True, + cwd=PROJECT_ROOT, + capture_output=True, + text=True, + encoding="utf-8", + ) print("\n" + "=" * 60) print("打包成功!") print("可执行文件位于: dist/SecRandom.exe") diff --git a/pyproject.toml b/pyproject.toml index 58930c7a..5899d396 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "secrandom" version = "1.1.0" description = "公平随机抽取系统 - Modern educational tool with intelligent weighting algorithm" readme = "README.md" -requires-python = "==3.13.9" +requires-python = "==3.13.5" dependencies = [ # === UI / 界面 === diff --git a/uv.lock b/uv.lock index bbe0d123..b61eafd6 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 -revision = 3 -requires-python = "==3.13.9" +revision = 2 +requires-python = "==3.13.5" [[package]] name = "aiohappyeyeballs" @@ -54,6 +54,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/ac/a7305707cb852b7e16ff80eaf5692309bde30e2b1100a1fcacdc8f731d97/aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17", size = 7617, upload-time = "2022-11-08T16:03:57.483Z" }, ] +[[package]] +name = "altgraph" +version = "0.17.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/f8/97fdf103f38fed6792a1601dbc16cc8aac56e7459a9fff08c812d8ae177a/altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7", size = 48428, upload-time = "2025-11-21T20:35:50.583Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/ba/000a1996d4308bc65120167c21241a3b205464a2e0b58deda26ae8ac21d1/altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597", size = 21228, upload-time = "2025-11-21T20:35:49.444Z" }, +] + [[package]] name = "attrs" version = "25.3.0" @@ -352,6 +361,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, ] +[[package]] +name = "macholib" +version = "1.16.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altgraph" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/ee/af1a3842bdd5902ce133bd246eb7ffd4375c38642aeb5dc0ae3a0329dfa2/macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30", size = 59309, upload-time = "2023-09-25T09:10:16.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094, upload-time = "2023-09-25T09:10:14.188Z" }, +] + [[package]] name = "markupsafe" version = "2.1.5" @@ -515,6 +536,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] +[[package]] +name = "pefile" +version = "2023.2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/78/c5/3b3c62223f72e2360737fd2a57c30e5b2adecd85e70276879609a7403334/pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", size = 74854, upload-time = "2023-02-07T12:23:55.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791, upload-time = "2023-02-07T12:28:36.678Z" }, +] + [[package]] name = "pillow" version = "12.0.0" @@ -715,6 +745,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725, upload-time = "2024-01-05T00:28:45.903Z" }, ] +[[package]] +name = "pyinstaller" +version = "6.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altgraph" }, + { name = "macholib", marker = "sys_platform == 'darwin'" }, + { name = "packaging" }, + { name = "pefile", marker = "sys_platform == 'win32'" }, + { name = "pyinstaller-hooks-contrib" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/94/1f62e95e4a28b64cfbb5b922ef3046f968b47170d37a1e1a029f56ac9cb4/pyinstaller-6.16.0.tar.gz", hash = "sha256:53559fe1e041a234f2b4dcc3288ea8bdd57f7cad8a6644e422c27bb407f3edef", size = 4008473, upload-time = "2025-09-13T20:07:01.733Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/0a/c42ce6e5d3de287f2e9432a074fb209f1fb72a86a72f3903849fdb5e4829/pyinstaller-6.16.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:7fd1c785219a87ca747c21fa92f561b0d2926a7edc06d0a0fe37f3736e00bd7a", size = 1027899, upload-time = "2025-09-13T20:05:59.2Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d0/f18fedde32835d5a758f464c75924e2154065625f09d5456c3c303527654/pyinstaller-6.16.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:b756ddb9007b8141c5476b553351f9d97559b8af5d07f9460869bfae02be26b0", size = 727990, upload-time = "2025-09-13T20:06:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/7a/db/c8bb47514ce857b24bf9294cf1ff74844b6a489fa0ab4ef6f923288c4e38/pyinstaller-6.16.0-py3-none-manylinux2014_i686.whl", hash = "sha256:0a48f55b85ff60f83169e10050f2759019cf1d06773ad1c4da3a411cd8751058", size = 739238, upload-time = "2025-09-13T20:06:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/451dc784a8fcca0fe9f9b6b802d58555364a95b60f253613a2c83fc6b023/pyinstaller-6.16.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:73ba72e04fcece92e32518bbb1e1fb5ac2892677943dfdff38e01a06e8742851", size = 737142, upload-time = "2025-09-13T20:06:11.732Z" }, + { url = "https://files.pythonhosted.org/packages/71/37/2f457479ef8fa2821cdb448acee2421dfb19fbe908bf5499d1930c164084/pyinstaller-6.16.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:b1752488248f7899281b17ca3238eefb5410521291371a686a4f5830f29f52b3", size = 734133, upload-time = "2025-09-13T20:06:15.477Z" }, + { url = "https://files.pythonhosted.org/packages/63/c4/0f7daac4d062a4d1ac2571d8a8b9b5d6812094fcd914d139af591ca5e1ba/pyinstaller-6.16.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ba618a61627ee674d6d68e5de084ba17c707b59a4f2a856084b3999bdffbd3f0", size = 733817, upload-time = "2025-09-13T20:06:19.683Z" }, + { url = "https://files.pythonhosted.org/packages/11/e4/b6127265b42bef883e8873d850becadf748bc5652e5a7029b059328f3c31/pyinstaller-6.16.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:c8b7ef536711617e12fef4673806198872033fa06fa92326ad7fd1d84a9fa454", size = 732912, upload-time = "2025-09-13T20:06:23.46Z" }, + { url = "https://files.pythonhosted.org/packages/2b/00/c6663107bdf814b2916e71563beabd09f693c47712213bc228994cb2cc65/pyinstaller-6.16.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d1ebf84d02c51fed19b82a8abb4df536923abd55bb684d694e1356e4ae2a0ce5", size = 732773, upload-time = "2025-09-13T20:06:27.352Z" }, + { url = "https://files.pythonhosted.org/packages/a3/14/cabe9bc5f60b95d2e70e7d045ab94b0015ff8f6c8b16e2142d3597e30749/pyinstaller-6.16.0-py3-none-win32.whl", hash = "sha256:6d5f8617f3650ff9ef893e2ab4ddbf3c0d23d0c602ef74b5df8fbef4607840c8", size = 1313878, upload-time = "2025-09-13T20:06:33.234Z" }, + { url = "https://files.pythonhosted.org/packages/aa/99/2005efbc297e7813c1d6f18484aa94a1a81ce87b6a5b497c563681f4c4ea/pyinstaller-6.16.0-py3-none-win_amd64.whl", hash = "sha256:bc10eb1a787f99fea613509f55b902fbd2d8b73ff5f51ff245ea29a481d97d41", size = 1374706, upload-time = "2025-09-13T20:06:39.95Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f4/4dfcf69b86d60fcaae05a42bbff1616d48a91e71726e5ed795d773dae9b3/pyinstaller-6.16.0-py3-none-win_arm64.whl", hash = "sha256:d0af8a401de792c233c32c44b16d065ca9ab8262ee0c906835c12bdebc992a64", size = 1315923, upload-time = "2025-09-13T20:06:45.846Z" }, +] + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2025.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/83/be0f57c0b77b66c33c2283ebd4ea341022b5a743e97c5fb3bebab82b38b9/pyinstaller_hooks_contrib-2025.9.tar.gz", hash = "sha256:56e972bdaad4e9af767ed47d132362d162112260cbe488c9da7fee01f228a5a6", size = 165189, upload-time = "2025-09-24T11:21:35.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/26/23b4cfc77d7f808c69f59070e1e8293a579ec281a547c61562357160b346/pyinstaller_hooks_contrib-2025.9-py3-none-any.whl", hash = "sha256:ccbfaa49399ef6b18486a165810155e5a8d4c59b41f20dc5da81af7482aaf038", size = 444283, upload-time = "2025-09-24T11:21:33.67Z" }, +] + [[package]] name = "pyobjc" version = "10.3.2" @@ -3469,6 +3540,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, ] +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -3553,6 +3633,7 @@ dependencies = [ { name = "pulsectl", marker = "sys_platform == 'linux'" }, { name = "pycaw" }, { name = "pycryptodome" }, + { name = "pyinstaller" }, { name = "pyotp" }, { name = "pypng" }, { name = "pyqrcode" }, @@ -3612,6 +3693,7 @@ requires-dist = [ { name = "pulsectl", marker = "sys_platform == 'linux'", specifier = "==24.8.0" }, { name = "pycaw", specifier = "==20251023" }, { name = "pycryptodome", specifier = "==3.23.0" }, + { name = "pyinstaller", specifier = ">=6.0.0" }, { name = "pyotp", specifier = "==2.9.0" }, { name = "pypng", specifier = "~=0.20220715.0" }, { name = "pyqrcode", specifier = "~=1.2.1" },