frontengine 1.0.40__tar.gz → 1.0.42__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. {frontengine-1.0.40 → frontengine-1.0.42}/PKG-INFO +1 -1
  2. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/monitor/monitor_widget.py +2 -1
  3. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/particle/particle_ui.py +13 -1
  4. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/pet/desktop_pet.py +79 -40
  5. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/presentation/cursor_effects.py +1 -1
  6. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/choose_file_dialog.py +9 -3
  7. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/window_pin_dialog.py +1 -1
  8. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/menu/preset_menu.py +6 -6
  9. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/menu/settings_menu.py +1 -1
  10. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/focus/focus_setting_ui.py +2 -2
  11. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/image/image_setting_ui.py +1 -1
  12. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/pet/pet_setting_ui.py +49 -44
  13. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/presentation/presentation_setting_ui.py +5 -5
  14. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +2 -2
  15. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +9 -4
  16. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/text/text_setting_ui.py +25 -40
  17. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/utils.py +52 -3
  18. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +42 -35
  19. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/web/web_setting_ui.py +25 -29
  20. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/widgets/widgets_setting_ui.py +7 -7
  21. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/user_setting/preset_repository.py +55 -31
  22. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/audio_meter/loopback_capture.py +1 -1
  23. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/audio_meter/system_audio_meter.py +10 -7
  24. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/platform_info/platform_info.py +5 -0
  25. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/redirect_manager/redirect_manager_class.py +7 -1
  26. frontengine-1.0.42/frontengine/utils/screen_text/screen_text_service.py +208 -0
  27. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/system_stats/system_stats.py +9 -4
  28. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/web_url.py +33 -15
  29. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/window_pin/window_pin.py +5 -0
  30. frontengine-1.0.42/frontengine/worker/__init__.py +0 -0
  31. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine.egg-info/PKG-INFO +1 -1
  32. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine.egg-info/SOURCES.txt +2 -0
  33. {frontengine-1.0.40 → frontengine-1.0.42}/pyproject.toml +1 -1
  34. {frontengine-1.0.40 → frontengine-1.0.42}/LICENSE +0 -0
  35. {frontengine-1.0.40 → frontengine-1.0.42}/README.md +0 -0
  36. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/__init__.py +0 -0
  37. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/__main__.py +0 -0
  38. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/__init__.py +0 -0
  39. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/base_widget.py +0 -0
  40. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/camera/__init__.py +0 -0
  41. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/camera/camera_widget.py +0 -0
  42. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/capture/__init__.py +0 -0
  43. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/capture/region_capture.py +0 -0
  44. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/focus_shield/__init__.py +0 -0
  45. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
  46. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/gaming/__init__.py +0 -0
  47. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/gaming/crosshair_widget.py +0 -0
  48. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/gif/__init__.py +0 -0
  49. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/gif/paint_gif.py +0 -0
  50. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/image/__init__.py +0 -0
  51. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/image/paint_image.py +0 -0
  52. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/load/__init__.py +0 -0
  53. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/load/load_someone_make_ui.py +0 -0
  54. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/measure/__init__.py +0 -0
  55. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/measure/measure_widget.py +0 -0
  56. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/monitor/__init__.py +0 -0
  57. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/notes/__init__.py +0 -0
  58. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/notes/sticky_note_widget.py +0 -0
  59. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/overlay_factory.py +0 -0
  60. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/particle/__init__.py +0 -0
  61. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/pet/__init__.py +0 -0
  62. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/presentation/__init__.py +0 -0
  63. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/presentation/annotation_overlay.py +0 -0
  64. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/presentation/keystroke_display.py +0 -0
  65. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/presentation/magnifier.py +0 -0
  66. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/scene/__init__.py +0 -0
  67. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  68. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/scene/extend_graphic_view.py +0 -0
  69. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/scene/scene.py +0 -0
  70. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/screen_care/__init__.py +0 -0
  71. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/screen_care/color_vision_widget.py +0 -0
  72. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/screen_care/screen_filter.py +0 -0
  73. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/sound_player/__init__.py +0 -0
  74. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/sound_player/sound_effect.py +0 -0
  75. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/sound_player/sound_player.py +0 -0
  76. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/spectrum/__init__.py +0 -0
  77. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/spectrum/spectrum_widget.py +0 -0
  78. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/teleprompter/__init__.py +0 -0
  79. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/teleprompter/teleprompter_widget.py +0 -0
  80. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/text/__init__.py +0 -0
  81. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/text/draw_text.py +0 -0
  82. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/toast/__init__.py +0 -0
  83. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/toast/toast_widget.py +0 -0
  84. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/video/__init__.py +0 -0
  85. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/video/video_player.py +0 -0
  86. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/wallpaper/__init__.py +0 -0
  87. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
  88. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/web/__init__.py +0 -0
  89. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/web/webview.py +0 -0
  90. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/show/window_helpers.py +0 -0
  91. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/system_tray/__init__.py +0 -0
  92. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/system_tray/extend_system_tray.py +0 -0
  93. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/__init__.py +0 -0
  94. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/color/__init__.py +0 -0
  95. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/color/global_color.py +0 -0
  96. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/__init__.py +0 -0
  97. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
  98. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
  99. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  100. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/reminder_dialog.py +0 -0
  101. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  102. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/screen_text_dialog.py +0 -0
  103. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
  104. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
  105. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/main_ui.py +0 -0
  106. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/menu/__init__.py +0 -0
  107. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/menu/help_menu.py +0 -0
  108. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/menu/how_to_menu.py +0 -0
  109. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/menu/language_menu.py +0 -0
  110. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/__init__.py +0 -0
  111. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/control_center/__init__.py +0 -0
  112. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  113. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/focus/__init__.py +0 -0
  114. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/gif/__init__.py +0 -0
  115. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  116. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/image/__init__.py +0 -0
  117. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/particle/__init__.py +0 -0
  118. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  119. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/pet/__init__.py +0 -0
  120. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/presentation/__init__.py +0 -0
  121. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  122. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  123. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  124. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  125. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  126. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  127. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  128. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  129. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  130. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  131. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  132. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  133. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/screen_care/__init__.py +0 -0
  134. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/sound_player/__init__.py +0 -0
  135. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/stream/__init__.py +0 -0
  136. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/stream/stream_setting_ui.py +0 -0
  137. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/text/__init__.py +0 -0
  138. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/tools/__init__.py +0 -0
  139. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/tools/tools_setting_ui.py +0 -0
  140. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/video/__init__.py +0 -0
  141. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  142. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/wallpaper/__init__.py +0 -0
  143. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/web/__init__.py +0 -0
  144. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/ui/page/widgets/__init__.py +0 -0
  145. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/user_setting/__init__.py +0 -0
  146. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/user_setting/scene_setting.py +0 -0
  147. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/user_setting/user_setting_file.py +0 -0
  148. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/__init__.py +0 -0
  149. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/app_profile/__init__.py +0 -0
  150. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/app_profile/app_profile_service.py +0 -0
  151. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/audio_meter/__init__.py +0 -0
  152. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
  153. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/audio_meter/screen_audio.py +0 -0
  154. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
  155. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/autostart/__init__.py +0 -0
  156. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/autostart/autostart_service.py +0 -0
  157. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/break_reminder/__init__.py +0 -0
  158. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/break_reminder/break_reminder.py +0 -0
  159. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/browser/__init__.py +0 -0
  160. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/browser/browser.py +0 -0
  161. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/clipboard/__init__.py +0 -0
  162. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/clipboard/clipboard_history.py +0 -0
  163. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
  164. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/color_vision/__init__.py +0 -0
  165. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/color_vision/color_vision.py +0 -0
  166. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/critical_exit/__init__.py +0 -0
  167. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  168. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  169. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  170. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/exception/__init__.py +0 -0
  171. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/exception/exception_tags.py +0 -0
  172. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/exception/exceptions.py +0 -0
  173. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/file/__init__.py +0 -0
  174. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/file/open/__init__.py +0 -0
  175. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/file/open/open_file.py +0 -0
  176. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/file/save/__init__.py +0 -0
  177. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/file/save/save_file.py +0 -0
  178. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/focus_shield/__init__.py +0 -0
  179. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/focus_shield/focus_shield.py +0 -0
  180. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/focus_timer/__init__.py +0 -0
  181. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/focus_timer/focus_timer.py +0 -0
  182. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/hotkey/__init__.py +0 -0
  183. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  184. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/input_watch/__init__.py +0 -0
  185. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/input_watch/input_watch_service.py +0 -0
  186. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/json/__init__.py +0 -0
  187. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/json/json_file.py +0 -0
  188. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/json/json_repository.py +0 -0
  189. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/json_format/__init__.py +0 -0
  190. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/json_format/json_process.py +0 -0
  191. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/logging/__init__.py +0 -0
  192. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/logging/loggin_instance.py +0 -0
  193. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/measure/__init__.py +0 -0
  194. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/measure/measure.py +0 -0
  195. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/__init__.py +0 -0
  196. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/english.py +0 -0
  197. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/france.py +0 -0
  198. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/germany.py +0 -0
  199. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/italy.py +0 -0
  200. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  201. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/russian.py +0 -0
  202. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  203. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/multi_language/traditional_chinese.py +0 -0
  204. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/now_playing/__init__.py +0 -0
  205. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/now_playing/now_playing.py +0 -0
  206. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/obs/__init__.py +0 -0
  207. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/obs/obs_client.py +0 -0
  208. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/obs/obs_protocol.py +0 -0
  209. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/obs/websocket_frames.py +0 -0
  210. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/pet_chat/__init__.py +0 -0
  211. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
  212. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/platform_info/__init__.py +0 -0
  213. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/playlist/__init__.py +0 -0
  214. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/playlist/playlist.py +0 -0
  215. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/plugins/__init__.py +0 -0
  216. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/plugins/plugin_loader.py +0 -0
  217. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/power_mode/__init__.py +0 -0
  218. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/power_mode/power_mode.py +0 -0
  219. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/preset_schedule/__init__.py +0 -0
  220. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  221. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/recording/__init__.py +0 -0
  222. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/recording/frame_recorder.py +0 -0
  223. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/recording/gif_writer.py +0 -0
  224. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/redirect_manager/__init__.py +0 -0
  225. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/reminder/__init__.py +0 -0
  226. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/reminder/reminder_service.py +0 -0
  227. {frontengine-1.0.40/frontengine/utils/smart_pause → frontengine-1.0.42/frontengine/utils/screen_text}/__init__.py +0 -0
  228. {frontengine-1.0.40/frontengine/utils/soundboard → frontengine-1.0.42/frontengine/utils/smart_pause}/__init__.py +0 -0
  229. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/smart_pause/pause_rules.py +0 -0
  230. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  231. {frontengine-1.0.40/frontengine/utils/system_stats → frontengine-1.0.42/frontengine/utils/soundboard}/__init__.py +0 -0
  232. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/soundboard/soundboard.py +0 -0
  233. {frontengine-1.0.40/frontengine/utils/text_source → frontengine-1.0.42/frontengine/utils/system_stats}/__init__.py +0 -0
  234. {frontengine-1.0.40/frontengine/utils/theme_schedule → frontengine-1.0.42/frontengine/utils/text_source}/__init__.py +0 -0
  235. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/text_source/text_source.py +0 -0
  236. {frontengine-1.0.40/frontengine/utils/usage_tracking → frontengine-1.0.42/frontengine/utils/theme_schedule}/__init__.py +0 -0
  237. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  238. {frontengine-1.0.40/frontengine/utils/weather → frontengine-1.0.42/frontengine/utils/usage_tracking}/__init__.py +0 -0
  239. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/usage_tracking/usage_service.py +0 -0
  240. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
  241. {frontengine-1.0.40/frontengine/utils/window_pin → frontengine-1.0.42/frontengine/utils/weather}/__init__.py +0 -0
  242. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/weather/weather_service.py +0 -0
  243. {frontengine-1.0.40/frontengine/utils/workshop → frontengine-1.0.42/frontengine/utils/window_pin}/__init__.py +0 -0
  244. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/window_pin/window_layout.py +0 -0
  245. {frontengine-1.0.40/frontengine/worker → frontengine-1.0.42/frontengine/utils/workshop}/__init__.py +0 -0
  246. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/utils/workshop/workshop_content.py +0 -0
  247. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine/worker/qthread_worker.py +0 -0
  248. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine.egg-info/dependency_links.txt +0 -0
  249. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine.egg-info/entry_points.txt +0 -0
  250. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine.egg-info/requires.txt +0 -0
  251. {frontengine-1.0.40 → frontengine-1.0.42}/frontengine.egg-info/top_level.txt +0 -0
  252. {frontengine-1.0.40 → frontengine-1.0.42}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.40
3
+ Version: 1.0.42
4
4
  Summary: FrontEngine is use-define frontview or only use like screen saver
5
5
  Author-email: JE-Chen <jechenmailman@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/Intergration-Automation-Testing/FrontEngine
@@ -92,7 +92,8 @@ class SystemMonitorWidget(BaseWidget):
92
92
  self._series: Dict[str, Deque[float]] = {
93
93
  key: deque(maxlen=self.history_length) for key, _label, _color in LINES
94
94
  }
95
- self._latest: Dict[str, float] = {key: 0.0 for key, _label, _color in LINES}
95
+ self._latest: Dict[str, float] = dict.fromkeys(
96
+ (key for key, _label, _color in LINES), 0.0)
96
97
  self._font = QFont()
97
98
  self._font.setPointSize(9)
98
99
  self._timer = QTimer(self)
@@ -1,7 +1,19 @@
1
1
  import random
2
2
  import sys
3
3
 
4
- from OpenGL.GL import *
4
+ # 明確列出用到的 OpenGL 名稱。星號匯入會讓靜態檢查完全看不出哪些名字存在,
5
+ # 也就是說整個檔案的「未定義名稱」檢查等於失效。
6
+ # The OpenGL names this file uses, spelled out. A star import blinds static
7
+ # analysis to what exists, which switches off undefined-name checking for the
8
+ # whole file.
9
+ from OpenGL.GL import (
10
+ GL_BLEND, GL_COLOR_BUFFER_BIT, GL_CULL_FACE, GL_DEPTH_BUFFER_BIT, GL_DEPTH_TEST,
11
+ GL_LINEAR, GL_MODELVIEW, GL_ONE_MINUS_SRC_ALPHA, GL_PROJECTION, GL_QUADS, GL_RGBA,
12
+ GL_SRC_ALPHA, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER,
13
+ GL_UNSIGNED_BYTE, glBegin, glBindTexture, glBlendFunc, glClear, glClearColor, glColor4f,
14
+ glDisable, glEnable, glEnd, glGenTextures, glLoadIdentity, glMatrixMode, glOrtho,
15
+ glTexCoord2f, glTexImage2D, glTexParameteri, glTranslatef, glVertex2f, glViewport,
16
+ )
5
17
  from PySide6.QtCore import Qt, QTimer
6
18
  from PySide6.QtGui import QSurfaceFormat, QPixmap
7
19
  from PySide6.QtOpenGLWidgets import QOpenGLWidget
@@ -92,6 +92,12 @@ def scan_pet_pack(folder: str) -> dict:
92
92
 
93
93
  # 可以請寵物幫忙開啟的網址協定 / URL schemes the pet will open for you
94
94
  _WEB_SCHEMES = frozenset({"http", "https"})
95
+ # 可以當寵物音效的副檔名。音效路徑可能來自匯入的預設集或 Workshop 內容,
96
+ # 所以只認這幾種副檔名的既存檔案。
97
+ # The suffixes accepted as a pet sound. The path can arrive from an imported
98
+ # preset or Workshop content, so only an existing file with one of these is
99
+ # honoured.
100
+ _SOUND_SUFFIXES = (".wav", ".mp3", ".ogg", ".m4a", ".flac")
95
101
 
96
102
 
97
103
  # 拖進寵物的檔案分類 / What a file dropped onto the pet means
@@ -99,6 +105,24 @@ DROP_SPRITE = "sprite" # 圖片或動作包 -> 換一套外觀
99
105
  DROP_FOOD = "food" # 其他檔案 -> 當成食物餵給寵物
100
106
 
101
107
 
108
+ def acceptable_sound(sound_path) -> Optional[Path]:
109
+ """
110
+ 可以拿來當寵物音效的路徑,否則回傳 None。音效路徑可能來自匯入的預設集或
111
+ Workshop 內容(別人寫的資料),所以只接受「確實存在、副檔名是音訊」的檔案
112
+ ——不讓一份預設集指使程式去開任意檔案。
113
+ The path to use as a pet sound, or None. It can arrive from an imported
114
+ preset or from Workshop content - data someone else wrote - so only a file
115
+ that actually exists and carries an audio suffix is accepted: a preset does
116
+ not get to point this at any file it likes.
117
+ """
118
+ if not sound_path:
119
+ return None
120
+ path = Path(str(sound_path))
121
+ if path.is_file() and path.suffix.lower() in _SOUND_SUFFIXES:
122
+ return path
123
+ return None
124
+
125
+
102
126
  def classify_drop(path) -> Optional[str]:
103
127
  """
104
128
  判斷拖到寵物身上的檔案要換裝還是當食物;路徑無效或空資料夾回傳 None。
@@ -238,7 +262,7 @@ class PetTimeline:
238
262
  """
239
263
 
240
264
  def __init__(self, events) -> None:
241
- self._events = sorted(list(events), key=lambda item: item[0])
265
+ self._events = sorted(events, key=lambda item: item[0])
242
266
  self._index = 0
243
267
 
244
268
  def __len__(self) -> int:
@@ -319,12 +343,13 @@ class PetTagGame:
319
343
  tick from outside.
320
344
  """
321
345
 
322
- CATCH_DISTANCE = 60.0
323
- FLEE_DISTANCE = 220.0
324
- COOLDOWN_TICKS = 20
346
+ DEFAULT_CATCH_DISTANCE = 60.0
347
+ DEFAULT_FLEE_DISTANCE = 220.0
348
+ DEFAULT_COOLDOWN_TICKS = 20
325
349
 
326
- def __init__(self, catch_distance: float = CATCH_DISTANCE, flee_distance: float = FLEE_DISTANCE,
327
- cooldown_ticks: int = COOLDOWN_TICKS) -> None:
350
+ def __init__(self, catch_distance: float = DEFAULT_CATCH_DISTANCE,
351
+ flee_distance: float = DEFAULT_FLEE_DISTANCE,
352
+ cooldown_ticks: int = DEFAULT_COOLDOWN_TICKS) -> None:
328
353
  self.catch_distance = float(catch_distance)
329
354
  self.flee_distance = float(flee_distance)
330
355
  self.cooldown_ticks = max(0, int(cooldown_ticks))
@@ -361,7 +386,7 @@ class PetTagGame:
361
386
  self.reset()
362
387
  return {}
363
388
  if self.it not in positions:
364
- self.it = sorted(positions)[0]
389
+ self.it = min(positions)
365
390
  self._cooldown = self.cooldown_ticks
366
391
  # 冷卻期間只是跑給對方追,不換鬼;先判斷再遞減,冷卻 N 拍就真的保護 N 拍。
367
392
  # While cooling down nobody can be tagged; check before decrementing so a
@@ -801,7 +826,7 @@ class PetMotion:
801
826
  self.vx = -abs(self.vx)
802
827
  return int(self.x), int(self.y)
803
828
 
804
- def _step_fall(self, left, top, right, bottom, floor_y) -> Tuple[int, int]:
829
+ def _step_fall(self, left, top, right, bottom, _floor_y) -> Tuple[int, int]:
805
830
  """落下:撞牆/天花板反彈,並在越過視窗平台或螢幕地板時落地。"""
806
831
  prev_feet = self.y + self.height
807
832
  self.vy += self.GRAVITY
@@ -816,36 +841,48 @@ class PetMotion:
816
841
  if self.y <= top:
817
842
  self.y = float(top)
818
843
  self.vy = abs(self.vy) * self.WALL_DAMPING
819
- new_feet = self.y + self.height
820
- center_x = self.x + self.width / 2.0
844
+ land_feet, land_span = self._landing_for(prev_feet, self.y + self.height, bottom)
845
+ if land_feet is not None:
846
+ self._land_on(land_feet, land_span)
847
+ return int(self.x), int(self.y)
821
848
 
849
+ def _landing_for(self, prev_feet: float, new_feet: float, bottom: float):
850
+ """
851
+ 這一步跨過了哪個落腳處:回傳 (腳底高度, 平台範圍)。平台優先於螢幕地板,
852
+ 多個平台則取最高的那個;都沒跨過就回傳 (None, None)。
853
+ What this step crossed, as (feet height, platform span). A platform wins
854
+ over the screen floor, the highest platform wins among several, and
855
+ crossing nothing gives (None, None).
856
+ """
857
+ center_x = self.x + self.width / 2.0
822
858
  land_feet = None
823
859
  land_span = None
824
860
  for platform_left, platform_right, platform_y in self.platforms:
825
- if platform_left <= center_x <= platform_right and prev_feet <= platform_y <= new_feet:
826
- if land_feet is None or platform_y < land_feet:
827
- land_feet = float(platform_y)
828
- land_span = (float(platform_left), float(platform_right))
861
+ crossed = prev_feet <= platform_y <= new_feet
862
+ over_it = platform_left <= center_x <= platform_right
863
+ if crossed and over_it and (land_feet is None or platform_y < land_feet):
864
+ land_feet = float(platform_y)
865
+ land_span = (float(platform_left), float(platform_right))
829
866
  if prev_feet <= bottom <= new_feet and (land_feet is None or bottom < land_feet):
830
- land_feet = float(bottom)
831
- land_span = None
832
-
833
- if land_feet is not None:
834
- self.y = float(land_feet - self.height)
835
- if abs(self.vy) > self.BOUNCE_THRESHOLD:
836
- self.vy = -self.vy * self.BOUNCE_DAMPING
837
- else:
838
- self.vy = 0.0
839
- self._airborne = False
840
- self.surface = SURFACE_FLOOR
841
- self.on_platform = land_span is not None
842
- self.platform_span = land_span
843
- self.ground_feet = land_feet
844
- self.vx = float(self.speed) if self.vx >= 0 else float(-self.speed)
845
- self._new_state(force_walk=True)
846
- return int(self.x), int(self.y)
867
+ return (float(bottom), None)
868
+ return (land_feet, land_span)
869
+
870
+ def _land_on(self, land_feet: float, land_span) -> None:
871
+ """落地:還有力氣就彈起來,否則就站定並恢復走動。"""
872
+ self.y = float(land_feet - self.height)
873
+ if abs(self.vy) > self.BOUNCE_THRESHOLD:
874
+ self.vy = -self.vy * self.BOUNCE_DAMPING
875
+ return
876
+ self.vy = 0.0
877
+ self._airborne = False
878
+ self.surface = SURFACE_FLOOR
879
+ self.on_platform = land_span is not None
880
+ self.platform_span = land_span
881
+ self.ground_feet = land_feet
882
+ self.vx = float(self.speed) if self.vx >= 0 else float(-self.speed)
883
+ self._new_state(force_walk=True)
847
884
 
848
- def _step_follow(self, left, top, right, bottom, floor_y) -> Tuple[int, int]:
885
+ def _step_follow(self, left, _top, right, _bottom, floor_y) -> Tuple[int, int]:
849
886
  """朝 follow_target_x 走過去;抵達附近即清除並恢復隨機行為。"""
850
887
  center_x = self.x + self.width / 2.0
851
888
  if abs(center_x - self.follow_target_x) <= self.speed + 2.0:
@@ -898,7 +935,7 @@ class PetMotion:
898
935
  self.vx = -abs(self.vx)
899
936
  return int(self.x), int(self.y)
900
937
 
901
- def _step_climb(self, left, top, right, bottom, floor_y) -> Tuple[int, int]:
938
+ def _step_climb(self, left, top, right, _bottom, floor_y) -> Tuple[int, int]:
902
939
  """沿螢幕邊緣爬行;隨機放手則掉落 / Crawl the screen perimeter; may let go and fall."""
903
940
  if self._rng.random() < self.DROP_CHANCE:
904
941
  self.surface = SURFACE_FLOOR
@@ -1348,7 +1385,7 @@ class DesktopPetWidget(BaseWidget):
1348
1385
 
1349
1386
  def start_moving(self, bounds: Tuple[int, int, int, int], interval_ms: int = 33) -> None:
1350
1387
  front_engine_logger.info(f"[DesktopPetWidget] start_moving | bounds={bounds}")
1351
- left, top, right, bottom = bounds
1388
+ left, _top, _right, bottom = bounds
1352
1389
  self.motion.set_bounds(bounds)
1353
1390
  self.motion.x = float(left)
1354
1391
  self.motion.y = float(bottom - self.pet_size)
@@ -1596,10 +1633,12 @@ class DesktopPetWidget(BaseWidget):
1596
1633
  self._pending_chat_reply = None
1597
1634
 
1598
1635
  def _init_sound(self, sound_path: Optional[str]) -> None:
1599
- if not sound_path:
1600
- return
1601
- path = Path(sound_path)
1602
- if not (path.exists() and path.is_file()):
1636
+ """載入寵物音效(路徑先經過 acceptable_sound 檢查)。"""
1637
+ path = acceptable_sound(sound_path)
1638
+ if path is None:
1639
+ if sound_path:
1640
+ front_engine_logger.warning(
1641
+ f"[DesktopPetWidget] refusing sound path: {sound_path!r}")
1603
1642
  return
1604
1643
  try:
1605
1644
  self._sound = QSoundEffect(self)
@@ -1829,8 +1868,8 @@ class DesktopPetWidget(BaseWidget):
1829
1868
  event.acceptProposedAction()
1830
1869
 
1831
1870
  def dragMoveEvent(self, event) -> None:
1832
- if self._accepts_drop(event.mimeData()):
1833
- event.acceptProposedAction()
1871
+ """拖曳過程中的判斷和進入時完全一樣,直接沿用同一套規則。"""
1872
+ self.dragEnterEvent(event)
1834
1873
 
1835
1874
  def dropEvent(self, event) -> None:
1836
1875
  path = self.dropped_path(event.mimeData())
@@ -121,7 +121,7 @@ class CursorEffectWidget(BaseWidget):
121
121
  except Exception: # pragma: no cover - defensive around injected providers
122
122
  return
123
123
  self.cursor_point = QPoint(int(position.x()) - self.x(), int(position.y()) - self.y())
124
- for ripple in list(self.ripples):
124
+ for ripple in self.ripples[:]:
125
125
  ripple["step"] += 1
126
126
  if ripple["step"] > RIPPLE_STEPS:
127
127
  self.ripples.remove(ripple)
@@ -7,6 +7,12 @@ from frontengine.utils.logging.loggin_instance import front_engine_logger
7
7
  from frontengine.utils.multi_language.language_wrapper import language_wrapper
8
8
 
9
9
 
10
+ # 動圖副檔名在多個選擇器裡重複出現
11
+ # The animation suffixes repeat across several pickers.
12
+ _GIF = ".gif"
13
+ _WEBP = ".webp"
14
+
15
+
10
16
  def choose_file(
11
17
  trigger_ui: QWidget,
12
18
  file_filter: str,
@@ -50,7 +56,7 @@ def choose_gif(trigger_ui: QWidget) -> Optional[str]:
50
56
  return choose_file(
51
57
  trigger_ui,
52
58
  file_filter="GIF WEBP (*.gif;*.webp)",
53
- extensions=[".gif", ".webp"],
59
+ extensions=[_GIF, _WEBP],
54
60
  warning_message=language_wrapper.language_word_dict.get("gif_setting_message_box")
55
61
  )
56
62
 
@@ -60,7 +66,7 @@ def choose_image(trigger_ui: QWidget) -> Optional[str]:
60
66
  return choose_file(
61
67
  trigger_ui,
62
68
  file_filter="Images (*.png;*.jpg;*.webp)",
63
- extensions=[".png", ".jpg", ".webp"],
69
+ extensions=[".png", ".jpg", _WEBP],
64
70
  warning_message=language_wrapper.language_word_dict.get("image_setting_message_box")
65
71
  )
66
72
 
@@ -90,7 +96,7 @@ def choose_pet(trigger_ui: QWidget) -> Optional[str]:
90
96
  return choose_file(
91
97
  trigger_ui,
92
98
  file_filter="Pet sprite (*.gif;*.webp;*.png;*.jpg)",
93
- extensions=[".gif", ".webp", ".png", ".jpg"],
99
+ extensions=[_GIF, _WEBP, ".png", ".jpg"],
94
100
  warning_message=language_wrapper.language_word_dict.get("gif_setting_message_box")
95
101
  )
96
102
 
@@ -119,7 +119,7 @@ class WindowPinDialog(QDialog):
119
119
  Release everything this dialog pinned and make it opaque again, so no
120
120
  other program is left stuck on top or faded after FrontEngine exits.
121
121
  """
122
- for handle in list(self.pinned):
122
+ for handle in self.pinned[:]:
123
123
  window_pin.set_always_on_top(handle, False)
124
124
  window_pin.set_opacity(handle, window_pin.MAX_OPACITY_PERCENT)
125
125
  self.pinned.clear()
@@ -113,7 +113,7 @@ def _load_action(ui: "FrontEngineMainUI") -> Callable[[], None]:
113
113
  return
114
114
  try:
115
115
  data = PresetRepository().load(preset)
116
- except (OSError, FileNotFoundError, ValueError) as error:
116
+ except (OSError, ValueError) as error:
117
117
  QMessageBox.warning(ui, title, str(error))
118
118
  return
119
119
  _apply_state(ui, data)
@@ -158,7 +158,7 @@ def _export_action(ui: "FrontEngineMainUI") -> Callable[[], None]:
158
158
  return
159
159
  try:
160
160
  PresetRepository().export_preset(preset, Path(destination))
161
- except (OSError, FileNotFoundError, ValueError) as error:
161
+ except (OSError, ValueError) as error:
162
162
  QMessageBox.warning(ui, title, str(error))
163
163
  return
164
164
  QMessageBox.information(ui, title, _t("preset_exported", "Preset exported."))
@@ -177,7 +177,7 @@ def _import_action(ui: "FrontEngineMainUI") -> Callable[[], None]:
177
177
  return
178
178
  try:
179
179
  PresetRepository().import_preset(Path(source))
180
- except (OSError, FileNotFoundError, ValueError) as error:
180
+ except (OSError, ValueError) as error:
181
181
  QMessageBox.warning(ui, title, str(error))
182
182
  return
183
183
  QMessageBox.information(ui, title, _t("preset_imported", "Preset imported."))
@@ -221,7 +221,7 @@ def apply_named_preset(ui: "FrontEngineMainUI", name: str) -> bool:
221
221
  front_engine_logger.info(f"[PresetMenu] apply_named_preset | name={name}")
222
222
  try:
223
223
  data = PresetRepository().load(name)
224
- except (OSError, FileNotFoundError, ValueError) as error:
224
+ except (OSError, ValueError) as error:
225
225
  front_engine_logger.warning(f"[PresetMenu] preset '{name}' failed: {error!r}")
226
226
  return False
227
227
  _apply_state(ui, data)
@@ -272,7 +272,7 @@ def _export_package_action(ui: "FrontEngineMainUI") -> Callable[[], None]:
272
272
  return
273
273
  try:
274
274
  PresetRepository().export_package(preset, Path(destination))
275
- except (OSError, FileNotFoundError, ValueError) as error:
275
+ except (OSError, ValueError) as error:
276
276
  QMessageBox.warning(ui, title, str(error))
277
277
  return
278
278
  QMessageBox.information(ui, title, _t("preset_exported", "Preset exported."))
@@ -291,7 +291,7 @@ def _import_package_action(ui: "FrontEngineMainUI") -> Callable[[], None]:
291
291
  return
292
292
  try:
293
293
  PresetRepository().import_package(Path(source))
294
- except (OSError, FileNotFoundError, ValueError) as error:
294
+ except (OSError, ValueError) as error:
295
295
  QMessageBox.warning(ui, title, str(error))
296
296
  return
297
297
  QMessageBox.information(ui, title, _t("preset_imported", "Preset imported."))
@@ -233,7 +233,7 @@ def _import_settings(ui: "FrontEngineMainUI") -> None:
233
233
  return
234
234
  try:
235
235
  import_user_setting(Path(source))
236
- except (OSError, FileNotFoundError, ValueError) as error:
236
+ except (OSError, ValueError) as error:
237
237
  QMessageBox.warning(ui, title, str(error))
238
238
  return
239
239
  # Hotkeys can be re-applied live; language/theme need a restart.
@@ -136,7 +136,7 @@ class FocusSettingUI(QWidget):
136
136
  language_wrapper.language_word_dict.get("focus_dim_start", "Dim the background"))
137
137
 
138
138
  def _apply_dim_settings(self) -> None:
139
- for widget in list(self.dim_widget_list):
139
+ for widget in self.dim_widget_list[:]:
140
140
  try:
141
141
  widget.set_dim(self.dim_slider.value())
142
142
  except RuntimeError:
@@ -179,7 +179,7 @@ class FocusSettingUI(QWidget):
179
179
  return
180
180
  geometry = screen.geometry()
181
181
  bounds = (geometry.x(), geometry.y(), geometry.width(), geometry.height())
182
- for widget in list(self.mask_widget_list):
182
+ for widget in self.mask_widget_list[:]:
183
183
  try:
184
184
  widget.set_region(self.mask_region_combobox.currentData(),
185
185
  self.mask_percent_slider.value())
@@ -208,7 +208,7 @@ class ImageSettingUI(QWidget):
208
208
  return
209
209
  self._slideshow_index = (self._slideshow_index + 1) % len(self._slideshow_paths)
210
210
  next_path = self._slideshow_paths[self._slideshow_index]
211
- for widget in list(self.image_widget_list):
211
+ for widget in self.image_widget_list[:]:
212
212
  setter = getattr(widget, "set_image_path", None)
213
213
  if setter is None:
214
214
  continue
@@ -11,6 +11,9 @@ from frontengine.show.pet.desktop_pet import (
11
11
  )
12
12
  from frontengine.ui.dialog.choose_file_dialog import choose_pet, choose_wav_sound
13
13
  from frontengine.ui.page.utils import (
14
+ apply_checkbox_state,
15
+ apply_combobox_data_state,
16
+ apply_combobox_text_state,
14
17
  build_recent_combobox,
15
18
  build_target_monitor_combobox,
16
19
  enable_file_drop,
@@ -29,6 +32,12 @@ from frontengine.utils.multi_language.language_wrapper import language_wrapper
29
32
  _PET_EXTENSIONS = (".gif", ".webp", ".png", ".jpg")
30
33
 
31
34
 
35
+ # 多處共用的英文備援字串(只有在翻譯缺漏時才會看到)
36
+ # The English fallback shared by several call sites, seen only when a
37
+ # translation is missing.
38
+ _CHOOSE_PACK = "Choose pet pack..."
39
+
40
+
32
41
  class PetSettingUI(QWidget):
33
42
  def __init__(self):
34
43
  front_engine_logger.info("[PetSettingUI] Init")
@@ -61,7 +70,7 @@ class PetSettingUI(QWidget):
61
70
  )
62
71
  self.choose_file_button.clicked.connect(self.choose_and_play)
63
72
  self.choose_pack_button = QPushButton(
64
- language_wrapper.language_word_dict.get("pet_choose_pack", "Choose pet pack...")
73
+ language_wrapper.language_word_dict.get("pet_choose_pack", _CHOOSE_PACK)
65
74
  )
66
75
  self.choose_pack_button.clicked.connect(self.choose_pack)
67
76
  self.choose_sound_button = QPushButton(
@@ -294,7 +303,7 @@ class PetSettingUI(QWidget):
294
303
  def _alive_pets(self) -> list:
295
304
  """回傳仍存活的寵物,並把已被銷毀者從清單移除。"""
296
305
  alive = []
297
- for pet in list(self.pet_list):
306
+ for pet in self.pet_list[:]:
298
307
  try:
299
308
  pet.center()
300
309
  except RuntimeError:
@@ -376,14 +385,14 @@ class PetSettingUI(QWidget):
376
385
  """選擇動作包資料夾(依 walk/idle/climb/fall/drag 檔名對應狀態)。"""
377
386
  front_engine_logger.info("[PetSettingUI] choose_pack")
378
387
  folder = QFileDialog.getExistingDirectory(
379
- self, language_wrapper.language_word_dict.get("pet_choose_pack", "Choose pet pack...")
388
+ self, language_wrapper.language_word_dict.get("pet_choose_pack", _CHOOSE_PACK)
380
389
  )
381
390
  if not folder:
382
391
  return
383
392
  if not scan_pet_pack(folder):
384
393
  QMessageBox.warning(
385
394
  self,
386
- language_wrapper.language_word_dict.get("pet_choose_pack", "Choose pet pack..."),
395
+ language_wrapper.language_word_dict.get("pet_choose_pack", _CHOOSE_PACK),
387
396
  language_wrapper.language_word_dict.get(
388
397
  "pet_pack_empty", "No sprites (walk/idle/climb/fall/drag) found in that folder."),
389
398
  )
@@ -432,49 +441,45 @@ class PetSettingUI(QWidget):
432
441
  }
433
442
 
434
443
  def set_state(self, state: dict) -> None:
444
+ """把預設集的內容還原到這一頁的控制項上。"""
445
+ self._restore_media(state)
446
+ apply_combobox_data_state(self.behaviour_combobox, self._behaviour_from(state))
447
+ apply_combobox_text_state(state, (
448
+ (self.size_combobox, "size"),
449
+ (self.speed_combobox, "speed"),
450
+ (self.target_monitor_combobox, "target_monitor"),
451
+ (self.volume_combobox, "volume"),
452
+ (self.focus_minutes_combobox, "focus_minutes"),
453
+ (self.break_minutes_combobox, "break_minutes"),
454
+ ))
455
+ apply_checkbox_state(state, (
456
+ (self.climb_checkbox, "climb"),
457
+ (self.talk_checkbox, "talk"),
458
+ (self.sit_checkbox, "sit"),
459
+ (self.audio_react_checkbox, "audio_react"),
460
+ (self.tag_checkbox, "tag"),
461
+ (self.speech_checkbox, "speech"),
462
+ (self.chat_checkbox, "chat"),
463
+ ))
464
+
465
+ def _restore_media(self, state: dict) -> None:
466
+ """還原圖片與音效的路徑(有圖才算準備好可以放)。"""
435
467
  if state.get("pet_image_path"):
436
468
  self.pet_image_path = state["pet_image_path"]
437
469
  self.ready_to_play = True
438
470
  self.ready_label.setText(language_wrapper.language_word_dict.get("Ready"))
439
- for combobox, key in ((self.size_combobox, "size"), (self.speed_combobox, "speed")):
440
- if state.get(key) is not None:
441
- index = combobox.findText(str(state[key]))
442
- if index >= 0:
443
- combobox.setCurrentIndex(index)
444
- behaviour = state.get("behaviour")
445
- if behaviour is None and "gravity" in state: # back-compat with old presets
446
- behaviour = BEHAVIOUR_FLOOR if state.get("gravity") else BEHAVIOUR_WANDER
447
- if behaviour is not None:
448
- index = self.behaviour_combobox.findData(behaviour)
449
- if index >= 0:
450
- self.behaviour_combobox.setCurrentIndex(index)
451
- if "climb" in state:
452
- self.climb_checkbox.setChecked(bool(state["climb"]))
453
- if "talk" in state:
454
- self.talk_checkbox.setChecked(bool(state["talk"]))
455
471
  if state.get("sound"):
456
472
  self.pet_sound_path = str(state["sound"])
457
- if "sit" in state:
458
- self.sit_checkbox.setChecked(bool(state["sit"]))
459
- if state.get("target_monitor") is not None:
460
- index = self.target_monitor_combobox.findText(str(state["target_monitor"]))
461
- if index >= 0:
462
- self.target_monitor_combobox.setCurrentIndex(index)
463
- if state.get("volume") is not None:
464
- index = self.volume_combobox.findText(str(state["volume"]))
465
- if index >= 0:
466
- self.volume_combobox.setCurrentIndex(index)
467
- if "audio_react" in state:
468
- self.audio_react_checkbox.setChecked(bool(state["audio_react"]))
469
- if "tag" in state:
470
- self.tag_checkbox.setChecked(bool(state["tag"]))
471
- if "speech" in state:
472
- self.speech_checkbox.setChecked(bool(state["speech"]))
473
- if "chat" in state:
474
- self.chat_checkbox.setChecked(bool(state["chat"]))
475
- for combobox, key in ((self.focus_minutes_combobox, "focus_minutes"),
476
- (self.break_minutes_combobox, "break_minutes")):
477
- if state.get(key) is not None:
478
- index = combobox.findText(str(state[key]))
479
- if index >= 0:
480
- combobox.setCurrentIndex(index)
473
+
474
+ @staticmethod
475
+ def _behaviour_from(state: dict):
476
+ """
477
+ 取出行為模式。舊的預設集只存了 gravity 布林值,這裡把它翻譯成對應的
478
+ 行為,免得舊檔案還原後寵物不會動。
479
+ The behaviour to use. Older presets only stored a `gravity` flag, so it
480
+ is translated here rather than leaving an old file with no behaviour.
481
+ """
482
+ behaviour = state.get("behaviour")
483
+ if behaviour is None and "gravity" in state:
484
+ behaviour = BEHAVIOUR_FLOOR if state.get("gravity") else BEHAVIOUR_WANDER
485
+ return behaviour
@@ -207,7 +207,7 @@ class PresentationSettingUI(QWidget):
207
207
  widget.clear()
208
208
 
209
209
  def _apply_annotation_settings(self) -> None:
210
- for widget in list(self.annotation_widget_list):
210
+ for widget in self.annotation_widget_list[:]:
211
211
  try:
212
212
  widget.set_tool(self.annotate_tool_combobox.currentData())
213
213
  widget.set_pen(self.annotate_color_combobox.currentData(),
@@ -244,7 +244,7 @@ class PresentationSettingUI(QWidget):
244
244
  language_wrapper.language_word_dict.get("cursor_effect_start", "Turn cursor effects on"))
245
245
 
246
246
  def _apply_cursor_settings(self) -> None:
247
- for widget in list(self.cursor_widget_list):
247
+ for widget in self.cursor_widget_list[:]:
248
248
  try:
249
249
  widget.set_effects(
250
250
  ring=self.cursor_ring_checkbox.isChecked(),
@@ -279,7 +279,7 @@ class PresentationSettingUI(QWidget):
279
279
  language_wrapper.language_word_dict.get("keystroke_start", "Show keystrokes"))
280
280
 
281
281
  def _on_key_pressed(self, keys) -> None:
282
- for widget in list(self.keystroke_widget_list):
282
+ for widget in self.keystroke_widget_list[:]:
283
283
  try:
284
284
  widget.push_keys(keys)
285
285
  except RuntimeError:
@@ -288,7 +288,7 @@ class PresentationSettingUI(QWidget):
288
288
  def _on_mouse_clicked(self, x: int, y: int) -> None:
289
289
  from PySide6.QtCore import QPoint
290
290
 
291
- for widget in list(self.cursor_widget_list):
291
+ for widget in self.cursor_widget_list[:]:
292
292
  try:
293
293
  widget.add_ripple(QPoint(int(x) - widget.x(), int(y) - widget.y()))
294
294
  except RuntimeError:
@@ -324,7 +324,7 @@ class PresentationSettingUI(QWidget):
324
324
  language_wrapper.language_word_dict.get("magnifier_start", "Turn magnifier on"))
325
325
 
326
326
  def _apply_magnifier_settings(self) -> None:
327
- for widget in list(self.magnifier_widget_list):
327
+ for widget in self.magnifier_widget_list[:]:
328
328
  try:
329
329
  widget.set_zoom(float(self.magnifier_zoom_combobox.currentText()))
330
330
  except RuntimeError:
@@ -208,7 +208,7 @@ class ScreenCareSettingUI(QWidget):
208
208
 
209
209
  def _apply_filter_settings(self) -> None:
210
210
  """滑桿或顏色改變時即時套用到已開啟的濾鏡。"""
211
- for widget in list(self.filter_widget_list):
211
+ for widget in self.filter_widget_list[:]:
212
212
  try:
213
213
  widget.set_filter(
214
214
  self.filter_color_combobox.currentData(), self.filter_strength_slider.value())
@@ -242,7 +242,7 @@ class ScreenCareSettingUI(QWidget):
242
242
  language_wrapper.language_word_dict.get("reading_ruler_start", "Turn ruler on"))
243
243
 
244
244
  def _apply_ruler_settings(self) -> None:
245
- for widget in list(self.ruler_widget_list):
245
+ for widget in self.ruler_widget_list[:]:
246
246
  try:
247
247
  widget.set_band(
248
248
  int(self.ruler_band_combobox.currentText()), self.ruler_strength_slider.value())
@@ -11,6 +11,11 @@ from frontengine.utils.logging.loggin_instance import front_engine_logger
11
11
  from frontengine.utils.multi_language.language_wrapper import language_wrapper
12
12
 
13
13
 
14
+ # 四處共用的語系鍵
15
+ # The language key shared by four call sites.
16
+ _NOT_READY = "Not Ready"
17
+
18
+
14
19
  class SoundPlayerSettingUI(QWidget):
15
20
  def __init__(self):
16
21
  front_engine_logger.info("[SoundPlayerSettingUI] Init")
@@ -36,13 +41,13 @@ class SoundPlayerSettingUI(QWidget):
36
41
  self.choose_wav_file_button = QPushButton(
37
42
  language_wrapper.language_word_dict.get("sound_player_setting_choose_wav_file"))
38
43
  self.choose_wav_file_button.clicked.connect(self.choose_and_copy_wav_file_to_cwd_sound_dir_then_play)
39
- self.wav_ready_label = QLabel(language_wrapper.language_word_dict.get("Not Ready"))
44
+ self.wav_ready_label = QLabel(language_wrapper.language_word_dict.get(_NOT_READY))
40
45
 
41
46
  # Choose general sound file
42
47
  self.choose_player_file_button = QPushButton(
43
48
  language_wrapper.language_word_dict.get("sound_player_setting_choose_sound_file"))
44
49
  self.choose_player_file_button.clicked.connect(self.choose_and_copy_sound_file_to_cwd_sound_dir_then_play)
45
- self.player_ready_label = QLabel(language_wrapper.language_word_dict.get("Not Ready"))
50
+ self.player_ready_label = QLabel(language_wrapper.language_word_dict.get(_NOT_READY))
46
51
 
47
52
  # Start buttons
48
53
  self.start_wav_button = QPushButton(language_wrapper.language_word_dict.get("sound_player_setting_play_wav"))
@@ -91,7 +96,7 @@ class SoundPlayerSettingUI(QWidget):
91
96
 
92
97
  def choose_and_copy_wav_file_to_cwd_sound_dir_then_play(self) -> None:
93
98
  front_engine_logger.info("[SoundPlayerSettingUI] choose_and_copy_wav_file_to_cwd_sound_dir_then_play")
94
- self.wav_ready_label.setText(language_wrapper.language_word_dict.get("Not Ready"))
99
+ self.wav_ready_label.setText(language_wrapper.language_word_dict.get(_NOT_READY))
95
100
  self.ready_to_play = False
96
101
  self.wav_sound_path = choose_wav_sound(self)
97
102
  if self.wav_sound_path:
@@ -100,7 +105,7 @@ class SoundPlayerSettingUI(QWidget):
100
105
 
101
106
  def choose_and_copy_sound_file_to_cwd_sound_dir_then_play(self) -> None:
102
107
  front_engine_logger.info("[SoundPlayerSettingUI] choose_and_copy_sound_file_to_cwd_sound_dir_then_play")
103
- self.player_ready_label.setText(language_wrapper.language_word_dict.get("Not Ready"))
108
+ self.player_ready_label.setText(language_wrapper.language_word_dict.get(_NOT_READY))
104
109
  self.ready_to_play = False
105
110
  self.player_sound_path = choose_player_sound(self)
106
111
  if self.player_sound_path: