frontengine 1.0.7__tar.gz → 1.0.9__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.7 → frontengine-1.0.9}/PKG-INFO +1 -1
  2. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/pet/desktop_pet.py +88 -11
  3. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/pet/pet_setting_ui.py +18 -1
  4. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/user_setting/user_setting_file.py +3 -0
  5. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/english.py +3 -0
  6. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/traditional_chinese.py +3 -0
  7. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine.egg-info/PKG-INFO +1 -1
  8. {frontengine-1.0.7 → frontengine-1.0.9}/pyproject.toml +1 -1
  9. {frontengine-1.0.7 → frontengine-1.0.9}/LICENSE +0 -0
  10. {frontengine-1.0.7 → frontengine-1.0.9}/README.md +0 -0
  11. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/__init__.py +0 -0
  12. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/__main__.py +0 -0
  13. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/__init__.py +0 -0
  14. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/base_widget.py +0 -0
  15. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/gif/__init__.py +0 -0
  16. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/gif/paint_gif.py +0 -0
  17. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/image/__init__.py +0 -0
  18. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/image/paint_image.py +0 -0
  19. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/load/__init__.py +0 -0
  20. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/load/load_someone_make_ui.py +0 -0
  21. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/overlay_factory.py +0 -0
  22. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/particle/__init__.py +0 -0
  23. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/particle/particle_ui.py +0 -0
  24. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/pet/__init__.py +0 -0
  25. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/scene/__init__.py +0 -0
  26. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  27. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/scene/extend_graphic_view.py +0 -0
  28. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/scene/scene.py +0 -0
  29. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/sound_player/__init__.py +0 -0
  30. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/sound_player/sound_effect.py +0 -0
  31. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/sound_player/sound_player.py +0 -0
  32. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/text/__init__.py +0 -0
  33. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/text/draw_text.py +0 -0
  34. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/video/__init__.py +0 -0
  35. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/video/video_player.py +0 -0
  36. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/web/__init__.py +0 -0
  37. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/web/webview.py +0 -0
  38. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/show/window_helpers.py +0 -0
  39. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/system_tray/__init__.py +0 -0
  40. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/system_tray/extend_system_tray.py +0 -0
  41. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/__init__.py +0 -0
  42. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/color/__init__.py +0 -0
  43. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/color/global_color.py +0 -0
  44. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/dialog/__init__.py +0 -0
  45. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  46. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  47. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  48. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/main_ui.py +0 -0
  49. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/menu/__init__.py +0 -0
  50. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/menu/help_menu.py +0 -0
  51. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/menu/how_to_menu.py +0 -0
  52. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/menu/language_menu.py +0 -0
  53. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/menu/preset_menu.py +0 -0
  54. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/menu/settings_menu.py +0 -0
  55. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/__init__.py +0 -0
  56. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/control_center/__init__.py +0 -0
  57. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  58. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/gif/__init__.py +0 -0
  59. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  60. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/image/__init__.py +0 -0
  61. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  62. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/particle/__init__.py +0 -0
  63. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  64. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/pet/__init__.py +0 -0
  65. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  66. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  67. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  68. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  69. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  70. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  71. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  72. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  73. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  74. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  75. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  76. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  77. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/sound_player/__init__.py +0 -0
  78. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  79. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/text/__init__.py +0 -0
  80. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  81. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/utils.py +0 -0
  82. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/video/__init__.py +0 -0
  83. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  84. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/web/__init__.py +0 -0
  85. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  86. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/user_setting/__init__.py +0 -0
  87. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/user_setting/preset_repository.py +0 -0
  88. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/user_setting/scene_setting.py +0 -0
  89. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/__init__.py +0 -0
  90. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/browser/__init__.py +0 -0
  91. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/browser/browser.py +0 -0
  92. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/critical_exit/__init__.py +0 -0
  93. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  94. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  95. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  96. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/exception/__init__.py +0 -0
  97. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/exception/exception_tags.py +0 -0
  98. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/exception/exceptions.py +0 -0
  99. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/file/__init__.py +0 -0
  100. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/file/open/__init__.py +0 -0
  101. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/file/open/open_file.py +0 -0
  102. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/file/save/__init__.py +0 -0
  103. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/file/save/save_file.py +0 -0
  104. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/hotkey/__init__.py +0 -0
  105. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  106. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/json/__init__.py +0 -0
  107. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/json/json_file.py +0 -0
  108. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/json/json_repository.py +0 -0
  109. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/json_format/__init__.py +0 -0
  110. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/json_format/json_process.py +0 -0
  111. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/logging/__init__.py +0 -0
  112. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/logging/loggin_instance.py +0 -0
  113. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/__init__.py +0 -0
  114. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/france.py +0 -0
  115. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/germany.py +0 -0
  116. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/italy.py +0 -0
  117. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  118. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/russian.py +0 -0
  119. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  120. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/preset_schedule/__init__.py +0 -0
  121. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  122. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/redirect_manager/__init__.py +0 -0
  123. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  124. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/smart_pause/__init__.py +0 -0
  125. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  126. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/theme_schedule/__init__.py +0 -0
  127. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  128. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/utils/web_url.py +0 -0
  129. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/worker/__init__.py +0 -0
  130. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine/worker/qthread_worker.py +0 -0
  131. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine.egg-info/SOURCES.txt +0 -0
  132. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine.egg-info/dependency_links.txt +0 -0
  133. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine.egg-info/entry_points.txt +0 -0
  134. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine.egg-info/requires.txt +0 -0
  135. {frontengine-1.0.7 → frontengine-1.0.9}/frontengine.egg-info/top_level.txt +0 -0
  136. {frontengine-1.0.7 → frontengine-1.0.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.7
3
+ Version: 1.0.9
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
@@ -3,12 +3,14 @@ from datetime import datetime
3
3
  from pathlib import Path
4
4
  from typing import Optional, Tuple
5
5
 
6
- from PySide6.QtCore import Qt, QRect, QTimer, Signal
6
+ from PySide6.QtCore import Qt, QRect, QTimer, QUrl, Signal
7
7
  from PySide6.QtGui import QAction, QColor, QCursor, QFont, QFontMetrics, QMovie, QPainter, QPixmap, QTransform
8
+ from PySide6.QtMultimedia import QSoundEffect
8
9
  from PySide6.QtWidgets import QMenu, QMessageBox, QWidget
9
10
 
10
11
  from frontengine.show.base_widget import BaseWidget
11
12
  from frontengine.show.window_helpers import apply_overlay_window_flags
13
+ from frontengine.user_setting.user_setting_file import user_setting_dict
12
14
  from frontengine.utils.logging.loggin_instance import front_engine_logger
13
15
  from frontengine.utils.multi_language.language_wrapper import language_wrapper
14
16
 
@@ -106,18 +108,56 @@ def message_bucket(hour: int) -> str:
106
108
  return "night"
107
109
 
108
110
 
109
- def pick_message(hour: int, rng, messages: dict) -> str:
111
+ def pick_message(hour: int, rng, messages: dict, mood: Optional[str] = None) -> str:
110
112
  """
111
- 依時段從對應句庫(加上通用句庫)隨機挑一句台詞;句庫皆空回傳空字串。
112
- Pick a chatter line from the time bucket plus the generic pool. rng needs
113
- a ``randrange`` method (injectable for tests).
113
+ 挑一句台詞:先放心情句庫(若有),再放時段句庫、通用句庫。皆空回傳空字串。
114
+ Pick a chatter line: mood pool (if any) first, then the time-of-day pool,
115
+ then the generic pool. rng needs ``randrange``.
114
116
  """
115
- pool = list(messages.get(message_bucket(hour), [])) + list(messages.get("any", []))
117
+ pool = []
118
+ if mood and mood in messages:
119
+ pool += list(messages[mood])
120
+ pool += list(messages.get(message_bucket(hour), []))
121
+ pool += list(messages.get("any", []))
116
122
  if not pool:
117
123
  return ""
118
124
  return pool[rng.randrange(len(pool))]
119
125
 
120
126
 
127
+ class PetMood:
128
+ """
129
+ 寵物心情值 0~100:互動會上升、被忽略會慢慢下降;level() 回傳 happy/content/sad。
130
+ Pet happiness 0..100: petting raises it, neglect decays it; level() buckets it.
131
+ """
132
+
133
+ HAPPY = "happy"
134
+ CONTENT = "content"
135
+ SAD = "sad"
136
+
137
+ def __init__(self, value: int = 60) -> None:
138
+ self.value = self._clamp(value)
139
+
140
+ @staticmethod
141
+ def _clamp(value) -> int:
142
+ try:
143
+ return max(0, min(100, int(value)))
144
+ except (TypeError, ValueError):
145
+ return 60
146
+
147
+ def pet(self, amount: int = 12) -> None:
148
+ self.value = self._clamp(self.value + amount)
149
+
150
+ def decay(self, amount: int = 1) -> None:
151
+ self.value = self._clamp(self.value - amount)
152
+
153
+ def level(self) -> str:
154
+ if self.value >= 70:
155
+ return self.HAPPY
156
+ if self.value <= 30:
157
+ return self.SAD
158
+ return self.CONTENT
159
+
160
+
121
161
  class SpeechBubble(QWidget):
122
162
  """寵物頭上的小對話泡泡,數秒後自動消失 / A small speech bubble that auto-hides."""
123
163
 
@@ -412,10 +452,11 @@ class DesktopPetWidget(BaseWidget):
412
452
  clone_requested = Signal()
413
453
 
414
454
  def __init__(self, image_path: str, size: int = 128, speed: int = 3,
415
- behaviour: str = BEHAVIOUR_FLOOR, climb: bool = True, talk: bool = True):
455
+ behaviour: str = BEHAVIOUR_FLOOR, climb: bool = True, talk: bool = True,
456
+ sound_path: Optional[str] = None):
416
457
  front_engine_logger.info(
417
458
  f"[DesktopPetWidget] Init | path={image_path}, size={size}, speed={speed}, "
418
- f"behaviour={behaviour}, climb={climb}, talk={talk}"
459
+ f"behaviour={behaviour}, climb={climb}, talk={talk}, sound={sound_path}"
419
460
  )
420
461
  super().__init__()
421
462
  self.opacity = 1.0
@@ -445,11 +486,14 @@ class DesktopPetWidget(BaseWidget):
445
486
  self._timer = QTimer(self)
446
487
  self._timer.timeout.connect(self._on_tick)
447
488
 
448
- # Speech bubbles / chatter
489
+ # Speech bubbles / chatter / mood
449
490
  self._talk = bool(talk)
450
491
  self._moved = False
451
492
  self._chatter_rng = _random_module.SystemRandom()
493
+ self._mood = PetMood(user_setting_dict.get("pet_mood", 60))
452
494
  self._messages = self._load_messages()
495
+ self._sound: Optional[QSoundEffect] = None
496
+ self._init_sound(sound_path)
453
497
  self._bubble = SpeechBubble()
454
498
  self._chatter_timer = QTimer(self)
455
499
  self._chatter_timer.setSingleShot(True)
@@ -544,23 +588,53 @@ class DesktopPetWidget(BaseWidget):
544
588
  "afternoon": lines("pet_chatter_afternoon", "Good afternoon!|Taking a break?"),
545
589
  "evening": lines("pet_chatter_evening", "Good evening!|How was your day?"),
546
590
  "night": lines("pet_chatter_night", "It's late - get some rest!|Still up?"),
591
+ "happy": lines("pet_chatter_happy", "I'm so happy!|You're the best!|(^o^)"),
592
+ "sad": lines("pet_chatter_sad", "Feeling a bit lonely...|Pet me?|(._.)"),
547
593
  "any": lines("pet_chatter_any", "Hi there!|(^_^)|Keep going!"),
548
594
  }
549
595
 
596
+ def _persist_mood(self) -> None:
597
+ user_setting_dict["pet_mood"] = self._mood.value
598
+
599
+ def _init_sound(self, sound_path: Optional[str]) -> None:
600
+ if not sound_path:
601
+ return
602
+ path = Path(sound_path)
603
+ if not (path.exists() and path.is_file()):
604
+ return
605
+ try:
606
+ self._sound = QSoundEffect(self)
607
+ self._sound.setSource(QUrl.fromLocalFile(str(path)))
608
+ except Exception as error: # pragma: no cover - audio backend guard
609
+ front_engine_logger.warning(f"[DesktopPetWidget] sound load failed: {error!r}")
610
+ self._sound = None
611
+
612
+ def _play_sound(self) -> None:
613
+ if self._sound is None:
614
+ return
615
+ try:
616
+ self._sound.play()
617
+ except Exception as error: # pragma: no cover - audio backend guard
618
+ front_engine_logger.warning(f"[DesktopPetWidget] sound play failed: {error!r}")
619
+
550
620
  def _schedule_chatter(self) -> None:
551
621
  if self._talk:
552
622
  self._chatter_timer.start(self._chatter_rng.randint(15000, 35000))
553
623
 
554
624
  def _on_chatter(self) -> None:
625
+ self._mood.decay() # a little neglect over time
626
+ self._persist_mood()
555
627
  self.say()
556
628
  self._schedule_chatter()
557
629
 
558
630
  def say(self, text: Optional[str] = None) -> None:
559
- """顯示一句台詞(text 為 None 時依時段隨機挑)。"""
631
+ """顯示一句台詞(text 為 None 時依心情/時段隨機挑)。"""
560
632
  if not self._talk:
561
633
  return
562
634
  if text is None:
563
- text = pick_message(datetime.now().hour, self._chatter_rng, self._messages)
635
+ level = self._mood.level()
636
+ mood = level if level in (PetMood.HAPPY, PetMood.SAD) else None
637
+ text = pick_message(datetime.now().hour, self._chatter_rng, self._messages, mood=mood)
564
638
  if text:
565
639
  self._bubble.show_message(text, self)
566
640
 
@@ -585,6 +659,9 @@ class DesktopPetWidget(BaseWidget):
585
659
  self._last_move_delta = (0, 0)
586
660
  self._drag_offset = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
587
661
  self.motion.wake() # interacting wakes a sleeping pet
662
+ self._mood.pet() # petting cheers it up
663
+ self._persist_mood()
664
+ self._play_sound()
588
665
  self._set_active_sprite(STATE_DRAG)
589
666
  super().mousePressEvent(event)
590
667
 
@@ -8,7 +8,7 @@ from PySide6.QtWidgets import (
8
8
  from frontengine.show.pet.desktop_pet import (
9
9
  DesktopPetWidget, BEHAVIOUR_FLOOR, BEHAVIOUR_WANDER, BEHAVIOUR_CHASE, scan_pet_pack,
10
10
  )
11
- from frontengine.ui.dialog.choose_file_dialog import choose_pet
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
14
  enable_file_drop,
@@ -32,6 +32,7 @@ class PetSettingUI(QWidget):
32
32
  self.pet_list: list = []
33
33
  self.ready_to_play = False
34
34
  self.pet_image_path: Optional[str] = None
35
+ self.pet_sound_path: Optional[str] = None
35
36
 
36
37
  # Choose file
37
38
  self.choose_file_button = QPushButton(
@@ -42,6 +43,10 @@ class PetSettingUI(QWidget):
42
43
  language_wrapper.language_word_dict.get("pet_choose_pack", "Choose pet pack...")
43
44
  )
44
45
  self.choose_pack_button.clicked.connect(self.choose_pack)
46
+ self.choose_sound_button = QPushButton(
47
+ language_wrapper.language_word_dict.get("pet_choose_sound", "Choose sound (optional)")
48
+ )
49
+ self.choose_sound_button.clicked.connect(self.choose_sound)
45
50
  self.ready_label = QLabel(language_wrapper.language_word_dict.get("Not Ready"))
46
51
 
47
52
  # Size
@@ -88,6 +93,7 @@ class PetSettingUI(QWidget):
88
93
  self.grid_layout.addWidget(self.choose_file_button, 0, 0)
89
94
  self.grid_layout.addWidget(self.ready_label, 0, 1)
90
95
  self.grid_layout.addWidget(self.choose_pack_button, 0, 2)
96
+ self.grid_layout.addWidget(self.choose_sound_button, 1, 2)
91
97
  self.grid_layout.addWidget(self.size_label, 1, 0)
92
98
  self.grid_layout.addWidget(self.size_combobox, 1, 1)
93
99
  self.grid_layout.addWidget(self.speed_label, 2, 0)
@@ -111,6 +117,7 @@ class PetSettingUI(QWidget):
111
117
  behaviour=self.behaviour_combobox.currentData(),
112
118
  climb=self.climb_checkbox.isChecked(),
113
119
  talk=self.talk_checkbox.isChecked(),
120
+ sound_path=self.pet_sound_path,
114
121
  )
115
122
  pet.clone_requested.connect(self._spawn_pet)
116
123
  pet.set_pet_window_flag()
@@ -141,6 +148,13 @@ class PetSettingUI(QWidget):
141
148
  add_recent_file("pet", self.pet_image_path)
142
149
  reload_recent_combobox(self.recent_files_combobox, "pet")
143
150
 
151
+ def choose_sound(self) -> None:
152
+ """選擇點擊時播放的音效(選填)/ Choose an optional click sound."""
153
+ front_engine_logger.info("[PetSettingUI] choose_sound")
154
+ path = choose_wav_sound(self)
155
+ if path:
156
+ self.pet_sound_path = path
157
+
144
158
  def choose_pack(self) -> None:
145
159
  """選擇動作包資料夾(依 walk/idle/climb/fall/drag 檔名對應狀態)。"""
146
160
  front_engine_logger.info("[PetSettingUI] choose_pack")
@@ -188,6 +202,7 @@ class PetSettingUI(QWidget):
188
202
  "behaviour": self.behaviour_combobox.currentData(),
189
203
  "climb": self.climb_checkbox.isChecked(),
190
204
  "talk": self.talk_checkbox.isChecked(),
205
+ "sound": self.pet_sound_path,
191
206
  }
192
207
 
193
208
  def set_state(self, state: dict) -> None:
@@ -211,3 +226,5 @@ class PetSettingUI(QWidget):
211
226
  self.climb_checkbox.setChecked(bool(state["climb"]))
212
227
  if "talk" in state:
213
228
  self.talk_checkbox.setChecked(bool(state["talk"]))
229
+ if state.get("sound"):
230
+ self.pet_sound_path = str(state["sound"])
@@ -33,6 +33,9 @@ user_setting_dict: Dict[str, Any] = {
33
33
  # 每天在指定時間自動套用預設集
34
34
  # Auto-apply a preset at a configured time each day
35
35
  "preset_schedule": dict(DEFAULT_PRESET_SCHEDULE),
36
+ # 桌面寵物心情值(0~100),跨工作階段保存
37
+ # Desktop pet mood (0..100), persisted across sessions
38
+ "pet_mood": 60,
36
39
  }
37
40
 
38
41
 
@@ -226,6 +226,7 @@ english_word_dict = {
226
226
  "pet_climb_label": "Climb walls",
227
227
  "pet_clone": "Clone",
228
228
  "pet_choose_pack": "Choose pet pack...",
229
+ "pet_choose_sound": "Choose sound (optional)",
229
230
  "pet_pack_empty": "No sprites (walk/idle/sleep/climb/fall/drag) found in that folder.",
230
231
  "pet_talk_label": "Speech bubbles",
231
232
  "pet_chatter_morning": "Good morning!|Rise and shine!|Ready for the day?",
@@ -233,6 +234,8 @@ english_word_dict = {
233
234
  "pet_chatter_evening": "Good evening!|How was your day?|Almost done?",
234
235
  "pet_chatter_night": "It's late - get some rest!|Still up?|Sweet dreams soon~",
235
236
  "pet_chatter_any": "Hi there!|(^_^)|Keep going!|You've got this!",
237
+ "pet_chatter_happy": "I'm so happy!|You're the best!|(^o^)/|Best day ever!",
238
+ "pet_chatter_sad": "Feeling a bit lonely...|Pet me?|(._.)|Don't forget about me~",
236
239
  "pet_start": "Spawn pet",
237
240
  "pet_message_box_text": "Pet image not found",
238
241
  }
@@ -227,6 +227,7 @@ traditional_chinese_word_dict = {
227
227
  "pet_climb_label": "攀爬牆壁",
228
228
  "pet_clone": "複製",
229
229
  "pet_choose_pack": "選擇動作包資料夾...",
230
+ "pet_choose_sound": "選擇音效(選填)",
230
231
  "pet_pack_empty": "該資料夾找不到精靈檔(walk/idle/sleep/climb/fall/drag)。",
231
232
  "pet_talk_label": "對話泡泡",
232
233
  "pet_chatter_morning": "早安!|新的一天開始囉!|準備好了嗎?",
@@ -234,6 +235,8 @@ traditional_chinese_word_dict = {
234
235
  "pet_chatter_evening": "晚上好!|今天過得如何?|快完成了嗎?",
235
236
  "pet_chatter_night": "很晚了,早點休息!|還沒睡嗎?|祝好夢~",
236
237
  "pet_chatter_any": "嗨!|(^_^)|加油!|你可以的!",
238
+ "pet_chatter_happy": "我好開心!|你最棒了!|(^o^)/|今天真美好!",
239
+ "pet_chatter_sad": "有點寂寞…|摸摸我好嗎?|(._.)|別忘了我~",
237
240
  "pet_start": "召喚寵物",
238
241
  "pet_message_box_text": "找不到寵物圖片",
239
242
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.7
3
+ Version: 1.0.9
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.7"
9
+ version = "1.0.9"
10
10
  authors = [
11
11
  { name = "JE-Chen", email = "jechenmailman@gmail.com" },
12
12
  ]
File without changes
File without changes
File without changes