frontengine 1.0.56__tar.gz → 1.0.58__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.
- {frontengine-1.0.56 → frontengine-1.0.58}/PKG-INFO +1 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/base_widget.py +28 -3
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/camera/camera_widget.py +10 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/canvas/whiteboard_widget.py +5 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/capture/region_capture.py +7 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/focus_shield/focus_shield_widget.py +16 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/gif/paint_gif.py +6 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/image/paint_image.py +7 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/load/load_someone_make_ui.py +34 -11
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/measure/measure_widget.py +14 -2
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/notes/sticky_note_widget.py +6 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/overlay_factory.py +36 -23
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/particle/particle_ui.py +24 -2
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/pet/desktop_pet.py +21 -3
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/presentation/annotation_overlay.py +8 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/presentation/cursor_effects.py +8 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/presentation/keystroke_display.py +14 -6
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/presentation/magnifier.py +17 -5
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/scene/scene.py +19 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/sound_player/sound_effect.py +4 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/toast/toast_widget.py +27 -4
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/video/video_player.py +5 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/system_tray/extend_system_tray.py +31 -38
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/choose_file_dialog.py +14 -2
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/hotkey_settings_dialog.py +8 -4
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/reminder_dialog.py +37 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/remote_control_dialog.py +23 -3
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/window_pin_dialog.py +16 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/main_ui.py +92 -26
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/menu/settings_menu.py +30 -7
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/control_center/control_center_ui.py +42 -16
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/focus/focus_setting_ui.py +9 -9
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/pet/pet_setting_ui.py +13 -8
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/presentation/presentation_setting_ui.py +21 -21
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_manager.py +67 -18
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_setting_ui.py +1 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +17 -17
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +12 -7
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/text/text_setting_ui.py +34 -7
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/tools/tools_setting_ui.py +24 -12
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +5 -5
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/web/web_setting_ui.py +12 -2
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/widgets/widgets_setting_ui.py +5 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/user_setting/user_setting_file.py +10 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/audio_meter/loopback_capture.py +25 -3
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/clipboard/clipboard_history.py +8 -1
- frontengine-1.0.58/frontengine/utils/critical_exit/critical_exit.py +97 -0
- frontengine-1.0.58/frontengine/utils/exception/exceptions.py +26 -0
- frontengine-1.0.58/frontengine/utils/json/json_file.py +61 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/json/json_repository.py +12 -1
- frontengine-1.0.58/frontengine/utils/logging/loggin_instance.py +80 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/midi/midi_input.py +10 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/english.py +4 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/france.py +4 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/germany.py +4 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/italy.py +4 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/russian.py +4 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/simplified_chinese.py +4 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/traditional_chinese.py +4 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/pet_chat/pet_chat_service.py +14 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/platform_info/platform_info.py +8 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/playlist/playlist.py +6 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/preset_schedule/preset_schedule_service.py +16 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/recording/gif_writer.py +46 -4
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/reminder/reminder_service.py +19 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/remote/remote_server.py +16 -2
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/screen_privacy/share_watch.py +15 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/text_source/text_source.py +5 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/weather/weather_service.py +21 -10
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/window_pin/window_layout.py +14 -3
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine.egg-info/PKG-INFO +1 -1
- {frontengine-1.0.56 → frontengine-1.0.58}/pyproject.toml +1 -1
- frontengine-1.0.56/frontengine/utils/critical_exit/critical_exit.py +0 -65
- frontengine-1.0.56/frontengine/utils/exception/exceptions.py +0 -18
- frontengine-1.0.56/frontengine/utils/json/json_file.py +0 -37
- frontengine-1.0.56/frontengine/utils/logging/loggin_instance.py +0 -46
- {frontengine-1.0.56 → frontengine-1.0.58}/LICENSE +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/README.md +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/__main__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/camera/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/canvas/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/capture/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/focus_shield/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/gif/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/image/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/load/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/measure/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/monitor/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/monitor/monitor_widget.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/notes/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/particle/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/pet/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/presentation/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/scene/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/scene/extend_graphic_scene.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/scene/extend_graphic_view.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/screen_care/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/screen_care/color_vision_widget.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/screen_care/screen_filter.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/sound_player/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/sound_player/sound_player.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/spectrum/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/spectrum/spectrum_widget.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/text/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/text/draw_text.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/toast/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/video/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/wallpaper/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/web/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/web/webview.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/window_helpers.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/system_tray/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/color/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/color/global_color.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/how_to_use_dialog.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/screen_privacy_dialog.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/screen_text_dialog.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/signage_dialog.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/menu/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/menu/help_menu.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/menu/how_to_menu.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/menu/language_menu.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/menu/preset_menu.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/control_center/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/focus/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/gif/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/image/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/image/image_setting_ui.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/particle/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/pet/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/presentation/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/screen_care/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/sound_player/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/text/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/tools/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/utils.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/video/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/video/video_setting_ui.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/wallpaper/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/web/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/ui/page/widgets/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/user_setting/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/user_setting/preset_repository.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/user_setting/scene_setting.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/app_profile/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/app_profile/app_profile_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/audio_meter/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/audio_meter/microphone_meter.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/audio_meter/screen_audio.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/autostart/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/autostart/autostart_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/break_reminder/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/break_reminder/break_reminder.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/browser/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/browser/browser.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/clipboard/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/color_vision/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/color_vision/color_vision.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/critical_exit/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/critical_exit/win32_vk.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/exception/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/exception/exception_tags.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/focus_shield/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/focus_shield/focus_shield.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/focus_timer/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/focus_timer/focus_timer.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/hotkey/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/hotkey/hotkey_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/input_watch/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/input_watch/input_watch_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/json/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/logging/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/measure/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/measure/measure.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/midi/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/language_wrapper.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/multi_language/retranslate.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/now_playing/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/now_playing/now_playing.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/pet_chat/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/platform_info/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/playlist/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/plugins/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/plugins/plugin_loader.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/power_mode/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/power_mode/power_mode.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/preset_schedule/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/recording/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/recording/frame_recorder.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/redirect_manager/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/reminder/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/remote/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/screen_privacy/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/screen_privacy/capture_affinity.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/screen_text/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/screen_text/screen_text_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/signage/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/signage/signage_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/smart_pause/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/smart_pause/pause_rules.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/system_stats/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/system_stats/system_stats.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/text_source/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/theme_schedule/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/usage_tracking/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/usage_tracking/usage_service.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/virtual_camera/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/virtual_camera/camera_feed.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/virtual_camera/virtual_camera.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/weather/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/web_url.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/window_pin/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/window_pin/window_pin.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/workshop/__init__.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine/utils/workshop/workshop_content.py +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine.egg-info/SOURCES.txt +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine.egg-info/dependency_links.txt +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine.egg-info/entry_points.txt +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine.egg-info/requires.txt +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/frontengine.egg-info/top_level.txt +0 -0
- {frontengine-1.0.56 → frontengine-1.0.58}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frontengine
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.58
|
|
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
|
|
@@ -26,6 +26,21 @@ class BaseWidget(QWidget):
|
|
|
26
26
|
# Locked means click-through; unlocked overlays can be dragged into place.
|
|
27
27
|
self.overlay_locked: bool = True
|
|
28
28
|
self.overlay_lockable: bool = True
|
|
29
|
+
# 自己處理滑鼠的覆蓋層(畫筆、白板、框選、量尺、寵物)要把這個關掉。
|
|
30
|
+
# 不關的話,使用者一在上面拖曳,基底的「拖曳擺位」就把整個視窗一起搬走:
|
|
31
|
+
# 畫布滑出畫面,框選還會擷取到跟選取範圍不同的區域。
|
|
32
|
+
# Overlays that handle the mouse themselves - pen, whiteboard, region
|
|
33
|
+
# select, ruler, pet - switch this off. Otherwise dragging to draw also
|
|
34
|
+
# drags the window: the canvas slides off screen, and region capture
|
|
35
|
+
# grabs an area other than the one that was selected.
|
|
36
|
+
self.overlay_draggable: bool = True
|
|
37
|
+
# 位置記憶是以類別名稱為鍵的,也就是「同一類共用一份」。每個實例位置
|
|
38
|
+
# 各異的(便利貼、寵物)要關掉,否則會全部被拉到同一個位置與大小。
|
|
39
|
+
# The remembered geometry is keyed by class name, i.e. shared by every
|
|
40
|
+
# instance of that kind. Overlays whose instances each have their own
|
|
41
|
+
# place - sticky notes, the pet - switch it off, or they all snap to one
|
|
42
|
+
# position and size.
|
|
43
|
+
self.overlay_remembers_geometry: bool = True
|
|
29
44
|
self.overlay_show_on_bottom: bool = False
|
|
30
45
|
# 綠幕背景色:設了就以不透明色填滿背景,方便 OBS 去背
|
|
31
46
|
# Chroma key colour: fills the background opaquely so OBS can key it out.
|
|
@@ -94,12 +109,13 @@ class BaseWidget(QWidget):
|
|
|
94
109
|
|
|
95
110
|
# --- drag to position (only while unlocked) --------------------------
|
|
96
111
|
def mousePressEvent(self, event) -> None:
|
|
97
|
-
if
|
|
112
|
+
if self.overlay_draggable and not self.overlay_locked \
|
|
113
|
+
and event.button() == Qt.MouseButton.LeftButton:
|
|
98
114
|
self._drag_origin = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
|
|
99
115
|
super().mousePressEvent(event)
|
|
100
116
|
|
|
101
117
|
def mouseMoveEvent(self, event) -> None:
|
|
102
|
-
if not self.overlay_locked and self._drag_origin is not None:
|
|
118
|
+
if self.overlay_draggable and not self.overlay_locked and self._drag_origin is not None:
|
|
103
119
|
self.move(event.globalPosition().toPoint() - self._drag_origin)
|
|
104
120
|
super().mouseMoveEvent(event)
|
|
105
121
|
|
|
@@ -111,6 +127,8 @@ class BaseWidget(QWidget):
|
|
|
111
127
|
|
|
112
128
|
def remember_geometry(self) -> None:
|
|
113
129
|
"""記住目前位置與大小,之後同類覆蓋層會開在同一處。"""
|
|
130
|
+
if not self.overlay_remembers_geometry:
|
|
131
|
+
return
|
|
114
132
|
geometry = self.geometry()
|
|
115
133
|
front_engine_logger.info(
|
|
116
134
|
f"{self.__class__.__name__} remember_geometry | {geometry.x()},{geometry.y()} "
|
|
@@ -129,12 +147,19 @@ class BaseWidget(QWidget):
|
|
|
129
147
|
|
|
130
148
|
def restore_saved_geometry(self) -> None:
|
|
131
149
|
"""若這類覆蓋層有記住的位置就套用(使用者拖曳過才會有)。"""
|
|
150
|
+
if not self.overlay_remembers_geometry:
|
|
151
|
+
return
|
|
132
152
|
saved = get_overlay_geometry(self.__class__.__name__)
|
|
133
153
|
if not saved:
|
|
134
154
|
return
|
|
135
155
|
try:
|
|
136
156
|
if self.isFullScreen():
|
|
137
|
-
|
|
157
|
+
# 使用者這次明講要全螢幕(含「顯示在所有螢幕」),就別拿記住的
|
|
158
|
+
# 位置把它拉回視窗模式——那會讓每台螢幕的覆蓋層全擠到同一格。
|
|
159
|
+
# The caller explicitly asked for fullscreen (this is also how
|
|
160
|
+
# "show on all screens" presents each one). Dragging it back to a
|
|
161
|
+
# remembered rect would pile every screen's overlay onto one spot.
|
|
162
|
+
return
|
|
138
163
|
self.setGeometry(*saved)
|
|
139
164
|
except RuntimeError: # pragma: no cover - widget closed mid-callback
|
|
140
165
|
return
|
|
@@ -138,6 +138,16 @@ class CameraWidget(BaseWidget):
|
|
|
138
138
|
self._camera.stop()
|
|
139
139
|
except RuntimeError: # pragma: no cover - already torn down
|
|
140
140
|
pass
|
|
141
|
+
# sink 的 parent 是這個 widget,所以只把 Python 參考設成 None 不會銷毀它。
|
|
142
|
+
# 每次重新 start()(例如換攝影機)都會多留一個還連著 _on_frame 的孤兒。
|
|
143
|
+
# The sink is parented to this widget, so dropping the Python reference
|
|
144
|
+
# does not destroy it: every restart - switching camera, say - leaves
|
|
145
|
+
# another orphan still connected to _on_frame.
|
|
146
|
+
if self._sink is not None:
|
|
147
|
+
try:
|
|
148
|
+
self._sink.deleteLater()
|
|
149
|
+
except RuntimeError: # pragma: no cover - already torn down
|
|
150
|
+
pass
|
|
141
151
|
self._camera = None
|
|
142
152
|
self._session = None
|
|
143
153
|
self._sink = None
|
|
@@ -79,6 +79,11 @@ class WhiteboardWidget(BaseWidget):
|
|
|
79
79
|
self.pen_width = max(1, int(width))
|
|
80
80
|
self.opacity = 1.0
|
|
81
81
|
self.overlay_lockable = False
|
|
82
|
+
# 白板自己處理拖曳(畫線與平移),基底的拖曳擺位會把整塊畫布搬走
|
|
83
|
+
# The whiteboard handles its own dragging - strokes and panning - and the
|
|
84
|
+
# base class's drag-to-position would carry the whole canvas off with it.
|
|
85
|
+
self.overlay_draggable = False
|
|
86
|
+
self.overlay_remembers_geometry = False
|
|
82
87
|
self.strokes: List[Dict[str, Any]] = []
|
|
83
88
|
self.zoom = 1.0
|
|
84
89
|
self.offset = [0.0, 0.0]
|
|
@@ -65,6 +65,13 @@ class RegionCaptureWidget(BaseWidget):
|
|
|
65
65
|
super().__init__()
|
|
66
66
|
self.opacity = 1.0
|
|
67
67
|
self.overlay_lockable = False
|
|
68
|
+
# 框選的拖曳就是選取範圍本身。讓基底也跟著搬視窗的話,事件座標會跟著
|
|
69
|
+
# 位移,最後擷取到的不是使用者框起來的那一塊。
|
|
70
|
+
# Here the drag is the selection. Letting the base class move the window
|
|
71
|
+
# too shifts the event coordinates, so what gets grabbed is not the area
|
|
72
|
+
# the user framed.
|
|
73
|
+
self.overlay_draggable = False
|
|
74
|
+
self.overlay_remembers_geometry = False
|
|
68
75
|
self.origin: Optional[QPoint] = None
|
|
69
76
|
self.current: Optional[QPoint] = None
|
|
70
77
|
self.captured: Optional[QPixmap] = None
|
{frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/focus_shield/focus_shield_widget.py
RENAMED
|
@@ -84,12 +84,28 @@ class DimBackgroundWidget(BaseWidget):
|
|
|
84
84
|
geometry = self.geometry()
|
|
85
85
|
return (geometry.x(), geometry.y(), geometry.width(), geometry.height())
|
|
86
86
|
|
|
87
|
+
def _to_logical(self, rect: Optional[Tuple[int, int, int, int]]):
|
|
88
|
+
"""
|
|
89
|
+
把 Win32 的視窗矩形換算成 Qt 的邏輯座標。GetWindowRect 給的是實體像素,
|
|
90
|
+
screen_rect() 給的是邏輯像素,顯示縮放不是 100% 時兩者對不起來:在 150%
|
|
91
|
+
的螢幕上,最大化的視窗會算成比整個螢幕還大,結果一塊都不壓暗。
|
|
92
|
+
Convert a Win32 window rect into Qt's logical coordinates. GetWindowRect
|
|
93
|
+
reports physical pixels while screen_rect() is logical, and the two
|
|
94
|
+
disagree at any scaling other than 100%: on a 150% display a maximised
|
|
95
|
+
window measures larger than the whole screen, so nothing gets dimmed.
|
|
96
|
+
"""
|
|
97
|
+
if rect is None:
|
|
98
|
+
return None
|
|
99
|
+
ratio = self.devicePixelRatio() or 1.0
|
|
100
|
+
return tuple(int(round(value / ratio)) for value in rect)
|
|
101
|
+
|
|
87
102
|
def refresh(self) -> None:
|
|
88
103
|
"""重新計算要壓暗的區塊。"""
|
|
89
104
|
try:
|
|
90
105
|
active = self._window_provider()
|
|
91
106
|
except Exception: # pragma: no cover - defensive around providers
|
|
92
107
|
active = None
|
|
108
|
+
active = self._to_logical(active)
|
|
93
109
|
rects = surrounding_rects(self.screen_rect(), active)
|
|
94
110
|
origin_x, origin_y = self.x(), self.y()
|
|
95
111
|
# 換算成本視窗的座標系 / Convert to this widget's own coordinates
|
|
@@ -39,6 +39,12 @@ class GifWidget(BaseWidget):
|
|
|
39
39
|
self.speed = speed
|
|
40
40
|
self.movie.setSpeed(self.speed)
|
|
41
41
|
|
|
42
|
+
def closeEvent(self, event) -> None:
|
|
43
|
+
# 關掉之後還在解碼就是白燒 CPU,畫面早就不見了
|
|
44
|
+
# A movie still decoding after the overlay is gone is pure wasted CPU.
|
|
45
|
+
self.movie.stop()
|
|
46
|
+
super().closeEvent(event)
|
|
47
|
+
|
|
42
48
|
def draw_content(self, painter: QPainter) -> None:
|
|
43
49
|
current_gif_frame = self.movie.currentPixmap()
|
|
44
50
|
painter.drawPixmap(
|
|
@@ -18,10 +18,14 @@ class ImageWidget(BaseWidget):
|
|
|
18
18
|
def __init__(self, image_path: str, draw_location_x: int = 0, draw_location_y: int = 0):
|
|
19
19
|
super().__init__(draw_location_x, draw_location_y)
|
|
20
20
|
self.image_path: Path = Path(image_path)
|
|
21
|
+
# 先給一張空圖,路徑失效時 paintEvent 才不會撞上未定義的屬性
|
|
22
|
+
# Start from an empty image so a bad path cannot leave paintEvent
|
|
23
|
+
# facing an attribute that was never assigned.
|
|
24
|
+
self.image: QImage = QImage()
|
|
21
25
|
|
|
22
26
|
if self.image_path.exists() and self.image_path.is_file():
|
|
23
27
|
front_engine_logger.info(f"Loading image file: {self.image_path}")
|
|
24
|
-
self.image
|
|
28
|
+
self.image = QImage(str(self.image_path))
|
|
25
29
|
self.resize(self.image.size())
|
|
26
30
|
else:
|
|
27
31
|
message_box: QMessageBox = QMessageBox(self)
|
|
@@ -45,6 +49,8 @@ class ImageWidget(BaseWidget):
|
|
|
45
49
|
return True
|
|
46
50
|
|
|
47
51
|
def draw_content(self, painter: QPainter) -> None:
|
|
52
|
+
if self.image.isNull():
|
|
53
|
+
return
|
|
48
54
|
painter.drawImage(
|
|
49
55
|
QRect(self.draw_location_x, self.draw_location_y, self.width(), self.height()),
|
|
50
56
|
self.image
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
from typing import Optional, List
|
|
2
2
|
|
|
3
|
-
from PySide6.
|
|
3
|
+
from PySide6.QtCore import Qt
|
|
4
|
+
from PySide6.QtGui import QGuiApplication
|
|
4
5
|
from PySide6.QtUiTools import QUiLoader
|
|
5
6
|
from PySide6.QtWidgets import QWidget
|
|
6
7
|
|
|
7
8
|
from frontengine.utils.exception.exceptions import FrontEngineLoadUIException
|
|
8
9
|
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
9
10
|
|
|
11
|
+
# 載入的視窗沒有 parent,Python 這邊不留參考就會立刻被回收,視窗根本不會出現。
|
|
12
|
+
# A loaded window has no parent, so without a reference on the Python side it is
|
|
13
|
+
# collected the moment this function returns and never appears at all.
|
|
14
|
+
_loaded_ui_widgets: List[QWidget] = []
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _forget_closed_ui() -> None:
|
|
18
|
+
"""把已被 Qt 銷毀的視窗從清單剔除,免得一直累積。"""
|
|
19
|
+
alive: List[QWidget] = []
|
|
20
|
+
for widget in _loaded_ui_widgets:
|
|
21
|
+
try:
|
|
22
|
+
widget.isVisible()
|
|
23
|
+
except RuntimeError: # 底層 C++ 物件已消滅 / underlying C++ object is gone
|
|
24
|
+
continue
|
|
25
|
+
alive.append(widget)
|
|
26
|
+
_loaded_ui_widgets[:] = alive
|
|
27
|
+
|
|
10
28
|
|
|
11
29
|
def load_extend_ui_file(ui_path: str, show_all_screen: bool = False) -> None:
|
|
12
30
|
"""
|
|
@@ -19,19 +37,24 @@ def load_extend_ui_file(ui_path: str, show_all_screen: bool = False) -> None:
|
|
|
19
37
|
front_engine_logger.info(
|
|
20
38
|
f"[load_extend_ui_file] ui_path={ui_path}, show_all_screen={show_all_screen}"
|
|
21
39
|
)
|
|
22
|
-
|
|
23
|
-
ui: QWidget = load_ui_file(ui_path)
|
|
40
|
+
_forget_closed_ui()
|
|
24
41
|
|
|
25
42
|
if show_all_screen:
|
|
26
|
-
|
|
43
|
+
# 一個 widget 不可能同時待在多台螢幕上,每台各載入一份
|
|
44
|
+
# One widget cannot sit on several screens at once, so load one per screen.
|
|
45
|
+
for screen in QGuiApplication.screens():
|
|
46
|
+
_present_ui(load_ui_file(ui_path), screen.availableGeometry().topLeft())
|
|
27
47
|
else:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
_present_ui(load_ui_file(ui_path), None)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _present_ui(ui: QWidget, top_left) -> None:
|
|
52
|
+
"""全螢幕顯示一個載入好的視窗,並保留參考直到它被關閉。"""
|
|
53
|
+
ui.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
54
|
+
if top_left is not None:
|
|
55
|
+
ui.move(top_left)
|
|
56
|
+
ui.showFullScreen()
|
|
57
|
+
_loaded_ui_widgets.append(ui)
|
|
35
58
|
|
|
36
59
|
|
|
37
60
|
def load_ui_file(ui_path: str) -> QWidget:
|
|
@@ -67,6 +67,10 @@ class MeasureWidget(BaseWidget):
|
|
|
67
67
|
self.color_format = color_format
|
|
68
68
|
self.opacity = 1.0
|
|
69
69
|
self.overlay_lockable = False
|
|
70
|
+
# 量測靠的是螢幕座標,覆蓋層一被拖走就量錯
|
|
71
|
+
# Measuring is in screen coordinates; dragging the overlay falsifies it.
|
|
72
|
+
self.overlay_draggable = False
|
|
73
|
+
self.overlay_remembers_geometry = False
|
|
70
74
|
self.points: List[QPoint] = []
|
|
71
75
|
self.cursor_point = QPoint(0, 0)
|
|
72
76
|
self.picked_color: Optional[QColor] = None
|
|
@@ -138,13 +142,21 @@ class MeasureWidget(BaseWidget):
|
|
|
138
142
|
self.readout = self.color_text(color)
|
|
139
143
|
self.update()
|
|
140
144
|
return self.readout
|
|
141
|
-
self.points.append(QPoint(point))
|
|
142
145
|
needed = 2 if self.mode == MODE_RULER else 3
|
|
146
|
+
if len(self.points) >= needed:
|
|
147
|
+
# 上一次已經量完了,這一下是新一次量測的第一點。先前的寫法是把新的
|
|
148
|
+
# 點加進去、拿舊的點算完再截掉,等於每一次新點擊都重算並重新複製
|
|
149
|
+
# 同一個舊結果——量尺變成只能用一次。
|
|
150
|
+
# The previous measurement is finished, so this click starts the next
|
|
151
|
+
# one. The old code appended the new point, measured the *old* ones
|
|
152
|
+
# and truncated the new one away, so every later click recomputed and
|
|
153
|
+
# re-copied the same stale result: the ruler worked exactly once.
|
|
154
|
+
self.points.clear()
|
|
155
|
+
self.points.append(QPoint(point))
|
|
143
156
|
if len(self.points) < needed:
|
|
144
157
|
self.update()
|
|
145
158
|
return None
|
|
146
159
|
self.readout = self._finish_measurement()
|
|
147
|
-
self.points = self.points[:needed]
|
|
148
160
|
self.update()
|
|
149
161
|
return self.readout
|
|
150
162
|
|
|
@@ -91,6 +91,12 @@ class StickyNoteWidget(BaseWidget):
|
|
|
91
91
|
# 要打字就不能點擊穿透,也就不提供鎖定
|
|
92
92
|
# Typing needs the mouse and keyboard, so this one is never lockable.
|
|
93
93
|
self.overlay_lockable = False
|
|
94
|
+
# 便利貼可以拖,但每張各有各的位置,所以不用「同類共用一份」的位置記憶。
|
|
95
|
+
# 每張的位置與大小是由 note_state() 各自存的。
|
|
96
|
+
# Notes are draggable, but each has its own place, so the class-wide
|
|
97
|
+
# geometry memory does not apply: note_state() persists each note's own
|
|
98
|
+
# position and size.
|
|
99
|
+
self.overlay_remembers_geometry = False
|
|
94
100
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False)
|
|
95
101
|
|
|
96
102
|
self.text_edit = QTextEdit(self)
|
|
@@ -11,7 +11,14 @@ from frontengine.show.video.video_player import VideoWidget
|
|
|
11
11
|
from frontengine.show.web.webview import WebWidget
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def
|
|
14
|
+
def _normalize_percent(value: Optional[Any], default: float) -> float:
|
|
15
|
+
"""
|
|
16
|
+
場景 JSON 裡的百分比欄位(不透明度、音量、播放速率)轉成 0.0-1.0。
|
|
17
|
+
沒填就用 default——default 已經是最終比例,不要再除以 100。
|
|
18
|
+
Turn a percentage field from the scene JSON (opacity, volume, play rate)
|
|
19
|
+
into a 0.0-1.0 ratio. A missing field falls back to `default`, which is
|
|
20
|
+
already the final ratio and must not be divided again.
|
|
21
|
+
"""
|
|
15
22
|
try:
|
|
16
23
|
return float(value) / 100 if value is not None else default
|
|
17
24
|
except (ValueError, TypeError):
|
|
@@ -25,11 +32,18 @@ def _normalize_int(value: Optional[Any], default: int) -> int:
|
|
|
25
32
|
return default
|
|
26
33
|
|
|
27
34
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
def _require(setting_dict: Dict[str, Any], key: str, kind: str) -> str:
|
|
36
|
+
"""
|
|
37
|
+
取出必要欄位。場景 JSON 是使用者自己編輯/外部提供的,缺欄位就明講,
|
|
38
|
+
不要把 None 一路送進 Path() 或 QWebEngineView.load() 才炸在深處。
|
|
39
|
+
Fetch a required field. Scene JSON is hand-edited or third-party, so say
|
|
40
|
+
what is missing here instead of letting None reach Path() or
|
|
41
|
+
QWebEngineView.load() and blow up somewhere deeper.
|
|
42
|
+
"""
|
|
43
|
+
value = setting_dict.get(key)
|
|
44
|
+
if not isinstance(value, str) or not value.strip():
|
|
45
|
+
raise ValueError(f"Scene entry of type '{kind}' is missing a valid '{key}'")
|
|
46
|
+
return value
|
|
33
47
|
|
|
34
48
|
|
|
35
49
|
class OverlayFactory(ABC):
|
|
@@ -46,31 +60,30 @@ class OverlayFactory(ABC):
|
|
|
46
60
|
|
|
47
61
|
class ImageOverlayFactory(OverlayFactory):
|
|
48
62
|
def create(self, setting_dict: Dict[str, Any]) -> QWidget:
|
|
49
|
-
widget = ImageWidget(setting_dict
|
|
50
|
-
widget.set_ui_variable(
|
|
63
|
+
widget = ImageWidget(_require(setting_dict, "file_path", "image"))
|
|
64
|
+
widget.set_ui_variable(_normalize_percent(setting_dict.get("opacity"), 0.2))
|
|
51
65
|
return widget
|
|
52
66
|
|
|
53
67
|
|
|
54
68
|
class GifOverlayFactory(OverlayFactory):
|
|
55
69
|
def create(self, setting_dict: Dict[str, Any]) -> QWidget:
|
|
56
|
-
widget = GifWidget(setting_dict
|
|
70
|
+
widget = GifWidget(_require(setting_dict, "file_path", "gif"))
|
|
57
71
|
widget.set_gif_variable(_normalize_int(setting_dict.get("speed"), 100))
|
|
58
|
-
widget.set_ui_variable(
|
|
72
|
+
widget.set_ui_variable(_normalize_percent(setting_dict.get("opacity"), 0.2))
|
|
59
73
|
return widget
|
|
60
74
|
|
|
61
75
|
|
|
62
76
|
class SoundOverlayFactory(OverlayFactory):
|
|
63
77
|
def create(self, setting_dict: Dict[str, Any]) -> QWidget:
|
|
64
|
-
widget = SoundPlayer(setting_dict
|
|
65
|
-
|
|
66
|
-
widget.set_player_variable(volume)
|
|
78
|
+
widget = SoundPlayer(_require(setting_dict, "file_path", "sound"))
|
|
79
|
+
widget.set_player_variable(_normalize_percent(setting_dict.get("volume"), 1.0))
|
|
67
80
|
return widget
|
|
68
81
|
|
|
69
82
|
|
|
70
83
|
class TextOverlayFactory(OverlayFactory):
|
|
71
84
|
def create(self, setting_dict: Dict[str, Any]) -> QWidget:
|
|
72
|
-
widget = TextWidget(setting_dict
|
|
73
|
-
widget.set_ui_variable(
|
|
85
|
+
widget = TextWidget(_require(setting_dict, "text", "text"))
|
|
86
|
+
widget.set_ui_variable(_normalize_percent(setting_dict.get("opacity"), 0.2))
|
|
74
87
|
widget.set_font_variable(_normalize_int(setting_dict.get("font_size"), 100))
|
|
75
88
|
widget.set_alignment(setting_dict.get("alignment", "Center"))
|
|
76
89
|
return widget
|
|
@@ -78,19 +91,19 @@ class TextOverlayFactory(OverlayFactory):
|
|
|
78
91
|
|
|
79
92
|
class VideoOverlayFactory(OverlayFactory):
|
|
80
93
|
def create(self, setting_dict: Dict[str, Any]) -> QWidget:
|
|
81
|
-
widget = VideoWidget(setting_dict
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
widget = VideoWidget(_require(setting_dict, "file_path", "video"))
|
|
95
|
+
widget.set_ui_variable(_normalize_percent(setting_dict.get("opacity"), 0.2))
|
|
96
|
+
widget.set_player_variable(
|
|
97
|
+
_normalize_percent(setting_dict.get("play_rate"), 1.0),
|
|
98
|
+
_normalize_percent(setting_dict.get("volume"), 1.0),
|
|
99
|
+
)
|
|
87
100
|
return widget
|
|
88
101
|
|
|
89
102
|
|
|
90
103
|
class WebOverlayFactory(OverlayFactory):
|
|
91
104
|
def create(self, setting_dict: Dict[str, Any]) -> QWidget:
|
|
92
|
-
widget = WebWidget(setting_dict
|
|
93
|
-
widget.set_ui_variable(
|
|
105
|
+
widget = WebWidget(_require(setting_dict, "url", "web"))
|
|
106
|
+
widget.set_ui_variable(_normalize_percent(setting_dict.get("opacity"), 0.2))
|
|
94
107
|
return widget
|
|
95
108
|
|
|
96
109
|
|
|
@@ -15,7 +15,7 @@ from OpenGL.GL import (
|
|
|
15
15
|
glTexCoord2f, glTexImage2D, glTexParameteri, glTranslatef, glVertex2f, glViewport,
|
|
16
16
|
)
|
|
17
17
|
from PySide6.QtCore import Qt, QTimer
|
|
18
|
-
from PySide6.QtGui import QSurfaceFormat, QPixmap
|
|
18
|
+
from PySide6.QtGui import QImage, QSurfaceFormat, QPixmap
|
|
19
19
|
from PySide6.QtOpenGLWidgets import QOpenGLWidget
|
|
20
20
|
|
|
21
21
|
from frontengine.show.window_helpers import apply_overlay_window_flags, load_overlay_icon
|
|
@@ -48,6 +48,7 @@ class ParticleOpenGLWidget(QOpenGLWidget):
|
|
|
48
48
|
|
|
49
49
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
|
50
50
|
self.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground)
|
|
51
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
51
52
|
self.setAutoFillBackground(False)
|
|
52
53
|
load_overlay_icon(self)
|
|
53
54
|
|
|
@@ -61,7 +62,13 @@ class ParticleOpenGLWidget(QOpenGLWidget):
|
|
|
61
62
|
Qt.AspectRatioMode.KeepAspectRatio,
|
|
62
63
|
Qt.TransformationMode.SmoothTransformation
|
|
63
64
|
)
|
|
64
|
-
|
|
65
|
+
# QPixmap.toImage() 給的是 ARGB32(記憶體順序 BGRA),直接當成 GL_RGBA 上傳
|
|
66
|
+
# 會把紅藍對調。轉成 RGBA8888 才和下面的 glTexImage2D 對得起來,
|
|
67
|
+
# 順帶去掉預乘 alpha,符合 GL_SRC_ALPHA 混色的預期。
|
|
68
|
+
# toImage() hands back ARGB32, i.e. BGRA in memory; uploading that as
|
|
69
|
+
# GL_RGBA swaps red and blue. RGBA8888 matches the glTexImage2D call
|
|
70
|
+
# below and is un-premultiplied, which is what GL_SRC_ALPHA expects.
|
|
71
|
+
self.image = scaled_pixmap.toImage().convertToFormat(QImage.Format.Format_RGBA8888)
|
|
65
72
|
|
|
66
73
|
self.particle_count = particle_count
|
|
67
74
|
self.particle_direction = particle_direction
|
|
@@ -99,6 +106,11 @@ class ParticleOpenGLWidget(QOpenGLWidget):
|
|
|
99
106
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
100
107
|
glEnable(GL_TEXTURE_2D)
|
|
101
108
|
|
|
109
|
+
if self.image.isNull():
|
|
110
|
+
# 沒有材質就別上傳 0x0 的貼圖,畫的時候直接跳過
|
|
111
|
+
# No texture: skip the 0x0 upload and let paintGL bail out.
|
|
112
|
+
return
|
|
113
|
+
|
|
102
114
|
w, h = self.image.width(), self.image.height()
|
|
103
115
|
|
|
104
116
|
self.texture_id = glGenTextures(1)
|
|
@@ -144,6 +156,9 @@ class ParticleOpenGLWidget(QOpenGLWidget):
|
|
|
144
156
|
glClearColor(0, 0, 0, 0)
|
|
145
157
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
|
146
158
|
|
|
159
|
+
if self.texture_id is None:
|
|
160
|
+
return
|
|
161
|
+
|
|
147
162
|
glBindTexture(GL_TEXTURE_2D, self.texture_id)
|
|
148
163
|
glColor4f(1, 1, 1, self.opacity)
|
|
149
164
|
|
|
@@ -175,3 +190,10 @@ class ParticleOpenGLWidget(QOpenGLWidget):
|
|
|
175
190
|
|
|
176
191
|
def set_ui_window_flag(self, show_on_bottom: bool = False) -> None:
|
|
177
192
|
apply_overlay_window_flags(self, show_on_bottom=show_on_bottom)
|
|
193
|
+
|
|
194
|
+
def closeEvent(self, event) -> None:
|
|
195
|
+
# 關掉之後還在跑 60Hz 更新上萬顆粒子,畫面卻早就沒了
|
|
196
|
+
# Without this the 60 Hz update keeps churning through every particle
|
|
197
|
+
# long after the overlay has gone from the screen.
|
|
198
|
+
self.timer.stop()
|
|
199
|
+
super().closeEvent(event)
|
|
@@ -1094,6 +1094,16 @@ class DesktopPetWidget(BaseWidget):
|
|
|
1094
1094
|
)
|
|
1095
1095
|
super().__init__()
|
|
1096
1096
|
self.opacity = 1.0
|
|
1097
|
+
# 寵物自己處理拖曳,位置與大小也由 PetMotion 決定。交給基底記憶的話,
|
|
1098
|
+
# 拖過一次之後那個「位置+大小」會套到之後每一隻寵物身上——尺寸下拉選單
|
|
1099
|
+
# 就等於失效:新寵物被悄悄縮回舊尺寸,碰撞判定卻還用新的大小。
|
|
1100
|
+
# The pet drags itself and PetMotion owns its geometry. Letting the base
|
|
1101
|
+
# class remember it means one drag pins that position and size onto every
|
|
1102
|
+
# later pet - which silently disables the size combobox: a new pet is
|
|
1103
|
+
# resized back while its collision box still uses the size that was asked
|
|
1104
|
+
# for.
|
|
1105
|
+
self.overlay_draggable = False
|
|
1106
|
+
self.overlay_remembers_geometry = False
|
|
1097
1107
|
# 基礎尺寸依已存等級放大 / Base size grown by the persisted level.
|
|
1098
1108
|
self._base_size: int = max(16, int(size))
|
|
1099
1109
|
self._growth = PetGrowth(user_setting_dict.get("pet_affection", 0))
|
|
@@ -1189,7 +1199,9 @@ class DesktopPetWidget(BaseWidget):
|
|
|
1189
1199
|
self.clone_action.triggered.connect(self.clone_requested.emit)
|
|
1190
1200
|
self.menu.addAction(self.clone_action)
|
|
1191
1201
|
self.feed_action = QAction(language_wrapper.language_word_dict.get("pet_feed", "Feed"), self)
|
|
1192
|
-
|
|
1202
|
+
# triggered 會送出 checked(bool),直接接的話 feed 收到的 kind 是 False
|
|
1203
|
+
# triggered emits checked(bool), which would arrive as feed's `kind`.
|
|
1204
|
+
self.feed_action.triggered.connect(lambda: self.feed())
|
|
1193
1205
|
self.menu.addAction(self.feed_action)
|
|
1194
1206
|
self.status_action = QAction(language_wrapper.language_word_dict.get("pet_status", "Status"), self)
|
|
1195
1207
|
self.status_action.triggered.connect(self.show_status)
|
|
@@ -1659,9 +1671,15 @@ class DesktopPetWidget(BaseWidget):
|
|
|
1659
1671
|
else:
|
|
1660
1672
|
self._pending_chat_reply = language_wrapper.language_word_dict.get(
|
|
1661
1673
|
"pet_chat_failed", "I couldn't reach my thoughts just now.")
|
|
1662
|
-
# 回覆可能在背景執行緒抵達,透過計時器回到 UI
|
|
1674
|
+
# 回覆可能在背景執行緒抵達,透過計時器回到 UI 執行緒再顯示。
|
|
1675
|
+
# 一定要傳 context 物件(這裡是 self):兩個參數的 singleShot 會把計時器
|
|
1676
|
+
# 建在呼叫端那條執行緒上,而那條執行緒沒有事件迴圈,計時器永遠不會觸發
|
|
1677
|
+
# ——寵物就一直停在「讓我想想…」,回覆再也不會出現。
|
|
1663
1678
|
# The reply may arrive off-thread; hop back to the UI thread to show it.
|
|
1664
|
-
|
|
1679
|
+
# The context object (self) is essential: the two-argument singleShot
|
|
1680
|
+
# creates the timer on the calling thread, which has no event loop, so it
|
|
1681
|
+
# never fires - the pet stays on "let me think..." and the answer is lost.
|
|
1682
|
+
QTimer.singleShot(0, self, self._say_pending_chat_reply)
|
|
1665
1683
|
|
|
1666
1684
|
def _say_pending_chat_reply(self) -> None:
|
|
1667
1685
|
if self._pending_chat_reply:
|
{frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/presentation/annotation_overlay.py
RENAMED
|
@@ -67,6 +67,14 @@ class AnnotationOverlay(BaseWidget):
|
|
|
67
67
|
front_engine_logger.info(f"[AnnotationOverlay] Init | tool={tool}, color={color}, width={width}")
|
|
68
68
|
super().__init__()
|
|
69
69
|
self.opacity = 1.0
|
|
70
|
+
# 這一層的滑鼠是拿來畫畫的:不能被基底拖走,也不能被「鎖定覆蓋層」
|
|
71
|
+
# 變成點擊穿透——那會讓畫筆整個失效,而畫好的線還留在畫面上。
|
|
72
|
+
# The mouse here is the pen: the base class must not drag the window with
|
|
73
|
+
# it, and "lock overlays" must not make it click-through - that kills the
|
|
74
|
+
# pen outright while the strokes stay frozen on screen.
|
|
75
|
+
self.overlay_lockable = False
|
|
76
|
+
self.overlay_draggable = False
|
|
77
|
+
self.overlay_remembers_geometry = False
|
|
70
78
|
self.tool = tool if tool in TOOLS else TOOL_PEN
|
|
71
79
|
self.pen_color = color
|
|
72
80
|
self.pen_width = clamp_width(width)
|
|
@@ -111,7 +111,14 @@ class CursorEffectWidget(BaseWidget):
|
|
|
111
111
|
|
|
112
112
|
def add_ripple(self, point: Optional[QPoint] = None) -> None:
|
|
113
113
|
"""在指定位置(預設為目前游標)加一圈點擊漣漪。"""
|
|
114
|
-
|
|
114
|
+
# QPoint 的真值判斷是 isNull(),所以 bool(QPoint(0, 0)) 是 False:
|
|
115
|
+
# 用 `point or ...` 的話,剛好點在覆蓋層左上角的那一下會被當成沒給座標,
|
|
116
|
+
# 漣漪畫到上一次輪詢到的游標位置去。
|
|
117
|
+
# QPoint's truthiness is isNull(), so bool(QPoint(0, 0)) is False: with
|
|
118
|
+
# `point or ...` a click exactly on the overlay's top-left corner reads as
|
|
119
|
+
# "no point given" and the ripple lands on the last polled cursor spot.
|
|
120
|
+
origin = self.cursor_point if point is None else point
|
|
121
|
+
self.ripples.append({"point": QPoint(origin), "step": 0})
|
|
115
122
|
self.update()
|
|
116
123
|
|
|
117
124
|
def tick(self) -> None:
|
{frontengine-1.0.56 → frontengine-1.0.58}/frontengine/show/presentation/keystroke_display.py
RENAMED
|
@@ -128,12 +128,20 @@ class KeystrokeDisplayWidget(BaseWidget):
|
|
|
128
128
|
return " ".join(visible_keys(self.entries, self._now(), self.hold_seconds))
|
|
129
129
|
|
|
130
130
|
def _expire(self) -> None:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
# 拿還沒過期的「全部」數量來比,不要用 visible_keys——它只回傳最多
|
|
132
|
+
# MAX_KEYS_SHOWN 筆,一旦按超過那個數量,這個比較就永遠不相等,
|
|
133
|
+
# 每 100ms 都會重建清單並重畫一次,其實什麼都沒過期。
|
|
134
|
+
# Compare against the full count of unexpired entries, not visible_keys:
|
|
135
|
+
# that returns at most MAX_KEYS_SHOWN, so past that many keys the test is
|
|
136
|
+
# never equal and every 100 ms tick rebuilds the list and repaints with
|
|
137
|
+
# nothing actually having expired.
|
|
138
|
+
now = self._now()
|
|
139
|
+
fresh = [
|
|
140
|
+
(text, stamp) for text, stamp in self.entries
|
|
141
|
+
if now - stamp <= self.hold_seconds
|
|
142
|
+
]
|
|
143
|
+
if len(fresh) != len(self.entries):
|
|
144
|
+
self.entries = fresh
|
|
137
145
|
self.update()
|
|
138
146
|
|
|
139
147
|
def draw_content(self, painter: QPainter) -> None:
|
|
@@ -86,14 +86,26 @@ class MagnifierWidget(BaseWidget):
|
|
|
86
86
|
|
|
87
87
|
@staticmethod
|
|
88
88
|
def _grab_screen(rect: QRect) -> Optional[QPixmap]:
|
|
89
|
-
"""
|
|
90
|
-
|
|
89
|
+
"""
|
|
90
|
+
擷取指定的螢幕區域。要用「那塊區域所在的那台螢幕」去擷取,不是主螢幕:
|
|
91
|
+
放大鏡可以被指定開在副螢幕上,寫死主螢幕的話,游標移到另一台時擷取到的
|
|
92
|
+
是完全不相干的位置。
|
|
93
|
+
Grab a screen region using the screen that region is actually on, not the
|
|
94
|
+
primary one: the magnifier can be placed on a secondary display, and
|
|
95
|
+
hard-coding the primary grabs somewhere else entirely once the cursor
|
|
96
|
+
moves across.
|
|
97
|
+
"""
|
|
98
|
+
screen = QGuiApplication.screenAt(rect.center()) or QGuiApplication.primaryScreen()
|
|
91
99
|
if screen is None:
|
|
92
100
|
return None
|
|
93
101
|
return screen.grabWindow(0, rect.x(), rect.y(), rect.width(), rect.height())
|
|
94
102
|
|
|
95
|
-
def screen_bounds(self) -> Tuple[int, int, int, int]:
|
|
96
|
-
|
|
103
|
+
def screen_bounds(self, position=None) -> Tuple[int, int, int, int]:
|
|
104
|
+
"""游標所在螢幕的範圍(沒給座標就用放大鏡自己那一台)。"""
|
|
105
|
+
screen = None
|
|
106
|
+
if position is not None:
|
|
107
|
+
screen = QGuiApplication.screenAt(position)
|
|
108
|
+
screen = screen or self.screen() or QGuiApplication.primaryScreen()
|
|
97
109
|
if screen is None:
|
|
98
110
|
return (0, 0, 1920, 1080)
|
|
99
111
|
geometry = screen.geometry()
|
|
@@ -104,7 +116,7 @@ class MagnifierWidget(BaseWidget):
|
|
|
104
116
|
try:
|
|
105
117
|
position = self._cursor_provider()
|
|
106
118
|
rect = source_rect((position.x(), position.y()), self.view_size, self.zoom,
|
|
107
|
-
self.screen_bounds())
|
|
119
|
+
self.screen_bounds(position))
|
|
108
120
|
self.captured = self._grabber(rect)
|
|
109
121
|
except Exception as error: # pragma: no cover - capture boundary
|
|
110
122
|
front_engine_logger.debug(f"[MagnifierWidget] capture failed: {error!r}")
|