frontengine 1.0.4__tar.gz → 1.0.5__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.4 → frontengine-1.0.5}/PKG-INFO +1 -1
  2. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/pet/desktop_pet.py +118 -17
  3. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/pet/pet_setting_ui.py +29 -2
  4. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/english.py +2 -0
  5. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/traditional_chinese.py +2 -0
  6. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine.egg-info/PKG-INFO +1 -1
  7. {frontengine-1.0.4 → frontengine-1.0.5}/pyproject.toml +1 -1
  8. {frontengine-1.0.4 → frontengine-1.0.5}/LICENSE +0 -0
  9. {frontengine-1.0.4 → frontengine-1.0.5}/README.md +0 -0
  10. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/__init__.py +0 -0
  11. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/__main__.py +0 -0
  12. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/__init__.py +0 -0
  13. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/base_widget.py +0 -0
  14. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/gif/__init__.py +0 -0
  15. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/gif/paint_gif.py +0 -0
  16. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/image/__init__.py +0 -0
  17. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/image/paint_image.py +0 -0
  18. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/load/__init__.py +0 -0
  19. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/load/load_someone_make_ui.py +0 -0
  20. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/overlay_factory.py +0 -0
  21. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/particle/__init__.py +0 -0
  22. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/particle/particle_ui.py +0 -0
  23. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/pet/__init__.py +0 -0
  24. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/scene/__init__.py +0 -0
  25. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  26. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/scene/extend_graphic_view.py +0 -0
  27. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/scene/scene.py +0 -0
  28. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/sound_player/__init__.py +0 -0
  29. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/sound_player/sound_effect.py +0 -0
  30. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/sound_player/sound_player.py +0 -0
  31. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/text/__init__.py +0 -0
  32. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/text/draw_text.py +0 -0
  33. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/video/__init__.py +0 -0
  34. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/video/video_player.py +0 -0
  35. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/web/__init__.py +0 -0
  36. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/web/webview.py +0 -0
  37. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/show/window_helpers.py +0 -0
  38. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/system_tray/__init__.py +0 -0
  39. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/system_tray/extend_system_tray.py +0 -0
  40. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/__init__.py +0 -0
  41. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/color/__init__.py +0 -0
  42. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/color/global_color.py +0 -0
  43. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/dialog/__init__.py +0 -0
  44. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  45. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  46. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  47. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/main_ui.py +0 -0
  48. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/menu/__init__.py +0 -0
  49. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/menu/help_menu.py +0 -0
  50. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/menu/how_to_menu.py +0 -0
  51. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/menu/language_menu.py +0 -0
  52. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/menu/preset_menu.py +0 -0
  53. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/menu/settings_menu.py +0 -0
  54. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/__init__.py +0 -0
  55. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/control_center/__init__.py +0 -0
  56. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  57. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/gif/__init__.py +0 -0
  58. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  59. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/image/__init__.py +0 -0
  60. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  61. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/particle/__init__.py +0 -0
  62. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  63. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/pet/__init__.py +0 -0
  64. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  65. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  66. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  67. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  68. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  69. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  70. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  71. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  72. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  73. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  74. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  75. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  76. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/sound_player/__init__.py +0 -0
  77. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  78. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/text/__init__.py +0 -0
  79. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  80. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/utils.py +0 -0
  81. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/video/__init__.py +0 -0
  82. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  83. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/web/__init__.py +0 -0
  84. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  85. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/user_setting/__init__.py +0 -0
  86. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/user_setting/preset_repository.py +0 -0
  87. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/user_setting/scene_setting.py +0 -0
  88. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/user_setting/user_setting_file.py +0 -0
  89. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/__init__.py +0 -0
  90. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/browser/__init__.py +0 -0
  91. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/browser/browser.py +0 -0
  92. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/critical_exit/__init__.py +0 -0
  93. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  94. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  95. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  96. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/exception/__init__.py +0 -0
  97. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/exception/exception_tags.py +0 -0
  98. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/exception/exceptions.py +0 -0
  99. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/file/__init__.py +0 -0
  100. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/file/open/__init__.py +0 -0
  101. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/file/open/open_file.py +0 -0
  102. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/file/save/__init__.py +0 -0
  103. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/file/save/save_file.py +0 -0
  104. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/hotkey/__init__.py +0 -0
  105. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  106. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/json/__init__.py +0 -0
  107. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/json/json_file.py +0 -0
  108. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/json/json_repository.py +0 -0
  109. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/json_format/__init__.py +0 -0
  110. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/json_format/json_process.py +0 -0
  111. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/logging/__init__.py +0 -0
  112. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/logging/loggin_instance.py +0 -0
  113. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/__init__.py +0 -0
  114. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/france.py +0 -0
  115. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/germany.py +0 -0
  116. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/italy.py +0 -0
  117. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  118. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/russian.py +0 -0
  119. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  120. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/preset_schedule/__init__.py +0 -0
  121. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  122. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/redirect_manager/__init__.py +0 -0
  123. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  124. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/smart_pause/__init__.py +0 -0
  125. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  126. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/theme_schedule/__init__.py +0 -0
  127. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  128. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/utils/web_url.py +0 -0
  129. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/worker/__init__.py +0 -0
  130. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine/worker/qthread_worker.py +0 -0
  131. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine.egg-info/SOURCES.txt +0 -0
  132. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine.egg-info/dependency_links.txt +0 -0
  133. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine.egg-info/entry_points.txt +0 -0
  134. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine.egg-info/requires.txt +0 -0
  135. {frontengine-1.0.4 → frontengine-1.0.5}/frontengine.egg-info/top_level.txt +0 -0
  136. {frontengine-1.0.4 → frontengine-1.0.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.4
3
+ Version: 1.0.5
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
@@ -22,6 +22,63 @@ SURFACE_LEFT = "left"
22
22
  SURFACE_CEILING = "ceiling"
23
23
  SURFACE_RIGHT = "right"
24
24
 
25
+ # 視覺狀態(決定顯示哪張精靈)/ Visual states (which sprite to show)
26
+ STATE_WALK = "walk"
27
+ STATE_IDLE = "idle"
28
+ STATE_CLIMB = "climb"
29
+ STATE_FALL = "fall"
30
+ STATE_DRAG = "drag"
31
+
32
+ # 動作包(資料夾)中,各狀態可接受的檔名(不含副檔名,先命中者為準)
33
+ # Accepted file stems per state inside a pet pack folder (first match wins).
34
+ _PACK_STATE_ALIASES = {
35
+ STATE_WALK: ("walk", "run", "move"),
36
+ STATE_IDLE: ("idle", "sit", "stand", "sleep"),
37
+ STATE_CLIMB: ("climb", "grab", "wall"),
38
+ STATE_FALL: ("fall", "jump"),
39
+ STATE_DRAG: ("drag", "pinch", "grabbed", "held"),
40
+ }
41
+ _PACK_EXTS = (".gif", ".webp", ".png", ".jpg", ".jpeg")
42
+
43
+
44
+ def scan_pet_pack(folder: str) -> dict:
45
+ """
46
+ 掃描動作包資料夾,回傳「視覺狀態 -> 圖片路徑」對應。非資料夾回傳空 dict。
47
+ Scan a pet-pack folder and return a ``state -> image path`` mapping.
48
+ Returns {} when `folder` is not a directory.
49
+ """
50
+ result: dict = {}
51
+ try:
52
+ folder_path = Path(folder)
53
+ if not folder_path.is_dir():
54
+ return result
55
+ by_stem = {
56
+ path.stem.lower(): path
57
+ for path in sorted(folder_path.iterdir())
58
+ if path.is_file() and path.suffix.lower() in _PACK_EXTS
59
+ }
60
+ for state, aliases in _PACK_STATE_ALIASES.items():
61
+ for alias in aliases:
62
+ if alias in by_stem:
63
+ result[state] = str(by_stem[alias])
64
+ break
65
+ except OSError:
66
+ pass
67
+ return result
68
+
69
+
70
+ def derive_visual_state(dragging: bool, airborne: bool, surface: str, motion_state: str) -> str:
71
+ """依目前情形推導應顯示的視覺狀態 / Derive which sprite state to show."""
72
+ if dragging:
73
+ return STATE_DRAG
74
+ if airborne:
75
+ return STATE_FALL
76
+ if surface in (SURFACE_LEFT, SURFACE_RIGHT, SURFACE_CEILING):
77
+ return STATE_CLIMB
78
+ if surface == SURFACE_FLOOR and motion_state == "idle":
79
+ return STATE_IDLE
80
+ return STATE_WALK
81
+
25
82
 
26
83
  class PetMotion:
27
84
  """
@@ -269,21 +326,17 @@ class DesktopPetWidget(BaseWidget):
269
326
  self.opacity = 1.0
270
327
  self.pet_size: int = max(16, int(size))
271
328
  self.image_path: Path = Path(image_path)
272
- self.movie: Optional[QMovie] = None
273
- self.pixmap: Optional[QPixmap] = None
274
329
  self._dragging: bool = False
275
330
  self._drag_offset = None
276
331
  self._last_move_delta: Tuple[int, int] = (0, 0)
277
-
278
- if self.image_path.exists() and self.image_path.is_file():
279
- if self.image_path.suffix.lower() in (".gif", ".webp"):
280
- self.movie = QMovie(str(self.image_path))
281
- self.movie.frameChanged.connect(self.update)
282
- self.movie.start()
283
- else:
284
- self.pixmap = QPixmap(str(self.image_path))
285
- else:
286
- front_engine_logger.error(f"[DesktopPetWidget] File not found: {self.image_path}")
332
+ # 視覺狀態 -> ("movie"|"pixmap", 物件) / state -> sprite
333
+ self._sprites: dict = {}
334
+ self._active = None
335
+ self._active_state = None
336
+
337
+ self._load_sprites(self.image_path)
338
+ if not self._sprites:
339
+ front_engine_logger.error(f"[DesktopPetWidget] No sprite found: {self.image_path}")
287
340
  message_box = QMessageBox(self)
288
341
  message_box.setText(language_wrapper.language_word_dict.get("pet_message_box_text", "Pet image not found"))
289
342
  message_box.show()
@@ -292,6 +345,7 @@ class DesktopPetWidget(BaseWidget):
292
345
  self.motion = PetMotion(
293
346
  0, 0, self.pet_size, self.pet_size, (0, 0, 1920, 1080), speed, behaviour, climb=climb
294
347
  )
348
+ self._set_active_sprite(STATE_WALK)
295
349
 
296
350
  self._timer = QTimer(self)
297
351
  self._timer.timeout.connect(self._on_tick)
@@ -308,10 +362,53 @@ class DesktopPetWidget(BaseWidget):
308
362
  """寵物需接收滑鼠以便拖曳,故 allow_input=True 並置頂。"""
309
363
  apply_overlay_window_flags(self, show_on_bottom=False, allow_input=True)
310
364
 
365
+ def _load_sprite(self, path: str):
366
+ if Path(path).suffix.lower() in (".gif", ".webp"):
367
+ movie = QMovie(str(path))
368
+ movie.frameChanged.connect(self.update)
369
+ return ("movie", movie)
370
+ return ("pixmap", QPixmap(str(path)))
371
+
372
+ def _load_sprites(self, image_path: Path) -> None:
373
+ """單一檔 -> 所有狀態共用;資料夾 -> 依檔名對應各狀態。"""
374
+ if image_path.is_dir():
375
+ for state, path in scan_pet_pack(str(image_path)).items():
376
+ self._sprites[state] = self._load_sprite(path)
377
+ elif image_path.is_file():
378
+ self._sprites["default"] = self._load_sprite(str(image_path))
379
+
380
+ def _sprite_for(self, state: str):
381
+ if "default" in self._sprites:
382
+ return self._sprites["default"]
383
+ if state in self._sprites:
384
+ return self._sprites[state]
385
+ for fallback in (STATE_WALK, STATE_IDLE):
386
+ if fallback in self._sprites:
387
+ return self._sprites[fallback]
388
+ return next(iter(self._sprites.values()), None)
389
+
390
+ def _set_active_sprite(self, state: str) -> None:
391
+ sprite = self._sprite_for(state)
392
+ if sprite is None or sprite is self._active:
393
+ return
394
+ if self._active is not None and self._active[0] == "movie":
395
+ self._active[1].stop()
396
+ self._active = sprite
397
+ self._active_state = state
398
+ if sprite[0] == "movie":
399
+ sprite[1].start()
400
+ self.update()
401
+
402
+ def _visual_state(self) -> str:
403
+ return derive_visual_state(
404
+ self._dragging, self.motion._airborne, self.motion.surface, self.motion.state
405
+ )
406
+
311
407
  def _current_pixmap(self) -> Optional[QPixmap]:
312
- if self.movie is not None:
313
- return self.movie.currentPixmap()
314
- return self.pixmap
408
+ if self._active is None:
409
+ return None
410
+ kind, obj = self._active
411
+ return obj.currentPixmap() if kind == "movie" else obj
315
412
 
316
413
  def draw_content(self, painter: QPainter) -> None:
317
414
  pixmap = self._current_pixmap()
@@ -332,12 +429,14 @@ class DesktopPetWidget(BaseWidget):
332
429
 
333
430
  def _on_tick(self) -> None:
334
431
  if self._dragging:
432
+ self._set_active_sprite(STATE_DRAG)
335
433
  return
336
434
  if self.motion.behaviour == BEHAVIOUR_CHASE:
337
435
  cursor = QCursor.pos()
338
436
  self.motion.set_target(cursor.x(), cursor.y())
339
437
  x, y = self.motion.step()
340
438
  self.move(x, y)
439
+ self._set_active_sprite(self._visual_state())
341
440
 
342
441
  # --- dragging + throwing ---
343
442
  def mousePressEvent(self, event) -> None:
@@ -345,6 +444,7 @@ class DesktopPetWidget(BaseWidget):
345
444
  self._dragging = True
346
445
  self._last_move_delta = (0, 0)
347
446
  self._drag_offset = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
447
+ self._set_active_sprite(STATE_DRAG)
348
448
  super().mousePressEvent(event)
349
449
 
350
450
  def mouseMoveEvent(self, event) -> None:
@@ -370,6 +470,7 @@ class DesktopPetWidget(BaseWidget):
370
470
  def closeEvent(self, event) -> None:
371
471
  if self._timer.isActive():
372
472
  self._timer.stop()
373
- if self.movie is not None:
374
- self.movie.stop()
473
+ for kind, obj in self._sprites.values():
474
+ if kind == "movie":
475
+ obj.stop()
375
476
  super().closeEvent(event)
@@ -2,11 +2,11 @@ from typing import Optional
2
2
 
3
3
  from PySide6.QtGui import QGuiApplication
4
4
  from PySide6.QtWidgets import (
5
- QWidget, QGridLayout, QLabel, QPushButton, QMessageBox, QComboBox, QCheckBox,
5
+ QWidget, QGridLayout, QLabel, QPushButton, QMessageBox, QComboBox, QCheckBox, QFileDialog,
6
6
  )
7
7
 
8
8
  from frontengine.show.pet.desktop_pet import (
9
- DesktopPetWidget, BEHAVIOUR_FLOOR, BEHAVIOUR_WANDER, BEHAVIOUR_CHASE,
9
+ DesktopPetWidget, BEHAVIOUR_FLOOR, BEHAVIOUR_WANDER, BEHAVIOUR_CHASE, scan_pet_pack,
10
10
  )
11
11
  from frontengine.ui.dialog.choose_file_dialog import choose_pet
12
12
  from frontengine.ui.page.utils import (
@@ -38,6 +38,10 @@ class PetSettingUI(QWidget):
38
38
  language_wrapper.language_word_dict.get("pet_choose_file", "Choose pet sprite")
39
39
  )
40
40
  self.choose_file_button.clicked.connect(self.choose_and_play)
41
+ self.choose_pack_button = QPushButton(
42
+ language_wrapper.language_word_dict.get("pet_choose_pack", "Choose pet pack...")
43
+ )
44
+ self.choose_pack_button.clicked.connect(self.choose_pack)
41
45
  self.ready_label = QLabel(language_wrapper.language_word_dict.get("Not Ready"))
42
46
 
43
47
  # Size
@@ -81,6 +85,7 @@ class PetSettingUI(QWidget):
81
85
  # Layout
82
86
  self.grid_layout.addWidget(self.choose_file_button, 0, 0)
83
87
  self.grid_layout.addWidget(self.ready_label, 0, 1)
88
+ self.grid_layout.addWidget(self.choose_pack_button, 0, 2)
84
89
  self.grid_layout.addWidget(self.size_label, 1, 0)
85
90
  self.grid_layout.addWidget(self.size_combobox, 1, 1)
86
91
  self.grid_layout.addWidget(self.speed_label, 2, 0)
@@ -132,6 +137,28 @@ class PetSettingUI(QWidget):
132
137
  add_recent_file("pet", self.pet_image_path)
133
138
  reload_recent_combobox(self.recent_files_combobox, "pet")
134
139
 
140
+ def choose_pack(self) -> None:
141
+ """選擇動作包資料夾(依 walk/idle/climb/fall/drag 檔名對應狀態)。"""
142
+ front_engine_logger.info("[PetSettingUI] choose_pack")
143
+ folder = QFileDialog.getExistingDirectory(
144
+ self, language_wrapper.language_word_dict.get("pet_choose_pack", "Choose pet pack...")
145
+ )
146
+ if not folder:
147
+ return
148
+ if not scan_pet_pack(folder):
149
+ QMessageBox.warning(
150
+ self,
151
+ language_wrapper.language_word_dict.get("pet_choose_pack", "Choose pet pack..."),
152
+ language_wrapper.language_word_dict.get(
153
+ "pet_pack_empty", "No sprites (walk/idle/climb/fall/drag) found in that folder."),
154
+ )
155
+ return
156
+ self.pet_image_path = folder
157
+ self.ready_to_play = True
158
+ self.ready_label.setText(language_wrapper.language_word_dict.get("Ready"))
159
+ add_recent_file("pet", folder)
160
+ reload_recent_combobox(self.recent_files_combobox, "pet")
161
+
135
162
  def _apply_recent_file(self, _index: int = 0) -> None:
136
163
  path = self.recent_files_combobox.currentData()
137
164
  self.recent_files_combobox.setCurrentIndex(0)
@@ -225,6 +225,8 @@ english_word_dict = {
225
225
  "pet_behaviour_chase": "Chase cursor",
226
226
  "pet_climb_label": "Climb walls",
227
227
  "pet_clone": "Clone",
228
+ "pet_choose_pack": "Choose pet pack...",
229
+ "pet_pack_empty": "No sprites (walk/idle/climb/fall/drag) found in that folder.",
228
230
  "pet_start": "Spawn pet",
229
231
  "pet_message_box_text": "Pet image not found",
230
232
  }
@@ -226,6 +226,8 @@ traditional_chinese_word_dict = {
226
226
  "pet_behaviour_chase": "追逐游標",
227
227
  "pet_climb_label": "攀爬牆壁",
228
228
  "pet_clone": "複製",
229
+ "pet_choose_pack": "選擇動作包資料夾...",
230
+ "pet_pack_empty": "該資料夾找不到精靈檔(walk/idle/climb/fall/drag)。",
229
231
  "pet_start": "召喚寵物",
230
232
  "pet_message_box_text": "找不到寵物圖片",
231
233
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.4
3
+ Version: 1.0.5
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.4"
9
+ version = "1.0.5"
10
10
  authors = [
11
11
  { name = "JE-Chen", email = "jechenmailman@gmail.com" },
12
12
  ]
File without changes
File without changes
File without changes