frontengine 1.0.5__tar.gz → 1.0.6__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.5 → frontengine-1.0.6}/PKG-INFO +1 -1
  2. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/pet/desktop_pet.py +128 -7
  3. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/pet/pet_setting_ui.py +7 -0
  4. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/english.py +6 -0
  5. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/traditional_chinese.py +6 -0
  6. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine.egg-info/PKG-INFO +1 -1
  7. {frontengine-1.0.5 → frontengine-1.0.6}/pyproject.toml +1 -1
  8. {frontengine-1.0.5 → frontengine-1.0.6}/LICENSE +0 -0
  9. {frontengine-1.0.5 → frontengine-1.0.6}/README.md +0 -0
  10. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/__init__.py +0 -0
  11. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/__main__.py +0 -0
  12. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/__init__.py +0 -0
  13. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/base_widget.py +0 -0
  14. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/gif/__init__.py +0 -0
  15. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/gif/paint_gif.py +0 -0
  16. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/image/__init__.py +0 -0
  17. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/image/paint_image.py +0 -0
  18. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/load/__init__.py +0 -0
  19. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/load/load_someone_make_ui.py +0 -0
  20. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/overlay_factory.py +0 -0
  21. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/particle/__init__.py +0 -0
  22. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/particle/particle_ui.py +0 -0
  23. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/pet/__init__.py +0 -0
  24. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/scene/__init__.py +0 -0
  25. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  26. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/scene/extend_graphic_view.py +0 -0
  27. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/scene/scene.py +0 -0
  28. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/sound_player/__init__.py +0 -0
  29. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/sound_player/sound_effect.py +0 -0
  30. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/sound_player/sound_player.py +0 -0
  31. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/text/__init__.py +0 -0
  32. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/text/draw_text.py +0 -0
  33. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/video/__init__.py +0 -0
  34. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/video/video_player.py +0 -0
  35. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/web/__init__.py +0 -0
  36. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/web/webview.py +0 -0
  37. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/show/window_helpers.py +0 -0
  38. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/system_tray/__init__.py +0 -0
  39. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/system_tray/extend_system_tray.py +0 -0
  40. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/__init__.py +0 -0
  41. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/color/__init__.py +0 -0
  42. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/color/global_color.py +0 -0
  43. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/dialog/__init__.py +0 -0
  44. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  45. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  46. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  47. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/main_ui.py +0 -0
  48. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/menu/__init__.py +0 -0
  49. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/menu/help_menu.py +0 -0
  50. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/menu/how_to_menu.py +0 -0
  51. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/menu/language_menu.py +0 -0
  52. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/menu/preset_menu.py +0 -0
  53. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/menu/settings_menu.py +0 -0
  54. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/__init__.py +0 -0
  55. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/control_center/__init__.py +0 -0
  56. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  57. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/gif/__init__.py +0 -0
  58. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  59. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/image/__init__.py +0 -0
  60. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  61. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/particle/__init__.py +0 -0
  62. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  63. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/pet/__init__.py +0 -0
  64. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  65. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  66. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  67. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  68. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  69. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  70. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  71. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  72. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  73. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  74. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  75. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  76. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/sound_player/__init__.py +0 -0
  77. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  78. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/text/__init__.py +0 -0
  79. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  80. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/utils.py +0 -0
  81. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/video/__init__.py +0 -0
  82. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  83. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/web/__init__.py +0 -0
  84. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  85. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/user_setting/__init__.py +0 -0
  86. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/user_setting/preset_repository.py +0 -0
  87. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/user_setting/scene_setting.py +0 -0
  88. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/user_setting/user_setting_file.py +0 -0
  89. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/__init__.py +0 -0
  90. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/browser/__init__.py +0 -0
  91. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/browser/browser.py +0 -0
  92. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/critical_exit/__init__.py +0 -0
  93. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  94. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  95. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  96. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/exception/__init__.py +0 -0
  97. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/exception/exception_tags.py +0 -0
  98. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/exception/exceptions.py +0 -0
  99. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/file/__init__.py +0 -0
  100. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/file/open/__init__.py +0 -0
  101. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/file/open/open_file.py +0 -0
  102. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/file/save/__init__.py +0 -0
  103. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/file/save/save_file.py +0 -0
  104. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/hotkey/__init__.py +0 -0
  105. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  106. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/json/__init__.py +0 -0
  107. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/json/json_file.py +0 -0
  108. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/json/json_repository.py +0 -0
  109. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/json_format/__init__.py +0 -0
  110. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/json_format/json_process.py +0 -0
  111. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/logging/__init__.py +0 -0
  112. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/logging/loggin_instance.py +0 -0
  113. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/__init__.py +0 -0
  114. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/france.py +0 -0
  115. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/germany.py +0 -0
  116. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/italy.py +0 -0
  117. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  118. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/russian.py +0 -0
  119. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  120. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/preset_schedule/__init__.py +0 -0
  121. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  122. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/redirect_manager/__init__.py +0 -0
  123. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  124. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/smart_pause/__init__.py +0 -0
  125. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  126. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/theme_schedule/__init__.py +0 -0
  127. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  128. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/utils/web_url.py +0 -0
  129. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/worker/__init__.py +0 -0
  130. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine/worker/qthread_worker.py +0 -0
  131. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine.egg-info/SOURCES.txt +0 -0
  132. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine.egg-info/dependency_links.txt +0 -0
  133. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine.egg-info/entry_points.txt +0 -0
  134. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine.egg-info/requires.txt +0 -0
  135. {frontengine-1.0.5 → frontengine-1.0.6}/frontengine.egg-info/top_level.txt +0 -0
  136. {frontengine-1.0.5 → frontengine-1.0.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.5
3
+ Version: 1.0.6
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
@@ -1,10 +1,11 @@
1
1
  import random as _random_module
2
+ from datetime import datetime
2
3
  from pathlib import Path
3
4
  from typing import Optional, Tuple
4
5
 
5
6
  from PySide6.QtCore import Qt, QRect, QTimer, Signal
6
- from PySide6.QtGui import QAction, QCursor, QMovie, QPainter, QPixmap, QTransform
7
- from PySide6.QtWidgets import QMenu, QMessageBox
7
+ from PySide6.QtGui import QAction, QColor, QCursor, QFont, QFontMetrics, QMovie, QPainter, QPixmap, QTransform
8
+ from PySide6.QtWidgets import QMenu, QMessageBox, QWidget
8
9
 
9
10
  from frontengine.show.base_widget import BaseWidget
10
11
  from frontengine.show.window_helpers import apply_overlay_window_flags
@@ -80,6 +81,74 @@ def derive_visual_state(dragging: bool, airborne: bool, surface: str, motion_sta
80
81
  return STATE_WALK
81
82
 
82
83
 
84
+ def message_bucket(hour: int) -> str:
85
+ """依小時回傳時段 / Time-of-day bucket for the given hour."""
86
+ hour = int(hour) % 24
87
+ if 5 <= hour < 12:
88
+ return "morning"
89
+ if 12 <= hour < 18:
90
+ return "afternoon"
91
+ if 18 <= hour < 22:
92
+ return "evening"
93
+ return "night"
94
+
95
+
96
+ def pick_message(hour: int, rng, messages: dict) -> str:
97
+ """
98
+ 依時段從對應句庫(加上通用句庫)隨機挑一句台詞;句庫皆空回傳空字串。
99
+ Pick a chatter line from the time bucket plus the generic pool. rng needs
100
+ a ``randrange`` method (injectable for tests).
101
+ """
102
+ pool = list(messages.get(message_bucket(hour), [])) + list(messages.get("any", []))
103
+ if not pool:
104
+ return ""
105
+ return pool[rng.randrange(len(pool))]
106
+
107
+
108
+ class SpeechBubble(QWidget):
109
+ """寵物頭上的小對話泡泡,數秒後自動消失 / A small speech bubble that auto-hides."""
110
+
111
+ def __init__(self) -> None:
112
+ super().__init__()
113
+ self._text = ""
114
+ self._font = QFont()
115
+ self._font.setPointSize(11)
116
+ self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
117
+ apply_overlay_window_flags(self, show_on_bottom=False, allow_input=False)
118
+ self._hide_timer = QTimer(self)
119
+ self._hide_timer.setSingleShot(True)
120
+ self._hide_timer.timeout.connect(self.hide)
121
+
122
+ def show_message(self, text: str, anchor: QWidget, duration_ms: int = 4000) -> None:
123
+ self._text = text
124
+ width = min(260, QFontMetrics(self._font).horizontalAdvance(text) + 28)
125
+ self.resize(max(60, width), 44)
126
+ self.reposition(anchor)
127
+ self.show()
128
+ self.raise_()
129
+ self.update()
130
+ self._hide_timer.start(max(500, int(duration_ms)))
131
+
132
+ def reposition(self, anchor: QWidget) -> None:
133
+ geo = anchor.frameGeometry()
134
+ self.move(geo.center().x() - self.width() // 2, geo.top() - self.height() - 6)
135
+
136
+ def paintEvent(self, event) -> None:
137
+ painter = QPainter(self)
138
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
139
+ rect = self.rect().adjusted(1, 1, -1, -1)
140
+ painter.setBrush(QColor(255, 255, 240, 235))
141
+ painter.setPen(QColor(90, 90, 90))
142
+ painter.drawRoundedRect(rect, 8, 8)
143
+ painter.setPen(QColor(30, 30, 30))
144
+ painter.setFont(self._font)
145
+ painter.drawText(
146
+ rect.adjusted(8, 2, -8, -2),
147
+ int(Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap),
148
+ self._text,
149
+ )
150
+
151
+
83
152
  class PetMotion:
84
153
  """
85
154
  純邏輯的寵物移動模型(不依賴 Qt,完全可測試)。包含:
@@ -317,10 +386,10 @@ class DesktopPetWidget(BaseWidget):
317
386
  clone_requested = Signal()
318
387
 
319
388
  def __init__(self, image_path: str, size: int = 128, speed: int = 3,
320
- behaviour: str = BEHAVIOUR_FLOOR, climb: bool = True):
389
+ behaviour: str = BEHAVIOUR_FLOOR, climb: bool = True, talk: bool = True):
321
390
  front_engine_logger.info(
322
391
  f"[DesktopPetWidget] Init | path={image_path}, size={size}, speed={speed}, "
323
- f"behaviour={behaviour}, climb={climb}"
392
+ f"behaviour={behaviour}, climb={climb}, talk={talk}"
324
393
  )
325
394
  super().__init__()
326
395
  self.opacity = 1.0
@@ -350,6 +419,16 @@ class DesktopPetWidget(BaseWidget):
350
419
  self._timer = QTimer(self)
351
420
  self._timer.timeout.connect(self._on_tick)
352
421
 
422
+ # Speech bubbles / chatter
423
+ self._talk = bool(talk)
424
+ self._moved = False
425
+ self._chatter_rng = _random_module.SystemRandom()
426
+ self._messages = self._load_messages()
427
+ self._bubble = SpeechBubble()
428
+ self._chatter_timer = QTimer(self)
429
+ self._chatter_timer.setSingleShot(True)
430
+ self._chatter_timer.timeout.connect(self._on_chatter)
431
+
353
432
  self.menu = QMenu(self)
354
433
  self.clone_action = QAction(language_wrapper.language_word_dict.get("pet_clone", "Clone"), self)
355
434
  self.clone_action.triggered.connect(self.clone_requested.emit)
@@ -426,6 +505,38 @@ class DesktopPetWidget(BaseWidget):
426
505
  self.motion.y = float(bottom - self.pet_size)
427
506
  self.move(int(self.motion.x), int(self.motion.y))
428
507
  self._timer.start(max(10, int(interval_ms)))
508
+ self._schedule_chatter()
509
+
510
+ def _load_messages(self) -> dict:
511
+ d = language_wrapper.language_word_dict
512
+
513
+ def lines(key: str, fallback: str) -> list:
514
+ return [part.strip() for part in str(d.get(key, fallback)).split("|") if part.strip()]
515
+
516
+ return {
517
+ "morning": lines("pet_chatter_morning", "Good morning!|Ready for the day?"),
518
+ "afternoon": lines("pet_chatter_afternoon", "Good afternoon!|Taking a break?"),
519
+ "evening": lines("pet_chatter_evening", "Good evening!|How was your day?"),
520
+ "night": lines("pet_chatter_night", "It's late - get some rest!|Still up?"),
521
+ "any": lines("pet_chatter_any", "Hi there!|(^_^)|Keep going!"),
522
+ }
523
+
524
+ def _schedule_chatter(self) -> None:
525
+ if self._talk:
526
+ self._chatter_timer.start(self._chatter_rng.randint(15000, 35000))
527
+
528
+ def _on_chatter(self) -> None:
529
+ self.say()
530
+ self._schedule_chatter()
531
+
532
+ def say(self, text: Optional[str] = None) -> None:
533
+ """顯示一句台詞(text 為 None 時依時段隨機挑)。"""
534
+ if not self._talk:
535
+ return
536
+ if text is None:
537
+ text = pick_message(datetime.now().hour, self._chatter_rng, self._messages)
538
+ if text:
539
+ self._bubble.show_message(text, self)
429
540
 
430
541
  def _on_tick(self) -> None:
431
542
  if self._dragging:
@@ -437,11 +548,14 @@ class DesktopPetWidget(BaseWidget):
437
548
  x, y = self.motion.step()
438
549
  self.move(x, y)
439
550
  self._set_active_sprite(self._visual_state())
551
+ if self._bubble.isVisible():
552
+ self._bubble.reposition(self)
440
553
 
441
554
  # --- dragging + throwing ---
442
555
  def mousePressEvent(self, event) -> None:
443
556
  if event.button() == Qt.MouseButton.LeftButton:
444
557
  self._dragging = True
558
+ self._moved = False
445
559
  self._last_move_delta = (0, 0)
446
560
  self._drag_offset = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
447
561
  self._set_active_sprite(STATE_DRAG)
@@ -451,6 +565,7 @@ class DesktopPetWidget(BaseWidget):
451
565
  if self._dragging and self._drag_offset is not None:
452
566
  new_top_left = event.globalPosition().toPoint() - self._drag_offset
453
567
  self._last_move_delta = (new_top_left.x() - self.x(), new_top_left.y() - self.y())
568
+ self._moved = True
454
569
  self.move(new_top_left)
455
570
  super().mouseMoveEvent(event)
456
571
 
@@ -459,9 +574,12 @@ class DesktopPetWidget(BaseWidget):
459
574
  self._dragging = False
460
575
  self.motion.x = float(self.x())
461
576
  self.motion.y = float(self.y())
462
- # Fling with the momentum of the last drag movement (floor mode).
463
- if self.motion.behaviour == BEHAVIOUR_FLOOR:
464
- self.motion.throw(self._last_move_delta[0], self._last_move_delta[1])
577
+ if self._moved:
578
+ # Fling with the momentum of the last drag movement (floor mode).
579
+ if self.motion.behaviour == BEHAVIOUR_FLOOR:
580
+ self.motion.throw(self._last_move_delta[0], self._last_move_delta[1])
581
+ else:
582
+ self.say() # a click (no drag) makes the pet talk
465
583
  super().mouseReleaseEvent(event)
466
584
 
467
585
  def contextMenuEvent(self, event) -> None:
@@ -470,6 +588,9 @@ class DesktopPetWidget(BaseWidget):
470
588
  def closeEvent(self, event) -> None:
471
589
  if self._timer.isActive():
472
590
  self._timer.stop()
591
+ if self._chatter_timer.isActive():
592
+ self._chatter_timer.stop()
593
+ self._bubble.close()
473
594
  for kind, obj in self._sprites.values():
474
595
  if kind == "movie":
475
596
  obj.stop()
@@ -69,6 +69,8 @@ class PetSettingUI(QWidget):
69
69
  # Climb walls / ceiling (only meaningful in floor mode)
70
70
  self.climb_checkbox = QCheckBox(language_wrapper.language_word_dict.get("pet_climb_label", "Climb walls"))
71
71
  self.climb_checkbox.setChecked(True)
72
+ self.talk_checkbox = QCheckBox(language_wrapper.language_word_dict.get("pet_talk_label", "Speech bubbles"))
73
+ self.talk_checkbox.setChecked(True)
72
74
 
73
75
  # Start
74
76
  self.start_button = QPushButton(language_wrapper.language_word_dict.get("pet_start", "Spawn pet"))
@@ -93,6 +95,7 @@ class PetSettingUI(QWidget):
93
95
  self.grid_layout.addWidget(self.behaviour_label, 3, 0)
94
96
  self.grid_layout.addWidget(self.behaviour_combobox, 3, 1)
95
97
  self.grid_layout.addWidget(self.climb_checkbox, 3, 2)
98
+ self.grid_layout.addWidget(self.talk_checkbox, 2, 2)
96
99
  self.grid_layout.addWidget(self.start_button, 4, 0)
97
100
  self.grid_layout.addWidget(self.recent_files_label, 5, 0)
98
101
  self.grid_layout.addWidget(self.recent_files_combobox, 5, 1)
@@ -107,6 +110,7 @@ class PetSettingUI(QWidget):
107
110
  speed=int(self.speed_combobox.currentText()),
108
111
  behaviour=self.behaviour_combobox.currentData(),
109
112
  climb=self.climb_checkbox.isChecked(),
113
+ talk=self.talk_checkbox.isChecked(),
110
114
  )
111
115
  pet.clone_requested.connect(self._spawn_pet)
112
116
  pet.set_pet_window_flag()
@@ -183,6 +187,7 @@ class PetSettingUI(QWidget):
183
187
  "speed": self.speed_combobox.currentText(),
184
188
  "behaviour": self.behaviour_combobox.currentData(),
185
189
  "climb": self.climb_checkbox.isChecked(),
190
+ "talk": self.talk_checkbox.isChecked(),
186
191
  }
187
192
 
188
193
  def set_state(self, state: dict) -> None:
@@ -204,3 +209,5 @@ class PetSettingUI(QWidget):
204
209
  self.behaviour_combobox.setCurrentIndex(index)
205
210
  if "climb" in state:
206
211
  self.climb_checkbox.setChecked(bool(state["climb"]))
212
+ if "talk" in state:
213
+ self.talk_checkbox.setChecked(bool(state["talk"]))
@@ -227,6 +227,12 @@ english_word_dict = {
227
227
  "pet_clone": "Clone",
228
228
  "pet_choose_pack": "Choose pet pack...",
229
229
  "pet_pack_empty": "No sprites (walk/idle/climb/fall/drag) found in that folder.",
230
+ "pet_talk_label": "Speech bubbles",
231
+ "pet_chatter_morning": "Good morning!|Rise and shine!|Ready for the day?",
232
+ "pet_chatter_afternoon": "Good afternoon!|Taking a break?|Keep it up!",
233
+ "pet_chatter_evening": "Good evening!|How was your day?|Almost done?",
234
+ "pet_chatter_night": "It's late - get some rest!|Still up?|Sweet dreams soon~",
235
+ "pet_chatter_any": "Hi there!|(^_^)|Keep going!|You've got this!",
230
236
  "pet_start": "Spawn pet",
231
237
  "pet_message_box_text": "Pet image not found",
232
238
  }
@@ -228,6 +228,12 @@ traditional_chinese_word_dict = {
228
228
  "pet_clone": "複製",
229
229
  "pet_choose_pack": "選擇動作包資料夾...",
230
230
  "pet_pack_empty": "該資料夾找不到精靈檔(walk/idle/climb/fall/drag)。",
231
+ "pet_talk_label": "對話泡泡",
232
+ "pet_chatter_morning": "早安!|新的一天開始囉!|準備好了嗎?",
233
+ "pet_chatter_afternoon": "午安!|要不要休息一下?|繼續加油!",
234
+ "pet_chatter_evening": "晚上好!|今天過得如何?|快完成了嗎?",
235
+ "pet_chatter_night": "很晚了,早點休息!|還沒睡嗎?|祝好夢~",
236
+ "pet_chatter_any": "嗨!|(^_^)|加油!|你可以的!",
231
237
  "pet_start": "召喚寵物",
232
238
  "pet_message_box_text": "找不到寵物圖片",
233
239
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.5
3
+ Version: 1.0.6
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.5"
9
+ version = "1.0.6"
10
10
  authors = [
11
11
  { name = "JE-Chen", email = "jechenmailman@gmail.com" },
12
12
  ]
File without changes
File without changes
File without changes