frontengine 1.0.20__tar.gz → 1.0.22__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. {frontengine-1.0.20 → frontengine-1.0.22}/PKG-INFO +1 -1
  2. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/pet/desktop_pet.py +83 -2
  3. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/pet/pet_setting_ui.py +43 -3
  4. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/user_setting/user_setting_file.py +3 -0
  5. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/english.py +1 -0
  6. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/traditional_chinese.py +1 -0
  7. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine.egg-info/PKG-INFO +1 -1
  8. {frontengine-1.0.20 → frontengine-1.0.22}/pyproject.toml +1 -1
  9. {frontengine-1.0.20 → frontengine-1.0.22}/LICENSE +0 -0
  10. {frontengine-1.0.20 → frontengine-1.0.22}/README.md +0 -0
  11. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/__init__.py +0 -0
  12. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/__main__.py +0 -0
  13. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/__init__.py +0 -0
  14. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/base_widget.py +0 -0
  15. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/gif/__init__.py +0 -0
  16. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/gif/paint_gif.py +0 -0
  17. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/image/__init__.py +0 -0
  18. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/image/paint_image.py +0 -0
  19. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/load/__init__.py +0 -0
  20. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/load/load_someone_make_ui.py +0 -0
  21. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/overlay_factory.py +0 -0
  22. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/particle/__init__.py +0 -0
  23. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/particle/particle_ui.py +0 -0
  24. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/pet/__init__.py +0 -0
  25. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/scene/__init__.py +0 -0
  26. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  27. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/scene/extend_graphic_view.py +0 -0
  28. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/scene/scene.py +0 -0
  29. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/sound_player/__init__.py +0 -0
  30. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/sound_player/sound_effect.py +0 -0
  31. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/sound_player/sound_player.py +0 -0
  32. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/text/__init__.py +0 -0
  33. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/text/draw_text.py +0 -0
  34. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/video/__init__.py +0 -0
  35. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/video/video_player.py +0 -0
  36. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/web/__init__.py +0 -0
  37. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/web/webview.py +0 -0
  38. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/show/window_helpers.py +0 -0
  39. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/system_tray/__init__.py +0 -0
  40. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/system_tray/extend_system_tray.py +0 -0
  41. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/__init__.py +0 -0
  42. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/color/__init__.py +0 -0
  43. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/color/global_color.py +0 -0
  44. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/dialog/__init__.py +0 -0
  45. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  46. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  47. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  48. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/main_ui.py +0 -0
  49. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/menu/__init__.py +0 -0
  50. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/menu/help_menu.py +0 -0
  51. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/menu/how_to_menu.py +0 -0
  52. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/menu/language_menu.py +0 -0
  53. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/menu/preset_menu.py +0 -0
  54. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/menu/settings_menu.py +0 -0
  55. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/__init__.py +0 -0
  56. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/control_center/__init__.py +0 -0
  57. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  58. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/gif/__init__.py +0 -0
  59. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  60. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/image/__init__.py +0 -0
  61. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  62. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/particle/__init__.py +0 -0
  63. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  64. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/pet/__init__.py +0 -0
  65. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  66. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  67. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  68. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  69. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  70. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  71. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  72. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  73. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  74. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  75. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  76. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  77. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/sound_player/__init__.py +0 -0
  78. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  79. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/text/__init__.py +0 -0
  80. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  81. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/utils.py +0 -0
  82. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/video/__init__.py +0 -0
  83. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  84. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/web/__init__.py +0 -0
  85. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  86. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/user_setting/__init__.py +0 -0
  87. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/user_setting/preset_repository.py +0 -0
  88. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/user_setting/scene_setting.py +0 -0
  89. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/__init__.py +0 -0
  90. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/browser/__init__.py +0 -0
  91. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/browser/browser.py +0 -0
  92. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/critical_exit/__init__.py +0 -0
  93. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  94. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  95. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  96. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/exception/__init__.py +0 -0
  97. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/exception/exception_tags.py +0 -0
  98. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/exception/exceptions.py +0 -0
  99. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/file/__init__.py +0 -0
  100. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/file/open/__init__.py +0 -0
  101. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/file/open/open_file.py +0 -0
  102. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/file/save/__init__.py +0 -0
  103. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/file/save/save_file.py +0 -0
  104. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/hotkey/__init__.py +0 -0
  105. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  106. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/json/__init__.py +0 -0
  107. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/json/json_file.py +0 -0
  108. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/json/json_repository.py +0 -0
  109. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/json_format/__init__.py +0 -0
  110. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/json_format/json_process.py +0 -0
  111. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/logging/__init__.py +0 -0
  112. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/logging/loggin_instance.py +0 -0
  113. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/__init__.py +0 -0
  114. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/france.py +0 -0
  115. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/germany.py +0 -0
  116. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/italy.py +0 -0
  117. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  118. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/russian.py +0 -0
  119. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  120. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/preset_schedule/__init__.py +0 -0
  121. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  122. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/redirect_manager/__init__.py +0 -0
  123. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  124. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/smart_pause/__init__.py +0 -0
  125. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  126. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/theme_schedule/__init__.py +0 -0
  127. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  128. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/utils/web_url.py +0 -0
  129. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/worker/__init__.py +0 -0
  130. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine/worker/qthread_worker.py +0 -0
  131. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine.egg-info/SOURCES.txt +0 -0
  132. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine.egg-info/dependency_links.txt +0 -0
  133. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine.egg-info/entry_points.txt +0 -0
  134. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine.egg-info/requires.txt +0 -0
  135. {frontengine-1.0.20 → frontengine-1.0.22}/frontengine.egg-info/top_level.txt +0 -0
  136. {frontengine-1.0.20 → frontengine-1.0.22}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.20
3
+ Version: 1.0.22
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
@@ -168,6 +168,46 @@ def idle_seconds():
168
168
  return None
169
169
 
170
170
 
171
+ class PetTimeline:
172
+ """
173
+ 寵物動作時間軸:一連串 (offset_seconds, action_dict),依經過秒數依序觸發。
174
+ A scene-timeline of (offset_seconds, action_dict) events fired in order as
175
+ elapsed time passes. Events are sorted; pop_due advances an internal index.
176
+ """
177
+
178
+ def __init__(self, events) -> None:
179
+ self._events = sorted(list(events), key=lambda item: item[0])
180
+ self._index = 0
181
+
182
+ def __len__(self) -> int:
183
+ return len(self._events)
184
+
185
+ def reset(self) -> None:
186
+ self._index = 0
187
+
188
+ def pop_due(self, elapsed: float) -> list:
189
+ due = []
190
+ while self._index < len(self._events) and self._events[self._index][0] <= elapsed:
191
+ due.append(self._events[self._index][1])
192
+ self._index += 1
193
+ return due
194
+
195
+
196
+ def parse_timeline(raw) -> list:
197
+ """把設定裡的 [{'at': 秒, ...}] 轉成 (offset, action_dict) 清單,忽略格式錯誤者。"""
198
+ events = []
199
+ if isinstance(raw, list):
200
+ for item in raw:
201
+ if not isinstance(item, dict) or "at" not in item:
202
+ continue
203
+ try:
204
+ offset = float(item["at"])
205
+ except (TypeError, ValueError):
206
+ continue
207
+ events.append((offset, item))
208
+ return events
209
+
210
+
171
211
  def pop_due_reminders(now, reminders):
172
212
  """
173
213
  依 now 分出已到期與未到期的提醒。reminders 為 (due_datetime, text) 清單。
@@ -797,7 +837,8 @@ class DesktopPetWidget(BaseWidget):
797
837
 
798
838
  def __init__(self, image_path: str, size: int = 128, speed: int = 3,
799
839
  behaviour: str = BEHAVIOUR_FLOOR, climb: bool = True, talk: bool = True,
800
- sound_path: Optional[str] = None, sit_on_windows: bool = True):
840
+ sound_path: Optional[str] = None, sit_on_windows: bool = True,
841
+ volume: float = 1.0):
801
842
  front_engine_logger.info(
802
843
  f"[DesktopPetWidget] Init | path={image_path}, size={size}, speed={speed}, "
803
844
  f"behaviour={behaviour}, climb={climb}, talk={talk}, sound={sound_path}, "
@@ -842,6 +883,7 @@ class DesktopPetWidget(BaseWidget):
842
883
  self._hunger = PetHunger(user_setting_dict.get("pet_hunger", 70))
843
884
  self._hunger_warned = False
844
885
  self._messages = self._load_messages()
886
+ self._volume = max(0.0, min(1.0, float(volume)))
845
887
  self._sound: Optional[QSoundEffect] = None
846
888
  self._init_sound(sound_path)
847
889
  self._sit_on_windows = bool(sit_on_windows)
@@ -860,7 +902,10 @@ class DesktopPetWidget(BaseWidget):
860
902
  self._now_provider = datetime.now
861
903
  self._reminders: list = []
862
904
  self._reminder_timer = QTimer(self)
863
- self._reminder_timer.timeout.connect(self._check_reminders)
905
+ self._reminder_timer.timeout.connect(self._on_schedule_tick)
906
+ # 場景時間軸 / Scene timeline of scheduled actions
907
+ self._timeline = PetTimeline(parse_timeline(user_setting_dict.get("pet_timeline", [])))
908
+ self._timeline_start = None
864
909
  self._bubble = SpeechBubble()
865
910
  self._chatter_timer = QTimer(self)
866
911
  self._chatter_timer.setSingleShot(True)
@@ -999,6 +1044,8 @@ class DesktopPetWidget(BaseWidget):
999
1044
  self._timer.start(max(10, int(interval_ms)))
1000
1045
  self._schedule_chatter()
1001
1046
  self._reminder_timer.start(10000)
1047
+ if self._timeline_start is None:
1048
+ self._timeline_start = self._now_provider()
1002
1049
 
1003
1050
  def _load_messages(self) -> dict:
1004
1051
  d = language_wrapper.language_word_dict
@@ -1067,6 +1114,39 @@ class DesktopPetWidget(BaseWidget):
1067
1114
  self._reminders.append((due, str(text)))
1068
1115
  front_engine_logger.info(f"[DesktopPetWidget] reminder set | due={due}, text={text}")
1069
1116
 
1117
+ def _on_schedule_tick(self) -> None:
1118
+ self._check_reminders()
1119
+ self._check_timeline()
1120
+
1121
+ def set_timeline(self, raw) -> None:
1122
+ """設定場景時間軸並從現在起算 / Set the timeline and start its clock now."""
1123
+ self._timeline = PetTimeline(parse_timeline(raw))
1124
+ self._timeline_start = self._now_provider()
1125
+
1126
+ def _check_timeline(self) -> None:
1127
+ if self._timeline is None or self._timeline_start is None or len(self._timeline) == 0:
1128
+ return
1129
+ elapsed = (self._now_provider() - self._timeline_start).total_seconds()
1130
+ for action in self._timeline.pop_due(elapsed):
1131
+ self._run_timeline_action(action)
1132
+
1133
+ def _run_timeline_action(self, action) -> None:
1134
+ if not isinstance(action, dict):
1135
+ return
1136
+ if action.get("say"):
1137
+ self.say(str(action["say"]))
1138
+ if action.get("sleep"):
1139
+ self.motion.sleep()
1140
+ if action.get("wake"):
1141
+ self.motion.wake()
1142
+ if action.get("feed"):
1143
+ self.feed()
1144
+ if action.get("status"):
1145
+ self.show_status()
1146
+ behaviour = action.get("behaviour")
1147
+ if behaviour in (BEHAVIOUR_FLOOR, BEHAVIOUR_WANDER, BEHAVIOUR_CHASE):
1148
+ self.motion.behaviour = behaviour
1149
+
1070
1150
  def _check_reminders(self) -> None:
1071
1151
  if not self._reminders:
1072
1152
  return
@@ -1101,6 +1181,7 @@ class DesktopPetWidget(BaseWidget):
1101
1181
  try:
1102
1182
  self._sound = QSoundEffect(self)
1103
1183
  self._sound.setSource(QUrl.fromLocalFile(str(path)))
1184
+ self._sound.setVolume(self._volume)
1104
1185
  except Exception as error: # pragma: no cover - audio backend guard
1105
1186
  front_engine_logger.warning(f"[DesktopPetWidget] sound load failed: {error!r}")
1106
1187
  self._sound = None
@@ -11,8 +11,10 @@ from frontengine.show.pet.desktop_pet import (
11
11
  from frontengine.ui.dialog.choose_file_dialog import choose_pet, choose_wav_sound
12
12
  from frontengine.ui.page.utils import (
13
13
  build_recent_combobox,
14
+ build_target_monitor_combobox,
14
15
  enable_file_drop,
15
16
  reload_recent_combobox,
17
+ resolve_preferred_monitor,
16
18
  )
17
19
  from frontengine.user_setting.user_setting_file import add_recent_file
18
20
  from frontengine.utils.logging.loggin_instance import front_engine_logger
@@ -79,6 +81,16 @@ class PetSettingUI(QWidget):
79
81
  self.sit_checkbox = QCheckBox(language_wrapper.language_word_dict.get("pet_sit_label", "Sit on windows"))
80
82
  self.sit_checkbox.setChecked(True)
81
83
 
84
+ # Target monitor + sound volume
85
+ self.target_monitor_label = QLabel(
86
+ language_wrapper.language_word_dict.get("target_monitor_label", "Target monitor"))
87
+ self.target_monitor_combobox = build_target_monitor_combobox()
88
+ self.volume_label = QLabel(language_wrapper.language_word_dict.get("pet_volume_label", "Volume"))
89
+ self.volume_combobox = QComboBox()
90
+ for label, value in (("0%", 0), ("25%", 25), ("50%", 50), ("75%", 75), ("100%", 100)):
91
+ self.volume_combobox.addItem(label, value)
92
+ self.volume_combobox.setCurrentText("100%")
93
+
82
94
  # Start
83
95
  self.start_button = QPushButton(language_wrapper.language_word_dict.get("pet_start", "Spawn pet"))
84
96
  self.start_button.clicked.connect(self.start_play_pet)
@@ -108,6 +120,10 @@ class PetSettingUI(QWidget):
108
120
  self.grid_layout.addWidget(self.start_button, 4, 0)
109
121
  self.grid_layout.addWidget(self.recent_files_label, 5, 0)
110
122
  self.grid_layout.addWidget(self.recent_files_combobox, 5, 1)
123
+ self.grid_layout.addWidget(self.target_monitor_label, 6, 0)
124
+ self.grid_layout.addWidget(self.target_monitor_combobox, 6, 1)
125
+ self.grid_layout.addWidget(self.volume_label, 6, 2)
126
+ self.grid_layout.addWidget(self.volume_combobox, 7, 2)
111
127
 
112
128
  def _spawn_pet(self) -> None:
113
129
  """建立、顯示並開始移動一隻寵物(供 Start 與右鍵複製共用)。"""
@@ -122,16 +138,30 @@ class PetSettingUI(QWidget):
122
138
  talk=self.talk_checkbox.isChecked(),
123
139
  sound_path=self.pet_sound_path,
124
140
  sit_on_windows=self.sit_checkbox.isChecked(),
141
+ volume=int(self.volume_combobox.currentData()) / 100.0,
125
142
  )
126
143
  pet.clone_requested.connect(self._spawn_pet)
127
144
  pet.set_peers_provider(lambda me=pet: self._peer_centers(me))
128
145
  pet.set_pet_window_flag()
129
146
  self.pet_list.append(pet)
130
- screen = QGuiApplication.primaryScreen()
131
- geometry = screen.availableGeometry() if screen is not None else None
132
147
  pet.show()
148
+ geometry = self._target_geometry()
133
149
  if geometry is not None:
134
- pet.start_moving((geometry.left(), geometry.top(), geometry.right(), geometry.bottom()))
150
+ pet.setScreen(geometry[1])
151
+ bounds = geometry[0]
152
+ pet.start_moving((bounds.left(), bounds.top(), bounds.right(), bounds.bottom()))
153
+
154
+ def _target_geometry(self):
155
+ """回傳 (available_geometry, screen) 依所選目標螢幕,否則主螢幕。"""
156
+ screens = QGuiApplication.screens()
157
+ index = resolve_preferred_monitor(self.target_monitor_combobox)
158
+ if index is not None and 0 <= index < len(screens):
159
+ screen = screens[index]
160
+ else:
161
+ screen = QGuiApplication.primaryScreen()
162
+ if screen is None:
163
+ return None
164
+ return (screen.availableGeometry(), screen)
135
165
 
136
166
  def _peer_centers(self, me) -> list:
137
167
  """回傳其他仍存活寵物的中心座標(略過已被銷毀者)。"""
@@ -221,6 +251,8 @@ class PetSettingUI(QWidget):
221
251
  "talk": self.talk_checkbox.isChecked(),
222
252
  "sound": self.pet_sound_path,
223
253
  "sit": self.sit_checkbox.isChecked(),
254
+ "target_monitor": self.target_monitor_combobox.currentText(),
255
+ "volume": self.volume_combobox.currentText(),
224
256
  }
225
257
 
226
258
  def set_state(self, state: dict) -> None:
@@ -248,3 +280,11 @@ class PetSettingUI(QWidget):
248
280
  self.pet_sound_path = str(state["sound"])
249
281
  if "sit" in state:
250
282
  self.sit_checkbox.setChecked(bool(state["sit"]))
283
+ if state.get("target_monitor") is not None:
284
+ index = self.target_monitor_combobox.findText(str(state["target_monitor"]))
285
+ if index >= 0:
286
+ self.target_monitor_combobox.setCurrentIndex(index)
287
+ if state.get("volume") is not None:
288
+ index = self.volume_combobox.findText(str(state["volume"]))
289
+ if index >= 0:
290
+ self.volume_combobox.setCurrentIndex(index)
@@ -42,6 +42,9 @@ user_setting_dict: Dict[str, Any] = {
42
42
  # 桌面寵物親密度(累積互動)
43
43
  # Desktop pet affection (accumulated interactions)
44
44
  "pet_affection": 0,
45
+ # 桌面寵物場景時間軸:[{"at": 秒, "say"/"sleep"/"feed"/"behaviour": ...}, ...]
46
+ # Desktop pet scene timeline of scheduled actions
47
+ "pet_timeline": [],
45
48
  }
46
49
 
47
50
 
@@ -246,6 +246,7 @@ english_word_dict = {
246
246
  "pet_pack_empty": "No sprites (walk/idle/sleep/climb/fall/drag) found in that folder.",
247
247
  "pet_talk_label": "Speech bubbles",
248
248
  "pet_sit_label": "Sit on windows",
249
+ "pet_volume_label": "Volume",
249
250
  "pet_chatter_morning": "Good morning!|Rise and shine!|Ready for the day?",
250
251
  "pet_chatter_afternoon": "Good afternoon!|Taking a break?|Keep it up!",
251
252
  "pet_chatter_evening": "Good evening!|How was your day?|Almost done?",
@@ -247,6 +247,7 @@ traditional_chinese_word_dict = {
247
247
  "pet_pack_empty": "該資料夾找不到精靈檔(walk/idle/sleep/climb/fall/drag)。",
248
248
  "pet_talk_label": "對話泡泡",
249
249
  "pet_sit_label": "站在視窗上",
250
+ "pet_volume_label": "音量",
250
251
  "pet_chatter_morning": "早安!|新的一天開始囉!|準備好了嗎?",
251
252
  "pet_chatter_afternoon": "午安!|要不要休息一下?|繼續加油!",
252
253
  "pet_chatter_evening": "晚上好!|今天過得如何?|快完成了嗎?",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.20
3
+ Version: 1.0.22
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
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "frontengine"
9
- version = "1.0.20"
9
+ version = "1.0.22"
10
10
  authors = [
11
11
  { name = "JE-Chen", email = "jechenmailman@gmail.com" },
12
12
  ]
File without changes
File without changes
File without changes