frontengine 1.0.38__tar.gz → 1.0.40__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.38 → frontengine-1.0.40}/PKG-INFO +31 -1
- {frontengine-1.0.38 → frontengine-1.0.40}/README.md +30 -0
- frontengine-1.0.40/frontengine/show/gaming/crosshair_widget.py +160 -0
- frontengine-1.0.40/frontengine/show/teleprompter/teleprompter_widget.py +171 -0
- frontengine-1.0.40/frontengine/ui/dialog/screen_text_dialog.py +120 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/main_ui.py +8 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +1 -1
- frontengine-1.0.40/frontengine/ui/page/stream/stream_setting_ui.py +351 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/tools/tools_setting_ui.py +216 -20
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/user_setting/user_setting_file.py +9 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/multi_language/english.py +43 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/multi_language/traditional_chinese.py +43 -0
- frontengine-1.0.40/frontengine/utils/obs/obs_client.py +148 -0
- frontengine-1.0.40/frontengine/utils/obs/obs_protocol.py +140 -0
- frontengine-1.0.40/frontengine/utils/obs/websocket_frames.py +147 -0
- frontengine-1.0.40/frontengine/utils/recording/frame_recorder.py +199 -0
- frontengine-1.0.40/frontengine/utils/recording/gif_writer.py +203 -0
- frontengine-1.0.40/frontengine/utils/soundboard/soundboard.py +172 -0
- frontengine-1.0.40/frontengine/utils/theme_schedule/__init__.py +0 -0
- frontengine-1.0.40/frontengine/utils/usage_tracking/__init__.py +0 -0
- frontengine-1.0.40/frontengine/utils/weather/__init__.py +0 -0
- frontengine-1.0.40/frontengine/utils/window_pin/__init__.py +0 -0
- frontengine-1.0.40/frontengine/utils/workshop/__init__.py +0 -0
- frontengine-1.0.40/frontengine/worker/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine.egg-info/PKG-INFO +31 -1
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine.egg-info/SOURCES.txt +16 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/pyproject.toml +1 -1
- {frontengine-1.0.38 → frontengine-1.0.40}/LICENSE +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/__main__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/base_widget.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/camera/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/camera/camera_widget.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/capture/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/capture/region_capture.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/focus_shield/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
- {frontengine-1.0.38/frontengine/show/gif → frontengine-1.0.40/frontengine/show/gaming}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/show/image → frontengine-1.0.40/frontengine/show/gif}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/gif/paint_gif.py +0 -0
- {frontengine-1.0.38/frontengine/show/load → frontengine-1.0.40/frontengine/show/image}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/image/paint_image.py +0 -0
- {frontengine-1.0.38/frontengine/show/measure → frontengine-1.0.40/frontengine/show/load}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/load/load_someone_make_ui.py +0 -0
- {frontengine-1.0.38/frontengine/show/monitor → frontengine-1.0.40/frontengine/show/measure}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/measure/measure_widget.py +0 -0
- {frontengine-1.0.38/frontengine/show/notes → frontengine-1.0.40/frontengine/show/monitor}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/monitor/monitor_widget.py +0 -0
- {frontengine-1.0.38/frontengine/show/particle → frontengine-1.0.40/frontengine/show/notes}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/notes/sticky_note_widget.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/overlay_factory.py +0 -0
- {frontengine-1.0.38/frontengine/show/pet → frontengine-1.0.40/frontengine/show/particle}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/particle/particle_ui.py +0 -0
- {frontengine-1.0.38/frontengine/show/presentation → frontengine-1.0.40/frontengine/show/pet}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/pet/desktop_pet.py +0 -0
- {frontengine-1.0.38/frontengine/show/scene → frontengine-1.0.40/frontengine/show/presentation}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/presentation/annotation_overlay.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/presentation/cursor_effects.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/presentation/keystroke_display.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/presentation/magnifier.py +0 -0
- {frontengine-1.0.38/frontengine/show/screen_care → frontengine-1.0.40/frontengine/show/scene}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/scene/extend_graphic_scene.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/scene/extend_graphic_view.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/scene/scene.py +0 -0
- {frontengine-1.0.38/frontengine/show/sound_player → frontengine-1.0.40/frontengine/show/screen_care}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/screen_care/color_vision_widget.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/screen_care/screen_filter.py +0 -0
- {frontengine-1.0.38/frontengine/show/spectrum → frontengine-1.0.40/frontengine/show/sound_player}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/sound_player/sound_effect.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/sound_player/sound_player.py +0 -0
- {frontengine-1.0.38/frontengine/show/text → frontengine-1.0.40/frontengine/show/spectrum}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/spectrum/spectrum_widget.py +0 -0
- {frontengine-1.0.38/frontengine/show/toast → frontengine-1.0.40/frontengine/show/teleprompter}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/show/video → frontengine-1.0.40/frontengine/show/text}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/text/draw_text.py +0 -0
- {frontengine-1.0.38/frontengine/show/wallpaper → frontengine-1.0.40/frontengine/show/toast}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/toast/toast_widget.py +0 -0
- {frontengine-1.0.38/frontengine/show/web → frontengine-1.0.40/frontengine/show/video}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/video/video_player.py +0 -0
- {frontengine-1.0.38/frontengine/system_tray → frontengine-1.0.40/frontengine/show/wallpaper}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
- {frontengine-1.0.38/frontengine/ui → frontengine-1.0.40/frontengine/show/web}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/web/webview.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/show/window_helpers.py +0 -0
- {frontengine-1.0.38/frontengine/ui/color → frontengine-1.0.40/frontengine/system_tray}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/system_tray/extend_system_tray.py +0 -0
- {frontengine-1.0.38/frontengine/ui/dialog → frontengine-1.0.40/frontengine/ui}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/ui/menu → frontengine-1.0.40/frontengine/ui/color}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/color/global_color.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page → frontengine-1.0.40/frontengine/ui/dialog}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/reminder_dialog.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/save_file_dialog.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/control_center → frontengine-1.0.40/frontengine/ui/menu}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/menu/help_menu.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/menu/how_to_menu.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/menu/language_menu.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/menu/preset_menu.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/menu/settings_menu.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/focus → frontengine-1.0.40/frontengine/ui/page}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/gif → frontengine-1.0.40/frontengine/ui/page/control_center}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/image → frontengine-1.0.40/frontengine/ui/page/focus}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/particle → frontengine-1.0.40/frontengine/ui/page/gif}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/pet → frontengine-1.0.40/frontengine/ui/page/image}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/image/image_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/presentation → frontengine-1.0.40/frontengine/ui/page/particle}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/scene_setting → frontengine-1.0.40/frontengine/ui/page/pet}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/scene_setting/scene_page → frontengine-1.0.40/frontengine/ui/page/presentation}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/screen_care → frontengine-1.0.40/frontengine/ui/page/scene_setting}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/sound_player → frontengine-1.0.40/frontengine/ui/page/scene_setting/scene_page}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/text → frontengine-1.0.40/frontengine/ui/page/screen_care}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/tools → frontengine-1.0.40/frontengine/ui/page/sound_player}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/video → frontengine-1.0.40/frontengine/ui/page/stream}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/wallpaper → frontengine-1.0.40/frontengine/ui/page/text}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/text/text_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/web → frontengine-1.0.40/frontengine/ui/page/tools}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/utils.py +0 -0
- {frontengine-1.0.38/frontengine/ui/page/widgets → frontengine-1.0.40/frontengine/ui/page/video}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/video/video_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/user_setting → frontengine-1.0.40/frontengine/ui/page/wallpaper}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/utils → frontengine-1.0.40/frontengine/ui/page/web}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/web/web_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/utils/app_profile → frontengine-1.0.40/frontengine/ui/page/widgets}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
- {frontengine-1.0.38/frontengine/utils/audio_meter → frontengine-1.0.40/frontengine/user_setting}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/user_setting/preset_repository.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/user_setting/scene_setting.py +0 -0
- {frontengine-1.0.38/frontengine/utils/autostart → frontengine-1.0.40/frontengine/utils}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/utils/break_reminder → frontengine-1.0.40/frontengine/utils/app_profile}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/app_profile/app_profile_service.py +0 -0
- {frontengine-1.0.38/frontengine/utils/browser → frontengine-1.0.40/frontengine/utils/audio_meter}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/audio_meter/screen_audio.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
- {frontengine-1.0.38/frontengine/utils/clipboard → frontengine-1.0.40/frontengine/utils/autostart}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/autostart/autostart_service.py +0 -0
- {frontengine-1.0.38/frontengine/utils/color_vision → frontengine-1.0.40/frontengine/utils/break_reminder}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/break_reminder/break_reminder.py +0 -0
- {frontengine-1.0.38/frontengine/utils/critical_exit → frontengine-1.0.40/frontengine/utils/browser}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/browser/browser.py +0 -0
- {frontengine-1.0.38/frontengine/utils/file → frontengine-1.0.40/frontengine/utils/clipboard}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/clipboard/clipboard_history.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
- {frontengine-1.0.38/frontengine/utils/focus_shield → frontengine-1.0.40/frontengine/utils/color_vision}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/color_vision/color_vision.py +0 -0
- {frontengine-1.0.38/frontengine/utils/focus_timer → frontengine-1.0.40/frontengine/utils/critical_exit}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/critical_exit/critical_exit.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/critical_exit/win32_vk.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/exception/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/exception/exception_tags.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/exception/exceptions.py +0 -0
- {frontengine-1.0.38/frontengine/utils/hotkey → frontengine-1.0.40/frontengine/utils/file}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/file/open/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/file/open/open_file.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/file/save/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/file/save/save_file.py +0 -0
- {frontengine-1.0.38/frontengine/utils/input_watch → frontengine-1.0.40/frontengine/utils/focus_shield}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/focus_shield/focus_shield.py +0 -0
- {frontengine-1.0.38/frontengine/utils/json → frontengine-1.0.40/frontengine/utils/focus_timer}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/focus_timer/focus_timer.py +0 -0
- {frontengine-1.0.38/frontengine/utils/logging → frontengine-1.0.40/frontengine/utils/hotkey}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/hotkey/hotkey_service.py +0 -0
- {frontengine-1.0.38/frontengine/utils/measure → frontengine-1.0.40/frontengine/utils/input_watch}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/input_watch/input_watch_service.py +0 -0
- {frontengine-1.0.38/frontengine/utils/multi_language → frontengine-1.0.40/frontengine/utils/json}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/json/json_file.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/json/json_repository.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/json_format/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/json_format/json_process.py +0 -0
- {frontengine-1.0.38/frontengine/utils/now_playing → frontengine-1.0.40/frontengine/utils/logging}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/logging/loggin_instance.py +0 -0
- {frontengine-1.0.38/frontengine/utils/pet_chat → frontengine-1.0.40/frontengine/utils/measure}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/measure/measure.py +0 -0
- {frontengine-1.0.38/frontengine/utils/platform_info → frontengine-1.0.40/frontengine/utils/multi_language}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/multi_language/france.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/multi_language/germany.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/multi_language/italy.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/multi_language/language_wrapper.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/multi_language/russian.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
- {frontengine-1.0.38/frontengine/utils/playlist → frontengine-1.0.40/frontengine/utils/now_playing}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/now_playing/now_playing.py +0 -0
- {frontengine-1.0.38/frontengine/utils/plugins → frontengine-1.0.40/frontengine/utils/obs}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/utils/power_mode → frontengine-1.0.40/frontengine/utils/pet_chat}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
- {frontengine-1.0.38/frontengine/utils/preset_schedule → frontengine-1.0.40/frontengine/utils/platform_info}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/platform_info/platform_info.py +0 -0
- {frontengine-1.0.38/frontengine/utils/redirect_manager → frontengine-1.0.40/frontengine/utils/playlist}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/playlist/playlist.py +0 -0
- {frontengine-1.0.38/frontengine/utils/reminder → frontengine-1.0.40/frontengine/utils/plugins}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/plugins/plugin_loader.py +0 -0
- {frontengine-1.0.38/frontengine/utils/smart_pause → frontengine-1.0.40/frontengine/utils/power_mode}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/power_mode/power_mode.py +0 -0
- {frontengine-1.0.38/frontengine/utils/system_stats → frontengine-1.0.40/frontengine/utils/preset_schedule}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
- {frontengine-1.0.38/frontengine/utils/text_source → frontengine-1.0.40/frontengine/utils/recording}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/utils/theme_schedule → frontengine-1.0.40/frontengine/utils/redirect_manager}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
- {frontengine-1.0.38/frontengine/utils/usage_tracking → frontengine-1.0.40/frontengine/utils/reminder}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/reminder/reminder_service.py +0 -0
- {frontengine-1.0.38/frontengine/utils/weather → frontengine-1.0.40/frontengine/utils/smart_pause}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/smart_pause/pause_rules.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
- {frontengine-1.0.38/frontengine/utils/window_pin → frontengine-1.0.40/frontengine/utils/soundboard}/__init__.py +0 -0
- {frontengine-1.0.38/frontengine/utils/workshop → frontengine-1.0.40/frontengine/utils/system_stats}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/system_stats/system_stats.py +0 -0
- {frontengine-1.0.38/frontengine/worker → frontengine-1.0.40/frontengine/utils/text_source}/__init__.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/text_source/text_source.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/usage_tracking/usage_service.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/weather/weather_service.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/web_url.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/window_pin/window_layout.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/window_pin/window_pin.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/utils/workshop/workshop_content.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine/worker/qthread_worker.py +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine.egg-info/dependency_links.txt +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine.egg-info/entry_points.txt +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine.egg-info/requires.txt +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/frontengine.egg-info/top_level.txt +0 -0
- {frontengine-1.0.38 → frontengine-1.0.40}/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.40
|
|
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
|
|
@@ -143,6 +143,17 @@ Measuring and capture tools on the **Tools** tab:
|
|
|
143
143
|
`hsl(...)` or a CSS custom property.
|
|
144
144
|
- **Region capture** — drag out an area; it lands on the clipboard and can be
|
|
145
145
|
saved to a file.
|
|
146
|
+
- **Read text** — drag out an area to copy the text in it, translate it, or ask
|
|
147
|
+
a question about it.
|
|
148
|
+
> This is the **only** feature here that sends screen content off the
|
|
149
|
+
> machine: the selection goes to Anthropic's API using your own
|
|
150
|
+
> `ANTHROPIC_API_KEY`. It asks once before the first send, remembers the
|
|
151
|
+
> answer, and the consent can be withdrawn from the result window at any
|
|
152
|
+
> time. Without consent or without a key, nothing is sent.
|
|
153
|
+
- **Record area** — drag out an area and record it to an animated GIF, with the
|
|
154
|
+
camera composited into the corner if you want the reaction-video look.
|
|
155
|
+
Recording is capped by both length and frame count, because every frame is
|
|
156
|
+
held in memory.
|
|
146
157
|
- **Pin a window** — keep another program's window on top, or fade it, while
|
|
147
158
|
you work against it. Only stacking and opacity are touched, never window
|
|
148
159
|
content. Windows only.
|
|
@@ -166,6 +177,25 @@ that caps every overlay's refresh rate and drops its render resolution.
|
|
|
166
177
|
|
|
167
178
|
---
|
|
168
179
|
|
|
180
|
+
## Stream and gaming
|
|
181
|
+
|
|
182
|
+
Four things on the **Stream** tab, for playing and for going live:
|
|
183
|
+
|
|
184
|
+
- **Crosshair** — a cross, dot, circle or T shape in the middle of the screen,
|
|
185
|
+
in your colour and size. It is **only drawn on top**: nothing is injected,
|
|
186
|
+
no memory is read, no game file is touched. An exclusive-fullscreen game will
|
|
187
|
+
cover it, which is how the OS stacks windows.
|
|
188
|
+
- **Teleprompter** — scroll a script at an adjustable speed and size, with a
|
|
189
|
+
mirror option for prompter glass. It stops at the last line rather than
|
|
190
|
+
snapping back mid-read.
|
|
191
|
+
- **Soundboard** — up to twelve slots, each with a sound and an optional
|
|
192
|
+
hotkey. A file that has gone missing is dropped rather than kept as a dead
|
|
193
|
+
button.
|
|
194
|
+
- **OBS** — switch scenes and toggle recording or streaming through OBS's own
|
|
195
|
+
WebSocket (built in since OBS 28). The password stays in your local settings.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
169
199
|
## Screen time, clipboard and layouts
|
|
170
200
|
|
|
171
201
|
Three things that remember, all of them **off until you switch them on** and
|
|
@@ -119,6 +119,17 @@ Measuring and capture tools on the **Tools** tab:
|
|
|
119
119
|
`hsl(...)` or a CSS custom property.
|
|
120
120
|
- **Region capture** — drag out an area; it lands on the clipboard and can be
|
|
121
121
|
saved to a file.
|
|
122
|
+
- **Read text** — drag out an area to copy the text in it, translate it, or ask
|
|
123
|
+
a question about it.
|
|
124
|
+
> This is the **only** feature here that sends screen content off the
|
|
125
|
+
> machine: the selection goes to Anthropic's API using your own
|
|
126
|
+
> `ANTHROPIC_API_KEY`. It asks once before the first send, remembers the
|
|
127
|
+
> answer, and the consent can be withdrawn from the result window at any
|
|
128
|
+
> time. Without consent or without a key, nothing is sent.
|
|
129
|
+
- **Record area** — drag out an area and record it to an animated GIF, with the
|
|
130
|
+
camera composited into the corner if you want the reaction-video look.
|
|
131
|
+
Recording is capped by both length and frame count, because every frame is
|
|
132
|
+
held in memory.
|
|
122
133
|
- **Pin a window** — keep another program's window on top, or fade it, while
|
|
123
134
|
you work against it. Only stacking and opacity are touched, never window
|
|
124
135
|
content. Windows only.
|
|
@@ -142,6 +153,25 @@ that caps every overlay's refresh rate and drops its render resolution.
|
|
|
142
153
|
|
|
143
154
|
---
|
|
144
155
|
|
|
156
|
+
## Stream and gaming
|
|
157
|
+
|
|
158
|
+
Four things on the **Stream** tab, for playing and for going live:
|
|
159
|
+
|
|
160
|
+
- **Crosshair** — a cross, dot, circle or T shape in the middle of the screen,
|
|
161
|
+
in your colour and size. It is **only drawn on top**: nothing is injected,
|
|
162
|
+
no memory is read, no game file is touched. An exclusive-fullscreen game will
|
|
163
|
+
cover it, which is how the OS stacks windows.
|
|
164
|
+
- **Teleprompter** — scroll a script at an adjustable speed and size, with a
|
|
165
|
+
mirror option for prompter glass. It stops at the last line rather than
|
|
166
|
+
snapping back mid-read.
|
|
167
|
+
- **Soundboard** — up to twelve slots, each with a sound and an optional
|
|
168
|
+
hotkey. A file that has gone missing is dropped rather than kept as a dead
|
|
169
|
+
button.
|
|
170
|
+
- **OBS** — switch scenes and toggle recording or streaming through OBS's own
|
|
171
|
+
WebSocket (built in since OBS 28). The password stays in your local settings.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
145
175
|
## Screen time, clipboard and layouts
|
|
146
176
|
|
|
147
177
|
Three things that remember, all of them **off until you switch them on** and
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
準星覆蓋層:在畫面正中央畫一個自訂準星,點擊穿透,不動到任何遊戲檔案。
|
|
3
|
+
|
|
4
|
+
這是純粹的疊圖——不注入遊戲、不讀取記憶體、不改動遊戲的任何東西。全螢幕
|
|
5
|
+
獨佔模式的遊戲會蓋在它上面,那是作業系統的堆疊規則,不是這裡能繞過的。
|
|
6
|
+
|
|
7
|
+
A crosshair overlay: draw a custom crosshair in the middle of the screen,
|
|
8
|
+
click-through, touching no game files.
|
|
9
|
+
|
|
10
|
+
It is only a layer on top - nothing is injected, no memory is read, no game is
|
|
11
|
+
modified. An exclusive-fullscreen game will cover it, which is how the OS
|
|
12
|
+
stacks windows and not something to work around.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Tuple
|
|
17
|
+
|
|
18
|
+
from PySide6.QtCore import QPointF, Qt
|
|
19
|
+
from PySide6.QtGui import QColor, QPainter, QPen
|
|
20
|
+
|
|
21
|
+
from frontengine.show.base_widget import BaseWidget
|
|
22
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
23
|
+
|
|
24
|
+
STYLE_CROSS = "cross"
|
|
25
|
+
STYLE_DOT = "dot"
|
|
26
|
+
STYLE_CIRCLE = "circle"
|
|
27
|
+
STYLE_T_SHAPE = "t"
|
|
28
|
+
STYLES = (STYLE_CROSS, STYLE_DOT, STYLE_CIRCLE, STYLE_T_SHAPE)
|
|
29
|
+
|
|
30
|
+
DEFAULT_COLOR = "#00ff88"
|
|
31
|
+
DEFAULT_SIZE = 24
|
|
32
|
+
DEFAULT_THICKNESS = 2
|
|
33
|
+
DEFAULT_GAP = 6
|
|
34
|
+
MIN_SIZE = 4
|
|
35
|
+
MAX_SIZE = 200
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def normalize_style(style) -> str:
|
|
39
|
+
"""把準星樣式正規化;不認得的一律當十字。"""
|
|
40
|
+
name = str(style or "").strip().lower()
|
|
41
|
+
return name if name in STYLES else STYLE_CROSS
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def clamp_size(value, fallback: int = DEFAULT_SIZE) -> int:
|
|
45
|
+
"""準星尺寸夾在看得見又不誇張的範圍。"""
|
|
46
|
+
try:
|
|
47
|
+
return max(MIN_SIZE, min(MAX_SIZE, int(value)))
|
|
48
|
+
except (TypeError, ValueError):
|
|
49
|
+
return fallback
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def clamp_thickness(value, fallback: int = DEFAULT_THICKNESS) -> int:
|
|
53
|
+
"""線寬夾在 1~12。"""
|
|
54
|
+
try:
|
|
55
|
+
return max(1, min(12, int(value)))
|
|
56
|
+
except (TypeError, ValueError):
|
|
57
|
+
return fallback
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def clamp_gap(value, size: int = DEFAULT_SIZE, fallback: int = DEFAULT_GAP) -> int:
|
|
61
|
+
"""
|
|
62
|
+
中央空隙不能大到把準星吃光,所以上限跟著尺寸走。
|
|
63
|
+
The centre gap cannot swallow the crosshair, so its ceiling follows the size.
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
gap = int(value)
|
|
67
|
+
except (TypeError, ValueError):
|
|
68
|
+
gap = fallback
|
|
69
|
+
return max(0, min(clamp_size(size) - 1, gap))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def arm_segments(size: int, gap: int) -> Tuple[Tuple[float, float], ...]:
|
|
73
|
+
"""
|
|
74
|
+
十字手臂的 (起點, 終點) 距離中心多遠。因為 clamp_gap 保證空隙一定小於
|
|
75
|
+
尺寸,所以手臂至少會有一個像素長——準星不會被空隙吃到看不見。
|
|
76
|
+
How far each arm starts and ends from the centre. clamp_gap keeps the gap
|
|
77
|
+
strictly inside the size, so an arm is always at least a pixel long: the
|
|
78
|
+
crosshair can never be swallowed by its own gap.
|
|
79
|
+
"""
|
|
80
|
+
length = clamp_size(size)
|
|
81
|
+
return ((clamp_gap(gap, length), length),)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class CrosshairWidget(BaseWidget):
|
|
85
|
+
"""
|
|
86
|
+
畫面中央的準星。位置固定在目標螢幕正中,不接受滑鼠(點擊會穿到遊戲)。
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, style: str = STYLE_CROSS, color: str = DEFAULT_COLOR,
|
|
90
|
+
size: int = DEFAULT_SIZE, thickness: int = DEFAULT_THICKNESS,
|
|
91
|
+
gap: int = DEFAULT_GAP) -> None:
|
|
92
|
+
front_engine_logger.info(f"[CrosshairWidget] Init | style={style}")
|
|
93
|
+
super().__init__()
|
|
94
|
+
self.style = normalize_style(style)
|
|
95
|
+
candidate = QColor(color)
|
|
96
|
+
self.color = candidate if candidate.isValid() else QColor(DEFAULT_COLOR)
|
|
97
|
+
self.size = clamp_size(size)
|
|
98
|
+
self.thickness = clamp_thickness(thickness)
|
|
99
|
+
self.gap = clamp_gap(gap, self.size)
|
|
100
|
+
self.opacity = 1.0
|
|
101
|
+
self.resize(self.size * 2 + 8, self.size * 2 + 8)
|
|
102
|
+
|
|
103
|
+
def set_crosshair(self, style=None, color=None, size=None,
|
|
104
|
+
thickness=None, gap=None) -> None:
|
|
105
|
+
"""一次改好幾項設定,改完立刻重畫。"""
|
|
106
|
+
if style is not None:
|
|
107
|
+
self.style = normalize_style(style)
|
|
108
|
+
if color is not None:
|
|
109
|
+
candidate = QColor(color)
|
|
110
|
+
if candidate.isValid():
|
|
111
|
+
self.color = candidate
|
|
112
|
+
if size is not None:
|
|
113
|
+
self.size = clamp_size(size)
|
|
114
|
+
if thickness is not None:
|
|
115
|
+
self.thickness = clamp_thickness(thickness)
|
|
116
|
+
if gap is not None or size is not None:
|
|
117
|
+
self.gap = clamp_gap(self.gap if gap is None else gap, self.size)
|
|
118
|
+
self.resize(self.size * 2 + 8, self.size * 2 + 8)
|
|
119
|
+
self.centre_on_screen()
|
|
120
|
+
self.update()
|
|
121
|
+
|
|
122
|
+
def centre_on_screen(self, screen=None) -> None:
|
|
123
|
+
"""擺到螢幕正中央(準星要對準的是畫面中心)。"""
|
|
124
|
+
from PySide6.QtGui import QGuiApplication
|
|
125
|
+
|
|
126
|
+
target = screen or self.screen() or QGuiApplication.primaryScreen()
|
|
127
|
+
if target is None: # pragma: no cover - no screen at all
|
|
128
|
+
return
|
|
129
|
+
geometry = target.geometry()
|
|
130
|
+
self.move(geometry.x() + (geometry.width() - self.width()) // 2,
|
|
131
|
+
geometry.y() + (geometry.height() - self.height()) // 2)
|
|
132
|
+
|
|
133
|
+
def draw_content(self, painter: QPainter) -> None:
|
|
134
|
+
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
|
135
|
+
pen = QPen(self.color, self.thickness)
|
|
136
|
+
pen.setCapStyle(Qt.PenCapStyle.FlatCap)
|
|
137
|
+
painter.setPen(pen)
|
|
138
|
+
centre = QPointF(self.width() / 2.0, self.height() / 2.0)
|
|
139
|
+
if self.style == STYLE_DOT:
|
|
140
|
+
painter.setBrush(self.color)
|
|
141
|
+
painter.drawEllipse(centre, self.thickness, self.thickness)
|
|
142
|
+
return
|
|
143
|
+
if self.style == STYLE_CIRCLE:
|
|
144
|
+
painter.setBrush(Qt.BrushStyle.NoBrush)
|
|
145
|
+
painter.drawEllipse(centre, float(self.size), float(self.size))
|
|
146
|
+
return
|
|
147
|
+
self._draw_arms(painter, centre)
|
|
148
|
+
|
|
149
|
+
def _draw_arms(self, painter: QPainter, centre: QPointF) -> None:
|
|
150
|
+
"""十字與 T 字共用的手臂繪製(T 字少了上面那一隻)。"""
|
|
151
|
+
for inner, outer in arm_segments(self.size, self.gap):
|
|
152
|
+
painter.drawLine(QPointF(centre.x() - outer, centre.y()),
|
|
153
|
+
QPointF(centre.x() - inner, centre.y()))
|
|
154
|
+
painter.drawLine(QPointF(centre.x() + inner, centre.y()),
|
|
155
|
+
QPointF(centre.x() + outer, centre.y()))
|
|
156
|
+
painter.drawLine(QPointF(centre.x(), centre.y() + inner),
|
|
157
|
+
QPointF(centre.x(), centre.y() + outer))
|
|
158
|
+
if self.style != STYLE_T_SHAPE:
|
|
159
|
+
painter.drawLine(QPointF(centre.x(), centre.y() - outer),
|
|
160
|
+
QPointF(centre.x(), centre.y() - inner))
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
提詞機:把講稿以可調速度往上捲,字夠大、可以左右鏡像(配提詞玻璃用)。
|
|
3
|
+
|
|
4
|
+
捲動用「每次前進幾個像素」而不是「每次一行」,這樣速度才會平順;到底之後
|
|
5
|
+
會停在最後一行,不會自己跳回開頭把人嚇一跳。
|
|
6
|
+
|
|
7
|
+
A teleprompter: scroll a script upward at an adjustable speed, in large type,
|
|
8
|
+
optionally mirrored for prompter glass.
|
|
9
|
+
|
|
10
|
+
Scrolling advances by pixels rather than by lines so the motion stays smooth,
|
|
11
|
+
and it stops at the last line rather than snapping back to the top mid-read.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import List, Optional
|
|
16
|
+
|
|
17
|
+
from PySide6.QtCore import Qt, QTimer
|
|
18
|
+
from PySide6.QtGui import QColor, QFont, QFontMetrics, QPainter, QTransform
|
|
19
|
+
|
|
20
|
+
from frontengine.show.base_widget import BaseWidget
|
|
21
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
22
|
+
from frontengine.utils.power_mode.power_mode import tier_interval
|
|
23
|
+
|
|
24
|
+
DEFAULT_SPEED = 40 # 像素/秒 / pixels per second
|
|
25
|
+
MIN_SPEED = 5
|
|
26
|
+
MAX_SPEED = 400
|
|
27
|
+
DEFAULT_FONT_SIZE = 32
|
|
28
|
+
MIN_FONT_SIZE = 10
|
|
29
|
+
MAX_FONT_SIZE = 120
|
|
30
|
+
_TICK_MS = 33
|
|
31
|
+
_SIDE_MARGIN = 40
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def clamp_speed(value, fallback: int = DEFAULT_SPEED) -> int:
|
|
35
|
+
"""捲動速度夾在讀得完又跟得上的範圍。"""
|
|
36
|
+
try:
|
|
37
|
+
return max(MIN_SPEED, min(MAX_SPEED, int(value)))
|
|
38
|
+
except (TypeError, ValueError):
|
|
39
|
+
return fallback
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def clamp_font_size(value, fallback: int = DEFAULT_FONT_SIZE) -> int:
|
|
43
|
+
"""字級夾在看得見的範圍。"""
|
|
44
|
+
try:
|
|
45
|
+
return max(MIN_FONT_SIZE, min(MAX_FONT_SIZE, int(value)))
|
|
46
|
+
except (TypeError, ValueError):
|
|
47
|
+
return fallback
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def wrap_lines(text: str, metrics: QFontMetrics, width: int) -> List[str]:
|
|
51
|
+
"""
|
|
52
|
+
依實際字寬把講稿折行;空行會保留(那是段落之間的呼吸)。
|
|
53
|
+
Wrap the script to the actual text width, keeping blank lines - they are the
|
|
54
|
+
pauses between paragraphs.
|
|
55
|
+
"""
|
|
56
|
+
usable = max(40, int(width))
|
|
57
|
+
lines: List[str] = []
|
|
58
|
+
for paragraph in str(text or "").splitlines():
|
|
59
|
+
if not paragraph.strip():
|
|
60
|
+
lines.append("")
|
|
61
|
+
continue
|
|
62
|
+
current = ""
|
|
63
|
+
for word in paragraph.split():
|
|
64
|
+
candidate = f"{current} {word}".strip()
|
|
65
|
+
if current and metrics.horizontalAdvance(candidate) > usable:
|
|
66
|
+
lines.append(current)
|
|
67
|
+
current = word
|
|
68
|
+
else:
|
|
69
|
+
current = candidate
|
|
70
|
+
if current:
|
|
71
|
+
lines.append(current)
|
|
72
|
+
return lines
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TeleprompterWidget(BaseWidget):
|
|
76
|
+
"""
|
|
77
|
+
提詞機覆蓋層。捲動位置以像素計,速度、字級與鏡像都可以邊跑邊調。
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, text: str = "", speed: int = DEFAULT_SPEED,
|
|
81
|
+
font_size: int = DEFAULT_FONT_SIZE, mirrored: bool = False) -> None:
|
|
82
|
+
front_engine_logger.info("[TeleprompterWidget] Init")
|
|
83
|
+
super().__init__()
|
|
84
|
+
self.script = str(text)
|
|
85
|
+
self.speed = clamp_speed(speed)
|
|
86
|
+
self.mirrored = bool(mirrored)
|
|
87
|
+
self.text_color = QColor("#ffffff")
|
|
88
|
+
self.opacity = 0.9
|
|
89
|
+
self.offset = 0.0
|
|
90
|
+
self._font = QFont()
|
|
91
|
+
self._font.setPointSize(clamp_font_size(font_size))
|
|
92
|
+
self._timer = QTimer(self)
|
|
93
|
+
self._timer.timeout.connect(self.advance)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def font_size(self) -> int:
|
|
97
|
+
return self._font.pointSize()
|
|
98
|
+
|
|
99
|
+
def set_script(self, text: str) -> None:
|
|
100
|
+
"""換一份講稿並回到開頭。"""
|
|
101
|
+
self.script = str(text)
|
|
102
|
+
self.offset = 0.0
|
|
103
|
+
self.update()
|
|
104
|
+
|
|
105
|
+
def set_speed(self, speed: int) -> None:
|
|
106
|
+
self.speed = clamp_speed(speed)
|
|
107
|
+
|
|
108
|
+
def set_font_size(self, size: int) -> None:
|
|
109
|
+
self._font.setPointSize(clamp_font_size(size))
|
|
110
|
+
self.update()
|
|
111
|
+
|
|
112
|
+
def set_mirrored(self, mirrored: bool) -> None:
|
|
113
|
+
"""左右鏡像:透過提詞玻璃反射後才是正的。"""
|
|
114
|
+
self.mirrored = bool(mirrored)
|
|
115
|
+
self.update()
|
|
116
|
+
|
|
117
|
+
def lines(self) -> List[str]:
|
|
118
|
+
"""目前折行後的講稿。"""
|
|
119
|
+
return wrap_lines(self.script, QFontMetrics(self._font),
|
|
120
|
+
max(40, self.width() - _SIDE_MARGIN * 2))
|
|
121
|
+
|
|
122
|
+
def content_height(self) -> int:
|
|
123
|
+
"""整份講稿捲完需要多高。"""
|
|
124
|
+
return len(self.lines()) * QFontMetrics(self._font).lineSpacing()
|
|
125
|
+
|
|
126
|
+
def max_offset(self) -> float:
|
|
127
|
+
"""捲到底的位置(不會捲過頭)。"""
|
|
128
|
+
return float(max(0, self.content_height() - self.height() // 2))
|
|
129
|
+
|
|
130
|
+
def start(self) -> None:
|
|
131
|
+
self._timer.start(tier_interval(_TICK_MS, self.quality_tier))
|
|
132
|
+
|
|
133
|
+
def stop(self) -> None:
|
|
134
|
+
self._timer.stop()
|
|
135
|
+
|
|
136
|
+
def apply_quality_tier(self) -> None:
|
|
137
|
+
if self._timer.isActive():
|
|
138
|
+
self._timer.start(tier_interval(_TICK_MS, self.quality_tier))
|
|
139
|
+
|
|
140
|
+
def rewind(self) -> None:
|
|
141
|
+
"""回到講稿開頭。"""
|
|
142
|
+
self.offset = 0.0
|
|
143
|
+
self.update()
|
|
144
|
+
|
|
145
|
+
def advance(self, seconds: Optional[float] = None) -> float:
|
|
146
|
+
"""
|
|
147
|
+
前進一小段(預設一個 tick 的量),回傳新的捲動位置。捲到底就停住。
|
|
148
|
+
Advance one tick's worth and return the new offset, stopping at the end.
|
|
149
|
+
"""
|
|
150
|
+
step = self.speed * (seconds if seconds is not None else _TICK_MS / 1000.0)
|
|
151
|
+
self.offset = min(self.max_offset(), self.offset + step)
|
|
152
|
+
self.update()
|
|
153
|
+
return self.offset
|
|
154
|
+
|
|
155
|
+
def draw_content(self, painter: QPainter) -> None:
|
|
156
|
+
painter.setFont(self._font)
|
|
157
|
+
painter.setPen(self.text_color)
|
|
158
|
+
if self.mirrored:
|
|
159
|
+
painter.setTransform(QTransform().translate(self.width(), 0).scale(-1, 1))
|
|
160
|
+
spacing = QFontMetrics(self._font).lineSpacing()
|
|
161
|
+
top = self.height() // 2 - int(self.offset)
|
|
162
|
+
for index, line in enumerate(self.lines()):
|
|
163
|
+
y = top + index * spacing
|
|
164
|
+
if y < -spacing or y > self.height():
|
|
165
|
+
continue
|
|
166
|
+
painter.drawText(_SIDE_MARGIN, y, self.width() - _SIDE_MARGIN * 2, spacing,
|
|
167
|
+
Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, line)
|
|
168
|
+
|
|
169
|
+
def closeEvent(self, event) -> None:
|
|
170
|
+
self.stop()
|
|
171
|
+
super().closeEvent(event)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
畫面文字視窗:顯示 Claude 從截圖讀出來的結果,並提供複製。
|
|
3
|
+
|
|
4
|
+
第一次使用會先問一次「要把截圖送到 Anthropic 的 API 嗎」,同意後才會送,
|
|
5
|
+
而且這個選擇記在設定裡,隨時可以在這裡收回。
|
|
6
|
+
|
|
7
|
+
The screen-text window: show what Claude read from the capture, and offer to
|
|
8
|
+
copy it.
|
|
9
|
+
|
|
10
|
+
The first use asks whether the screenshot may be sent to Anthropic's API.
|
|
11
|
+
Nothing is sent before that is answered, the choice is remembered, and it can
|
|
12
|
+
be withdrawn from right here.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
from PySide6.QtGui import QGuiApplication
|
|
19
|
+
from PySide6.QtWidgets import (
|
|
20
|
+
QCheckBox, QDialog, QDialogButtonBox, QGridLayout, QLabel, QMessageBox, QPlainTextEdit,
|
|
21
|
+
QPushButton, QWidget,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from frontengine.user_setting.user_setting_file import user_setting_dict, write_user_setting
|
|
25
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
26
|
+
from frontengine.utils.multi_language.language_wrapper import language_wrapper
|
|
27
|
+
from frontengine.utils.screen_text.screen_text_service import API_KEY_ENV, api_key
|
|
28
|
+
|
|
29
|
+
CONSENT_KEY = "screen_text_consent"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _t(key: str, fallback: str) -> str:
|
|
33
|
+
return language_wrapper.language_word_dict.get(key, fallback)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def has_consent() -> bool:
|
|
37
|
+
"""使用者是否已經同意把截圖送出去。"""
|
|
38
|
+
return bool(user_setting_dict.get(CONSENT_KEY))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def set_consent(granted: bool) -> None:
|
|
42
|
+
"""記住(或收回)同意。"""
|
|
43
|
+
user_setting_dict[CONSENT_KEY] = bool(granted)
|
|
44
|
+
write_user_setting()
|
|
45
|
+
front_engine_logger.info(f"[ScreenText] consent = {bool(granted)}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def ask_for_consent(parent: Optional[QWidget] = None) -> bool:
|
|
49
|
+
"""
|
|
50
|
+
問使用者要不要把截圖送到 Anthropic。已經同意過就直接回 True,不再打擾。
|
|
51
|
+
Ask whether screenshots may go to Anthropic. Already-granted consent returns
|
|
52
|
+
True without asking again.
|
|
53
|
+
"""
|
|
54
|
+
if has_consent():
|
|
55
|
+
return True
|
|
56
|
+
answer = QMessageBox.question(
|
|
57
|
+
parent,
|
|
58
|
+
_t("screen_text_consent_title", "Send this screenshot?"),
|
|
59
|
+
_t("screen_text_consent_body",
|
|
60
|
+
"Reading text needs the captured area sent to Anthropic's API. It leaves this "
|
|
61
|
+
"machine, and anything visible in the selection goes with it. Your own "
|
|
62
|
+
"ANTHROPIC_API_KEY is used. Send captures?"),
|
|
63
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|
64
|
+
QMessageBox.StandardButton.No)
|
|
65
|
+
granted = answer == QMessageBox.StandardButton.Yes
|
|
66
|
+
set_consent(granted)
|
|
67
|
+
return granted
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ScreenTextDialog(QDialog):
|
|
71
|
+
"""顯示讀到的文字,可複製,也可以在這裡收回同意。"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, text: str = "", parent: Optional[QWidget] = None) -> None:
|
|
74
|
+
front_engine_logger.info("[ScreenTextDialog] Init")
|
|
75
|
+
super().__init__(parent)
|
|
76
|
+
self.setWindowTitle(_t("screen_text_title", "Text on screen"))
|
|
77
|
+
|
|
78
|
+
self.text_edit = QPlainTextEdit(str(text))
|
|
79
|
+
self.text_edit.setReadOnly(False)
|
|
80
|
+
self.copy_button = QPushButton(_t("screen_text_copy", "Copy"))
|
|
81
|
+
self.copy_button.clicked.connect(self.copy_text)
|
|
82
|
+
self.consent_checkbox = QCheckBox(
|
|
83
|
+
_t("screen_text_consent_toggle", "Allow sending captures to Anthropic"))
|
|
84
|
+
self.consent_checkbox.setChecked(has_consent())
|
|
85
|
+
self.consent_checkbox.toggled.connect(set_consent)
|
|
86
|
+
self.hint_label = QLabel(self.status_text())
|
|
87
|
+
self.hint_label.setWordWrap(True)
|
|
88
|
+
|
|
89
|
+
self.button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
|
|
90
|
+
self.button_box.rejected.connect(self.reject)
|
|
91
|
+
|
|
92
|
+
layout = QGridLayout(self)
|
|
93
|
+
layout.addWidget(self.text_edit, 0, 0, 1, 2)
|
|
94
|
+
layout.addWidget(self.copy_button, 1, 0)
|
|
95
|
+
layout.addWidget(self.consent_checkbox, 1, 1)
|
|
96
|
+
layout.addWidget(self.hint_label, 2, 0, 1, 2)
|
|
97
|
+
layout.addWidget(self.button_box, 3, 0, 1, 2)
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def status_text() -> str:
|
|
101
|
+
"""說明目前為什麼可用或不可用。"""
|
|
102
|
+
if api_key() is None:
|
|
103
|
+
return _t("screen_text_no_key",
|
|
104
|
+
"Set {env} in your environment to use this.").format(env=API_KEY_ENV)
|
|
105
|
+
return _t("screen_text_ready",
|
|
106
|
+
"Captures are sent to Anthropic only while the box above is ticked.")
|
|
107
|
+
|
|
108
|
+
def set_text(self, text: str) -> None:
|
|
109
|
+
self.text_edit.setPlainText(str(text or ""))
|
|
110
|
+
|
|
111
|
+
def text(self) -> str:
|
|
112
|
+
return self.text_edit.toPlainText()
|
|
113
|
+
|
|
114
|
+
def copy_text(self) -> bool:
|
|
115
|
+
"""把結果放到剪貼簿。"""
|
|
116
|
+
clipboard = QGuiApplication.clipboard()
|
|
117
|
+
if clipboard is None: # pragma: no cover - no clipboard at all
|
|
118
|
+
return False
|
|
119
|
+
clipboard.setText(self.text())
|
|
120
|
+
return True
|
|
@@ -29,6 +29,7 @@ from frontengine.ui.page.scene_setting.scene_setting_ui import SceneSettingUI
|
|
|
29
29
|
from frontengine.ui.page.presentation.presentation_setting_ui import PresentationSettingUI
|
|
30
30
|
from frontengine.ui.page.screen_care.screen_care_setting_ui import ScreenCareSettingUI
|
|
31
31
|
from frontengine.ui.page.sound_player.sound_player_setting_ui import SoundPlayerSettingUI
|
|
32
|
+
from frontengine.ui.page.stream.stream_setting_ui import StreamSettingUI
|
|
32
33
|
from frontengine.ui.page.text.text_setting_ui import TextSettingUI
|
|
33
34
|
from frontengine.ui.page.tools.tools_setting_ui import ToolsSettingUI
|
|
34
35
|
from frontengine.ui.page.video.video_setting_ui import VideoSettingUI
|
|
@@ -126,6 +127,7 @@ class FrontEngineMainUI(QMainWindow):
|
|
|
126
127
|
self.focus_setting_ui = FocusSettingUI()
|
|
127
128
|
self.widgets_setting_ui = WidgetsSettingUI()
|
|
128
129
|
self.tools_setting_ui = ToolsSettingUI()
|
|
130
|
+
self.stream_setting_ui = StreamSettingUI()
|
|
129
131
|
|
|
130
132
|
# 控制中心
|
|
131
133
|
# Control Center
|
|
@@ -298,6 +300,9 @@ class FrontEngineMainUI(QMainWindow):
|
|
|
298
300
|
for attribute in ("measure_widget_list", "capture_widget_list", "camera_widget_list"):
|
|
299
301
|
self.control_center_ui.register_overlay_source(
|
|
300
302
|
lambda attribute=attribute: getattr(self.tools_setting_ui, attribute, []))
|
|
303
|
+
for attribute in ("crosshair_widget_list", "teleprompter_widget_list"):
|
|
304
|
+
self.control_center_ui.register_overlay_source(
|
|
305
|
+
lambda attribute=attribute: getattr(self.stream_setting_ui, attribute, []))
|
|
301
306
|
|
|
302
307
|
def _add_tabs(self) -> None:
|
|
303
308
|
"""加入所有內建與擴充的 Tab / Add all built-in and extended tabs"""
|
|
@@ -317,6 +322,7 @@ class FrontEngineMainUI(QMainWindow):
|
|
|
317
322
|
(self.focus_setting_ui, "tab_focus_text"),
|
|
318
323
|
(self.widgets_setting_ui, "tab_widgets_text"),
|
|
319
324
|
(self.tools_setting_ui, "tab_tools_text"),
|
|
325
|
+
(self.stream_setting_ui, "tab_stream_text"),
|
|
320
326
|
(self.control_center_ui, "tab_control_center_text"),
|
|
321
327
|
]
|
|
322
328
|
|
|
@@ -518,6 +524,8 @@ class FrontEngineMainUI(QMainWindow):
|
|
|
518
524
|
if getattr(self, "tools_setting_ui", None) is not None:
|
|
519
525
|
self.tools_setting_ui.release_pinned_windows()
|
|
520
526
|
self.tools_setting_ui.stop_camera()
|
|
527
|
+
if getattr(self, "stream_setting_ui", None) is not None:
|
|
528
|
+
self.stream_setting_ui.soundboard.stop_all()
|
|
521
529
|
if getattr(self, "reminder_service", None) is not None:
|
|
522
530
|
self.reminder_service.stop()
|
|
523
531
|
if getattr(self, "app_profile_service", None) is not None:
|
{frontengine-1.0.38 → frontengine-1.0.40}/frontengine/ui/page/screen_care/screen_care_setting_ui.py
RENAMED
|
@@ -320,7 +320,7 @@ class ScreenCareSettingUI(QWidget):
|
|
|
320
320
|
language_wrapper.language_word_dict.get("color_vision_start", "Simulate"))
|
|
321
321
|
|
|
322
322
|
def _apply_color_vision_settings(self) -> None:
|
|
323
|
-
for widget in
|
|
323
|
+
for widget in self.color_vision_widget_list[:]:
|
|
324
324
|
try:
|
|
325
325
|
widget.set_simulation(self.color_vision_combobox.currentData(),
|
|
326
326
|
self.color_vision_slider.value() / 100.0)
|