frontengine 1.0.22__tar.gz → 1.0.24__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.22 → frontengine-1.0.24}/PKG-INFO +1 -1
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/pet/desktop_pet.py +40 -2
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/pet/pet_setting_ui.py +10 -0
- frontengine-1.0.24/frontengine/utils/audio_meter/system_audio_meter.py +136 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/english.py +1 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/traditional_chinese.py +1 -0
- frontengine-1.0.24/frontengine/worker/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine.egg-info/PKG-INFO +1 -1
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine.egg-info/SOURCES.txt +2 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/pyproject.toml +1 -1
- {frontengine-1.0.22 → frontengine-1.0.24}/LICENSE +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/README.md +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/__main__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/base_widget.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/gif/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/gif/paint_gif.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/image/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/image/paint_image.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/load/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/load/load_someone_make_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/overlay_factory.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/particle/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/particle/particle_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/pet/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/scene/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/scene/extend_graphic_scene.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/scene/extend_graphic_view.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/scene/scene.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/sound_player/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/sound_player/sound_effect.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/sound_player/sound_player.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/text/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/text/draw_text.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/video/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/video/video_player.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/web/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/web/webview.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/show/window_helpers.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/system_tray/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/system_tray/extend_system_tray.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/color/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/color/global_color.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/dialog/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/dialog/save_file_dialog.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/main_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/menu/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/menu/help_menu.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/menu/how_to_menu.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/menu/language_menu.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/menu/preset_menu.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/menu/settings_menu.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/control_center/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/gif/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/image/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/image/image_setting_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/particle/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/pet/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/sound_player/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/text/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/text/text_setting_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/utils.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/video/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/video/video_setting_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/web/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/web/web_setting_ui.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/user_setting/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/user_setting/preset_repository.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/user_setting/scene_setting.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/user_setting/user_setting_file.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/__init__.py +0 -0
- {frontengine-1.0.22/frontengine/utils/browser → frontengine-1.0.24/frontengine/utils/audio_meter}/__init__.py +0 -0
- {frontengine-1.0.22/frontengine/utils/critical_exit → frontengine-1.0.24/frontengine/utils/browser}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/browser/browser.py +0 -0
- {frontengine-1.0.22/frontengine/utils/file → frontengine-1.0.24/frontengine/utils/critical_exit}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/critical_exit/critical_exit.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/critical_exit/win32_vk.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/exception/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/exception/exception_tags.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/exception/exceptions.py +0 -0
- {frontengine-1.0.22/frontengine/utils/hotkey → frontengine-1.0.24/frontengine/utils/file}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/file/open/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/file/open/open_file.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/file/save/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/file/save/save_file.py +0 -0
- {frontengine-1.0.22/frontengine/utils/json → frontengine-1.0.24/frontengine/utils/hotkey}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/hotkey/hotkey_service.py +0 -0
- {frontengine-1.0.22/frontengine/utils/logging → frontengine-1.0.24/frontengine/utils/json}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/json/json_file.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/json/json_repository.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/json_format/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/json_format/json_process.py +0 -0
- {frontengine-1.0.22/frontengine/utils/multi_language → frontengine-1.0.24/frontengine/utils/logging}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/logging/loggin_instance.py +0 -0
- {frontengine-1.0.22/frontengine/utils/preset_schedule → frontengine-1.0.24/frontengine/utils/multi_language}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/france.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/germany.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/italy.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/language_wrapper.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/russian.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
- {frontengine-1.0.22/frontengine/utils/redirect_manager → frontengine-1.0.24/frontengine/utils/preset_schedule}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
- {frontengine-1.0.22/frontengine/utils/smart_pause → frontengine-1.0.24/frontengine/utils/redirect_manager}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
- {frontengine-1.0.22/frontengine/utils/theme_schedule → frontengine-1.0.24/frontengine/utils/smart_pause}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
- {frontengine-1.0.22/frontengine/worker → frontengine-1.0.24/frontengine/utils/theme_schedule}/__init__.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/web_url.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine/worker/qthread_worker.py +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine.egg-info/dependency_links.txt +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine.egg-info/entry_points.txt +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine.egg-info/requires.txt +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/frontengine.egg-info/top_level.txt +0 -0
- {frontengine-1.0.22 → frontengine-1.0.24}/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.24
|
|
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
|
|
@@ -369,6 +369,15 @@ def format_status(level: int, mood_level: str, hunger_level: str, labels: dict)
|
|
|
369
369
|
return f"{level_label}{level} · {mood_text} · {hunger_text}"
|
|
370
370
|
|
|
371
371
|
|
|
372
|
+
def audio_pulse_scale(level, base: float = 0.7, amplitude: float = 0.3) -> float:
|
|
373
|
+
"""把音訊音量 (0~1) 轉成脈動縮放比例 / Map an audio level (0..1) to a pulse scale."""
|
|
374
|
+
try:
|
|
375
|
+
clamped = max(0.0, min(1.0, float(level)))
|
|
376
|
+
except (TypeError, ValueError):
|
|
377
|
+
return 1.0
|
|
378
|
+
return base + amplitude * clamped
|
|
379
|
+
|
|
380
|
+
|
|
372
381
|
def size_for_level(base_size: int, level: int) -> int:
|
|
373
382
|
"""依等級放大基礎尺寸(每級 +8%,上限 +50%)/ Grow base size by level (cap +50%)."""
|
|
374
383
|
factor = min(1.5, 1.0 + 0.08 * max(0, int(level) - 1))
|
|
@@ -838,7 +847,7 @@ class DesktopPetWidget(BaseWidget):
|
|
|
838
847
|
def __init__(self, image_path: str, size: int = 128, speed: int = 3,
|
|
839
848
|
behaviour: str = BEHAVIOUR_FLOOR, climb: bool = True, talk: bool = True,
|
|
840
849
|
sound_path: Optional[str] = None, sit_on_windows: bool = True,
|
|
841
|
-
volume: float = 1.0):
|
|
850
|
+
volume: float = 1.0, audio_react: bool = False):
|
|
842
851
|
front_engine_logger.info(
|
|
843
852
|
f"[DesktopPetWidget] Init | path={image_path}, size={size}, speed={speed}, "
|
|
844
853
|
f"behaviour={behaviour}, climb={climb}, talk={talk}, sound={sound_path}, "
|
|
@@ -888,6 +897,10 @@ class DesktopPetWidget(BaseWidget):
|
|
|
888
897
|
self._init_sound(sound_path)
|
|
889
898
|
self._sit_on_windows = bool(sit_on_windows)
|
|
890
899
|
self._platform_tick = 0
|
|
900
|
+
# 音訊視覺化:依可注入的音量 provider 脈動 / Audio-reactive pulse
|
|
901
|
+
self._audio_react = bool(audio_react)
|
|
902
|
+
self._audio_level_provider = None # callable -> level 0..1 or None
|
|
903
|
+
self._audio_scale = 1.0
|
|
891
904
|
# 多寵物互動 / Multi-pet interaction
|
|
892
905
|
self._peers_provider = None
|
|
893
906
|
self._peer_greeted = False
|
|
@@ -1026,13 +1039,35 @@ class DesktopPetWidget(BaseWidget):
|
|
|
1026
1039
|
kind, obj = self._active
|
|
1027
1040
|
return obj.currentPixmap() if kind == "movie" else obj
|
|
1028
1041
|
|
|
1042
|
+
def set_audio_level_provider(self, provider) -> None:
|
|
1043
|
+
"""設定回傳目前音量 (0~1 或 None) 的函式 / Provider of the current audio level."""
|
|
1044
|
+
self._audio_level_provider = provider
|
|
1045
|
+
|
|
1046
|
+
def _update_audio_scale(self) -> None:
|
|
1047
|
+
if not self._audio_react or self._audio_level_provider is None:
|
|
1048
|
+
self._audio_scale = 1.0
|
|
1049
|
+
return
|
|
1050
|
+
try:
|
|
1051
|
+
level = self._audio_level_provider()
|
|
1052
|
+
except Exception: # pragma: no cover - defensive
|
|
1053
|
+
level = None
|
|
1054
|
+
self._audio_scale = 1.0 if level is None else audio_pulse_scale(level)
|
|
1055
|
+
|
|
1029
1056
|
def draw_content(self, painter: QPainter) -> None:
|
|
1030
1057
|
pixmap = self._current_pixmap()
|
|
1031
1058
|
if pixmap is None or pixmap.isNull():
|
|
1032
1059
|
return
|
|
1033
1060
|
if self.motion.facing_left:
|
|
1034
1061
|
pixmap = pixmap.transformed(QTransform().scale(-1, 1))
|
|
1035
|
-
|
|
1062
|
+
scale = self._audio_scale if self._audio_react else 1.0
|
|
1063
|
+
if scale >= 0.999:
|
|
1064
|
+
painter.drawPixmap(QRect(0, 0, self.width(), self.height()), pixmap)
|
|
1065
|
+
else:
|
|
1066
|
+
# Anchor to the bottom so the feet stay on the ground while it pulses.
|
|
1067
|
+
width = int(self.width() * scale)
|
|
1068
|
+
height = int(self.height() * scale)
|
|
1069
|
+
painter.drawPixmap(
|
|
1070
|
+
QRect((self.width() - width) // 2, self.height() - height, width, height), pixmap)
|
|
1036
1071
|
|
|
1037
1072
|
def start_moving(self, bounds: Tuple[int, int, int, int], interval_ms: int = 33) -> None:
|
|
1038
1073
|
front_engine_logger.info(f"[DesktopPetWidget] start_moving | bounds={bounds}")
|
|
@@ -1306,6 +1341,9 @@ class DesktopPetWidget(BaseWidget):
|
|
|
1306
1341
|
x, y = self.motion.step()
|
|
1307
1342
|
self.move(x, y)
|
|
1308
1343
|
self._set_active_sprite(self._visual_state())
|
|
1344
|
+
if self._audio_react:
|
|
1345
|
+
self._update_audio_scale()
|
|
1346
|
+
self.update()
|
|
1309
1347
|
self._peer_tick += 1
|
|
1310
1348
|
if self._peer_tick >= 5:
|
|
1311
1349
|
self._peer_tick = 0
|
|
@@ -17,6 +17,7 @@ from frontengine.ui.page.utils import (
|
|
|
17
17
|
resolve_preferred_monitor,
|
|
18
18
|
)
|
|
19
19
|
from frontengine.user_setting.user_setting_file import add_recent_file
|
|
20
|
+
from frontengine.utils.audio_meter.system_audio_meter import system_audio_level
|
|
20
21
|
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
21
22
|
from frontengine.utils.multi_language.language_wrapper import language_wrapper
|
|
22
23
|
|
|
@@ -90,6 +91,8 @@ class PetSettingUI(QWidget):
|
|
|
90
91
|
for label, value in (("0%", 0), ("25%", 25), ("50%", 50), ("75%", 75), ("100%", 100)):
|
|
91
92
|
self.volume_combobox.addItem(label, value)
|
|
92
93
|
self.volume_combobox.setCurrentText("100%")
|
|
94
|
+
self.audio_react_checkbox = QCheckBox(
|
|
95
|
+
language_wrapper.language_word_dict.get("pet_audio_label", "React to audio"))
|
|
93
96
|
|
|
94
97
|
# Start
|
|
95
98
|
self.start_button = QPushButton(language_wrapper.language_word_dict.get("pet_start", "Spawn pet"))
|
|
@@ -124,6 +127,7 @@ class PetSettingUI(QWidget):
|
|
|
124
127
|
self.grid_layout.addWidget(self.target_monitor_combobox, 6, 1)
|
|
125
128
|
self.grid_layout.addWidget(self.volume_label, 6, 2)
|
|
126
129
|
self.grid_layout.addWidget(self.volume_combobox, 7, 2)
|
|
130
|
+
self.grid_layout.addWidget(self.audio_react_checkbox, 7, 0)
|
|
127
131
|
|
|
128
132
|
def _spawn_pet(self) -> None:
|
|
129
133
|
"""建立、顯示並開始移動一隻寵物(供 Start 與右鍵複製共用)。"""
|
|
@@ -139,9 +143,12 @@ class PetSettingUI(QWidget):
|
|
|
139
143
|
sound_path=self.pet_sound_path,
|
|
140
144
|
sit_on_windows=self.sit_checkbox.isChecked(),
|
|
141
145
|
volume=int(self.volume_combobox.currentData()) / 100.0,
|
|
146
|
+
audio_react=self.audio_react_checkbox.isChecked(),
|
|
142
147
|
)
|
|
143
148
|
pet.clone_requested.connect(self._spawn_pet)
|
|
144
149
|
pet.set_peers_provider(lambda me=pet: self._peer_centers(me))
|
|
150
|
+
if self.audio_react_checkbox.isChecked():
|
|
151
|
+
pet.set_audio_level_provider(system_audio_level) # real system output peak meter
|
|
145
152
|
pet.set_pet_window_flag()
|
|
146
153
|
self.pet_list.append(pet)
|
|
147
154
|
pet.show()
|
|
@@ -253,6 +260,7 @@ class PetSettingUI(QWidget):
|
|
|
253
260
|
"sit": self.sit_checkbox.isChecked(),
|
|
254
261
|
"target_monitor": self.target_monitor_combobox.currentText(),
|
|
255
262
|
"volume": self.volume_combobox.currentText(),
|
|
263
|
+
"audio_react": self.audio_react_checkbox.isChecked(),
|
|
256
264
|
}
|
|
257
265
|
|
|
258
266
|
def set_state(self, state: dict) -> None:
|
|
@@ -288,3 +296,5 @@ class PetSettingUI(QWidget):
|
|
|
288
296
|
index = self.volume_combobox.findText(str(state["volume"]))
|
|
289
297
|
if index >= 0:
|
|
290
298
|
self.volume_combobox.setCurrentIndex(index)
|
|
299
|
+
if "audio_react" in state:
|
|
300
|
+
self.audio_react_checkbox.setChecked(bool(state["audio_react"]))
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
以 WASAPI 的 IAudioMeterInformation::GetPeakValue 讀取「預設輸出裝置」的
|
|
3
|
+
峰值電表 (0~1)。這只是讀取電表數值,不擷取或錄製任何音訊內容(隱私友善),
|
|
4
|
+
僅 Windows 有效,任何失敗都退化為 None。
|
|
5
|
+
|
|
6
|
+
Read the default output device's peak meter (0..1) via WASAPI's
|
|
7
|
+
IAudioMeterInformation::GetPeakValue. This only reads a meter value — it does
|
|
8
|
+
NOT capture or record any audio content. Windows only; degrades to None.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
import uuid
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
17
|
+
|
|
18
|
+
# WASAPI / Core Audio identifiers
|
|
19
|
+
_CLSID_MMDeviceEnumerator = "{BCDE0395-E52F-467C-8E3D-C4579291692E}"
|
|
20
|
+
_IID_IMMDeviceEnumerator = "{A95664D2-9614-4F35-A746-DE8DB63617E6}"
|
|
21
|
+
_IID_IAudioMeterInformation = "{C02216F6-8C67-4B5B-9D00-D008E73E0064}"
|
|
22
|
+
_CLSCTX_ALL = 23
|
|
23
|
+
_E_RENDER = 0 # audio output
|
|
24
|
+
_E_CONSOLE = 0 # default role
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SystemAudioMeter:
|
|
28
|
+
"""讀取系統輸出峰值電表;建立失敗或非 Windows 時 level() 回傳 None。"""
|
|
29
|
+
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
self._ok = False
|
|
32
|
+
self._meter = None
|
|
33
|
+
self._device = None
|
|
34
|
+
self._enumerator = None
|
|
35
|
+
self._get_peak = None
|
|
36
|
+
self._byref = None
|
|
37
|
+
self._c_float = None
|
|
38
|
+
self._release_method = None
|
|
39
|
+
if sys.platform == "win32":
|
|
40
|
+
try:
|
|
41
|
+
self._init()
|
|
42
|
+
self._ok = True
|
|
43
|
+
except Exception as error: # pragma: no cover - COM boundary
|
|
44
|
+
front_engine_logger.warning(f"[SystemAudioMeter] init failed: {error!r}")
|
|
45
|
+
self._release_all()
|
|
46
|
+
|
|
47
|
+
# --- COM plumbing -------------------------------------------------
|
|
48
|
+
def _init(self) -> None:
|
|
49
|
+
import ctypes
|
|
50
|
+
from ctypes import POINTER, byref, c_float, c_void_p, c_ulong, HRESULT
|
|
51
|
+
from ctypes.wintypes import DWORD
|
|
52
|
+
|
|
53
|
+
class _GUID(ctypes.Structure):
|
|
54
|
+
_fields_ = [
|
|
55
|
+
("Data1", ctypes.c_uint32),
|
|
56
|
+
("Data2", ctypes.c_uint16),
|
|
57
|
+
("Data3", ctypes.c_uint16),
|
|
58
|
+
("Data4", ctypes.c_uint8 * 8),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
def guid(text: str) -> "_GUID":
|
|
62
|
+
value = _GUID()
|
|
63
|
+
ctypes.memmove(byref(value), uuid.UUID(text).bytes_le, ctypes.sizeof(value))
|
|
64
|
+
return value
|
|
65
|
+
|
|
66
|
+
def method(iface, index, restype, argtypes):
|
|
67
|
+
vtable = ctypes.cast(iface, POINTER(POINTER(c_void_p)))
|
|
68
|
+
func_address = vtable[0][index]
|
|
69
|
+
proto = ctypes.WINFUNCTYPE(restype, c_void_p, *argtypes)
|
|
70
|
+
return proto(func_address)
|
|
71
|
+
|
|
72
|
+
ole32 = ctypes.windll.ole32
|
|
73
|
+
ole32.CoInitialize(None) # S_FALSE if already initialised; harmless
|
|
74
|
+
|
|
75
|
+
enumerator = c_void_p()
|
|
76
|
+
clsid = guid(_CLSID_MMDeviceEnumerator)
|
|
77
|
+
iid_enum = guid(_IID_IMMDeviceEnumerator)
|
|
78
|
+
if ole32.CoCreateInstance(byref(clsid), None, _CLSCTX_ALL, byref(iid_enum), byref(enumerator)) != 0 \
|
|
79
|
+
or not enumerator.value:
|
|
80
|
+
raise OSError("CoCreateInstance(MMDeviceEnumerator) failed")
|
|
81
|
+
self._enumerator = enumerator
|
|
82
|
+
|
|
83
|
+
device = c_void_p()
|
|
84
|
+
get_default = method(enumerator, 4, HRESULT, [DWORD, DWORD, POINTER(c_void_p)])
|
|
85
|
+
if get_default(enumerator, _E_RENDER, _E_CONSOLE, byref(device)) != 0 or not device.value:
|
|
86
|
+
raise OSError("GetDefaultAudioEndpoint failed")
|
|
87
|
+
self._device = device
|
|
88
|
+
|
|
89
|
+
meter = c_void_p()
|
|
90
|
+
iid_meter = guid(_IID_IAudioMeterInformation)
|
|
91
|
+
activate = method(device, 3, HRESULT, [POINTER(_GUID), DWORD, c_void_p, POINTER(c_void_p)])
|
|
92
|
+
if activate(device, byref(iid_meter), _CLSCTX_ALL, None, byref(meter)) != 0 or not meter.value:
|
|
93
|
+
raise OSError("Activate(IAudioMeterInformation) failed")
|
|
94
|
+
self._meter = meter
|
|
95
|
+
|
|
96
|
+
self._get_peak = method(meter, 3, HRESULT, [POINTER(c_float)])
|
|
97
|
+
self._byref = byref
|
|
98
|
+
self._c_float = c_float
|
|
99
|
+
self._release_method = lambda iface: method(iface, 2, c_ulong, [])
|
|
100
|
+
|
|
101
|
+
def level(self) -> Optional[float]:
|
|
102
|
+
"""回傳目前輸出峰值 0~1,或 None。"""
|
|
103
|
+
if not self._ok or self._meter is None or self._get_peak is None:
|
|
104
|
+
return None
|
|
105
|
+
try:
|
|
106
|
+
peak = self._c_float()
|
|
107
|
+
if self._get_peak(self._meter, self._byref(peak)) != 0:
|
|
108
|
+
return None
|
|
109
|
+
return max(0.0, min(1.0, float(peak.value)))
|
|
110
|
+
except Exception: # pragma: no cover - COM boundary
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
def _release_all(self) -> None:
|
|
114
|
+
for iface in (self._meter, self._device, self._enumerator):
|
|
115
|
+
if iface is not None and getattr(iface, "value", None) and self._release_method is not None:
|
|
116
|
+
try:
|
|
117
|
+
self._release_method(iface)(iface)
|
|
118
|
+
except Exception as error: # pragma: no cover - COM boundary
|
|
119
|
+
# 釋放個別介面失敗不影響清理流程,記錄後繼續。
|
|
120
|
+
# A single interface failing to release must not abort cleanup.
|
|
121
|
+
front_engine_logger.debug(f"[SystemAudioMeter] release failed: {error!r}")
|
|
122
|
+
self._meter = self._device = self._enumerator = None
|
|
123
|
+
self._ok = False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_meter_singleton: Optional[SystemAudioMeter] = None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def system_audio_level() -> Optional[float]:
|
|
130
|
+
"""便利函式:以單例電表回傳目前系統輸出峰值 0~1,或 None。"""
|
|
131
|
+
global _meter_singleton
|
|
132
|
+
if sys.platform != "win32":
|
|
133
|
+
return None
|
|
134
|
+
if _meter_singleton is None:
|
|
135
|
+
_meter_singleton = SystemAudioMeter()
|
|
136
|
+
return _meter_singleton.level()
|
|
@@ -247,6 +247,7 @@ english_word_dict = {
|
|
|
247
247
|
"pet_talk_label": "Speech bubbles",
|
|
248
248
|
"pet_sit_label": "Sit on windows",
|
|
249
249
|
"pet_volume_label": "Volume",
|
|
250
|
+
"pet_audio_label": "React to audio",
|
|
250
251
|
"pet_chatter_morning": "Good morning!|Rise and shine!|Ready for the day?",
|
|
251
252
|
"pet_chatter_afternoon": "Good afternoon!|Taking a break?|Keep it up!",
|
|
252
253
|
"pet_chatter_evening": "Good evening!|How was your day?|Almost done?",
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/traditional_chinese.py
RENAMED
|
@@ -248,6 +248,7 @@ traditional_chinese_word_dict = {
|
|
|
248
248
|
"pet_talk_label": "對話泡泡",
|
|
249
249
|
"pet_sit_label": "站在視窗上",
|
|
250
250
|
"pet_volume_label": "音量",
|
|
251
|
+
"pet_audio_label": "隨音訊律動",
|
|
251
252
|
"pet_chatter_morning": "早安!|新的一天開始囉!|準備好了嗎?",
|
|
252
253
|
"pet_chatter_afternoon": "午安!|要不要休息一下?|繼續加油!",
|
|
253
254
|
"pet_chatter_evening": "晚上好!|今天過得如何?|快完成了嗎?",
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frontengine
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.24
|
|
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
|
|
@@ -90,6 +90,8 @@ frontengine/user_setting/scene_setting.py
|
|
|
90
90
|
frontengine/user_setting/user_setting_file.py
|
|
91
91
|
frontengine/utils/__init__.py
|
|
92
92
|
frontengine/utils/web_url.py
|
|
93
|
+
frontengine/utils/audio_meter/__init__.py
|
|
94
|
+
frontengine/utils/audio_meter/system_audio_meter.py
|
|
93
95
|
frontengine/utils/browser/__init__.py
|
|
94
96
|
frontengine/utils/browser/browser.py
|
|
95
97
|
frontengine/utils/critical_exit/__init__.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/control_center/control_center_ui.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/particle/particle_setting_ui.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_manager.py
RENAMED
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/gif.py
RENAMED
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/image.py
RENAMED
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/registry.py
RENAMED
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/sound.py
RENAMED
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/text.py
RENAMED
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/video.py
RENAMED
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_page/web.py
RENAMED
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/ui/page/scene_setting/scene_setting_ui.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/critical_exit/check_key_is_press.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/language_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/multi_language/simplified_chinese.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/smart_pause/smart_pause_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.22 → frontengine-1.0.24}/frontengine/utils/theme_schedule/theme_schedule_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|