frontengine 1.0.3__tar.gz → 1.0.4__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.3 → frontengine-1.0.4}/PKG-INFO +1 -1
  2. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/pet/desktop_pet.py +91 -13
  3. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/pet/pet_setting_ui.py +21 -10
  4. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/english.py +2 -0
  5. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/traditional_chinese.py +2 -0
  6. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine.egg-info/PKG-INFO +1 -1
  7. {frontengine-1.0.3 → frontengine-1.0.4}/pyproject.toml +1 -1
  8. {frontengine-1.0.3 → frontengine-1.0.4}/LICENSE +0 -0
  9. {frontengine-1.0.3 → frontengine-1.0.4}/README.md +0 -0
  10. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/__init__.py +0 -0
  11. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/__main__.py +0 -0
  12. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/__init__.py +0 -0
  13. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/base_widget.py +0 -0
  14. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/gif/__init__.py +0 -0
  15. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/gif/paint_gif.py +0 -0
  16. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/image/__init__.py +0 -0
  17. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/image/paint_image.py +0 -0
  18. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/load/__init__.py +0 -0
  19. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/load/load_someone_make_ui.py +0 -0
  20. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/overlay_factory.py +0 -0
  21. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/particle/__init__.py +0 -0
  22. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/particle/particle_ui.py +0 -0
  23. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/pet/__init__.py +0 -0
  24. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/scene/__init__.py +0 -0
  25. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  26. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/scene/extend_graphic_view.py +0 -0
  27. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/scene/scene.py +0 -0
  28. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/sound_player/__init__.py +0 -0
  29. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/sound_player/sound_effect.py +0 -0
  30. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/sound_player/sound_player.py +0 -0
  31. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/text/__init__.py +0 -0
  32. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/text/draw_text.py +0 -0
  33. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/video/__init__.py +0 -0
  34. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/video/video_player.py +0 -0
  35. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/web/__init__.py +0 -0
  36. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/web/webview.py +0 -0
  37. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/show/window_helpers.py +0 -0
  38. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/system_tray/__init__.py +0 -0
  39. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/system_tray/extend_system_tray.py +0 -0
  40. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/__init__.py +0 -0
  41. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/color/__init__.py +0 -0
  42. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/color/global_color.py +0 -0
  43. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/dialog/__init__.py +0 -0
  44. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  45. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  46. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  47. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/main_ui.py +0 -0
  48. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/menu/__init__.py +0 -0
  49. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/menu/help_menu.py +0 -0
  50. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/menu/how_to_menu.py +0 -0
  51. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/menu/language_menu.py +0 -0
  52. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/menu/preset_menu.py +0 -0
  53. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/menu/settings_menu.py +0 -0
  54. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/__init__.py +0 -0
  55. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/control_center/__init__.py +0 -0
  56. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  57. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/gif/__init__.py +0 -0
  58. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  59. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/image/__init__.py +0 -0
  60. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  61. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/particle/__init__.py +0 -0
  62. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  63. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/pet/__init__.py +0 -0
  64. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  65. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  66. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  67. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  68. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  69. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  70. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  71. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  72. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  73. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  74. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  75. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  76. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/sound_player/__init__.py +0 -0
  77. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  78. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/text/__init__.py +0 -0
  79. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  80. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/utils.py +0 -0
  81. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/video/__init__.py +0 -0
  82. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  83. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/web/__init__.py +0 -0
  84. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  85. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/user_setting/__init__.py +0 -0
  86. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/user_setting/preset_repository.py +0 -0
  87. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/user_setting/scene_setting.py +0 -0
  88. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/user_setting/user_setting_file.py +0 -0
  89. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/__init__.py +0 -0
  90. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/browser/__init__.py +0 -0
  91. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/browser/browser.py +0 -0
  92. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/critical_exit/__init__.py +0 -0
  93. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  94. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  95. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  96. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/exception/__init__.py +0 -0
  97. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/exception/exception_tags.py +0 -0
  98. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/exception/exceptions.py +0 -0
  99. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/file/__init__.py +0 -0
  100. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/file/open/__init__.py +0 -0
  101. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/file/open/open_file.py +0 -0
  102. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/file/save/__init__.py +0 -0
  103. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/file/save/save_file.py +0 -0
  104. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/hotkey/__init__.py +0 -0
  105. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  106. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/json/__init__.py +0 -0
  107. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/json/json_file.py +0 -0
  108. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/json/json_repository.py +0 -0
  109. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/json_format/__init__.py +0 -0
  110. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/json_format/json_process.py +0 -0
  111. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/logging/__init__.py +0 -0
  112. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/logging/loggin_instance.py +0 -0
  113. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/__init__.py +0 -0
  114. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/france.py +0 -0
  115. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/germany.py +0 -0
  116. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/italy.py +0 -0
  117. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  118. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/russian.py +0 -0
  119. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  120. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/preset_schedule/__init__.py +0 -0
  121. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  122. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/redirect_manager/__init__.py +0 -0
  123. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  124. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/smart_pause/__init__.py +0 -0
  125. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  126. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/theme_schedule/__init__.py +0 -0
  127. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  128. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/utils/web_url.py +0 -0
  129. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/worker/__init__.py +0 -0
  130. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine/worker/qthread_worker.py +0 -0
  131. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine.egg-info/SOURCES.txt +0 -0
  132. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine.egg-info/dependency_links.txt +0 -0
  133. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine.egg-info/entry_points.txt +0 -0
  134. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine.egg-info/requires.txt +0 -0
  135. {frontengine-1.0.3 → frontengine-1.0.4}/frontengine.egg-info/top_level.txt +0 -0
  136. {frontengine-1.0.3 → frontengine-1.0.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.3
3
+ Version: 1.0.4
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
@@ -2,7 +2,7 @@ import random as _random_module
2
2
  from pathlib import Path
3
3
  from typing import Optional, Tuple
4
4
 
5
- from PySide6.QtCore import Qt, QRect, QTimer
5
+ from PySide6.QtCore import Qt, QRect, QTimer, Signal
6
6
  from PySide6.QtGui import QAction, QCursor, QMovie, QPainter, QPixmap, QTransform
7
7
  from PySide6.QtWidgets import QMenu, QMessageBox
8
8
 
@@ -16,6 +16,12 @@ BEHAVIOUR_FLOOR = "floor" # walk on the floor with gravity, throw & bounce
16
16
  BEHAVIOUR_WANDER = "wander" # free 2D bounce, no gravity
17
17
  BEHAVIOUR_CHASE = "chase" # chase the mouse cursor, sleep when caught
18
18
 
19
+ # 攀爬時所在的表面 / Surface the pet is crawling on while climbing
20
+ SURFACE_FLOOR = "floor"
21
+ SURFACE_LEFT = "left"
22
+ SURFACE_CEILING = "ceiling"
23
+ SURFACE_RIGHT = "right"
24
+
19
25
 
20
26
  class PetMotion:
21
27
  """
@@ -34,6 +40,8 @@ class PetMotion:
34
40
  BOUNCE_THRESHOLD = 8.0
35
41
  CHASE_STOP_DISTANCE = 10.0
36
42
  CHASE_SPEED_FACTOR = 2.0
43
+ CLIMB_CHANCE = 0.5 # chance to grab a wall (vs. turn around) at a floor corner
44
+ DROP_CHANCE = 0.008 # chance per step to let go while on a wall/ceiling
37
45
 
38
46
  def __init__(
39
47
  self,
@@ -45,6 +53,7 @@ class PetMotion:
45
53
  speed: int = 3,
46
54
  behaviour: str = BEHAVIOUR_FLOOR,
47
55
  rng=None,
56
+ climb: bool = True,
48
57
  ) -> None:
49
58
  self.x = float(x)
50
59
  self.y = float(y)
@@ -61,6 +70,8 @@ class PetMotion:
61
70
  self._state_ticks = 0
62
71
  self.target: Optional[Tuple[float, float]] = None
63
72
  self.asleep = False
73
+ self.climb = bool(climb)
74
+ self.surface = SURFACE_FLOOR
64
75
 
65
76
  @property
66
77
  def facing_left(self) -> bool:
@@ -78,6 +89,7 @@ class PetMotion:
78
89
  self.vy = float(vy)
79
90
  self._airborne = True
80
91
  self.asleep = False
92
+ self.surface = SURFACE_FLOOR
81
93
 
82
94
  def step(self) -> Tuple[int, int]:
83
95
  left, top, right, bottom = self.bounds
@@ -88,9 +100,9 @@ class PetMotion:
88
100
  return self._step_wander(left, top, right, bottom)
89
101
  return self._step_floor(left, top, right, bottom, floor_y)
90
102
 
91
- # --- floor / gravity ---
103
+ # --- floor / gravity / climbing ---
92
104
  def _step_floor(self, left, top, right, bottom, floor_y) -> Tuple[int, int]:
93
- airborne = self._airborne or self.y < floor_y - 0.5
105
+ airborne = self._airborne or (self.surface == SURFACE_FLOOR and self.y < floor_y - 0.5)
94
106
  if airborne:
95
107
  self.vy += self.GRAVITY
96
108
  self.x += self.vx
@@ -111,23 +123,81 @@ class PetMotion:
111
123
  else:
112
124
  self.vy = 0.0
113
125
  self._airborne = False
126
+ self.surface = SURFACE_FLOOR
114
127
  self.vx = float(self.speed) if self.vx >= 0 else float(-self.speed)
115
128
  self._new_state(force_walk=True)
116
129
  return int(self.x), int(self.y)
117
130
 
118
- # grounded: random walk / idle
131
+ if self.surface != SURFACE_FLOOR:
132
+ return self._step_climb(left, top, right, bottom, floor_y)
133
+
134
+ # grounded on the floor: random walk / idle, with a chance to climb a wall
119
135
  self.y = float(floor_y)
120
136
  if self._state_ticks <= 0:
121
137
  self._new_state()
122
138
  self._state_ticks -= 1
123
- if self.state == "walk":
139
+ if self.state != "walk":
140
+ return int(self.x), int(self.y)
141
+ self.x += self.vx
142
+ if self.x <= left:
143
+ self.x = float(left)
144
+ if self.climb and self._rng.random() < self.CLIMB_CHANCE:
145
+ self.surface = SURFACE_LEFT
146
+ self.vy = float(-self.speed)
147
+ else:
148
+ self.vx = abs(self.vx)
149
+ elif self.x + self.width >= right:
150
+ self.x = float(right - self.width)
151
+ if self.climb and self._rng.random() < self.CLIMB_CHANCE:
152
+ self.surface = SURFACE_RIGHT
153
+ self.vy = float(-self.speed)
154
+ else:
155
+ self.vx = -abs(self.vx)
156
+ return int(self.x), int(self.y)
157
+
158
+ def _step_climb(self, left, top, right, bottom, floor_y) -> Tuple[int, int]:
159
+ """沿螢幕邊緣爬行;隨機放手則掉落 / Crawl the screen perimeter; may let go and fall."""
160
+ if self._rng.random() < self.DROP_CHANCE:
161
+ self.surface = SURFACE_FLOOR
162
+ self._airborne = True
163
+ self.vy = 1.0
164
+ return int(self.x), int(self.y)
165
+
166
+ if self.surface == SURFACE_RIGHT:
167
+ self.x = float(right - self.width)
168
+ self.y += self.vy
169
+ if self.vy < 0 and self.y <= top: # reached the ceiling, crawl left
170
+ self.y = float(top)
171
+ self.surface = SURFACE_CEILING
172
+ self.vx = float(-self.speed)
173
+ elif self.vy > 0 and self.y >= floor_y: # climbed back down
174
+ self.y = float(floor_y)
175
+ self.surface = SURFACE_FLOOR
176
+ self.vx = float(-self.speed)
177
+ self._new_state(force_walk=True)
178
+ elif self.surface == SURFACE_LEFT:
179
+ self.x = float(left)
180
+ self.y += self.vy
181
+ if self.vy < 0 and self.y <= top:
182
+ self.y = float(top)
183
+ self.surface = SURFACE_CEILING
184
+ self.vx = float(self.speed)
185
+ elif self.vy > 0 and self.y >= floor_y:
186
+ self.y = float(floor_y)
187
+ self.surface = SURFACE_FLOOR
188
+ self.vx = float(self.speed)
189
+ self._new_state(force_walk=True)
190
+ elif self.surface == SURFACE_CEILING:
191
+ self.y = float(top)
124
192
  self.x += self.vx
125
- if self.x <= left:
193
+ if self.vx < 0 and self.x <= left: # corner -> climb down the left wall
126
194
  self.x = float(left)
127
- self.vx = abs(self.vx)
128
- elif self.x + self.width >= right:
195
+ self.surface = SURFACE_LEFT
196
+ self.vy = float(self.speed)
197
+ elif self.vx > 0 and self.x + self.width >= right:
129
198
  self.x = float(right - self.width)
130
- self.vx = -abs(self.vx)
199
+ self.surface = SURFACE_RIGHT
200
+ self.vy = float(self.speed)
131
201
  return int(self.x), int(self.y)
132
202
 
133
203
  def _new_state(self, force_walk: bool = False) -> None:
@@ -187,10 +257,13 @@ class DesktopPetWidget(BaseWidget):
187
257
  A draggable, throwable, cursor-chasing animated sprite (GIF or image).
188
258
  """
189
259
 
260
+ clone_requested = Signal()
261
+
190
262
  def __init__(self, image_path: str, size: int = 128, speed: int = 3,
191
- behaviour: str = BEHAVIOUR_FLOOR):
263
+ behaviour: str = BEHAVIOUR_FLOOR, climb: bool = True):
192
264
  front_engine_logger.info(
193
- f"[DesktopPetWidget] Init | path={image_path}, size={size}, speed={speed}, behaviour={behaviour}"
265
+ f"[DesktopPetWidget] Init | path={image_path}, size={size}, speed={speed}, "
266
+ f"behaviour={behaviour}, climb={climb}"
194
267
  )
195
268
  super().__init__()
196
269
  self.opacity = 1.0
@@ -216,14 +289,19 @@ class DesktopPetWidget(BaseWidget):
216
289
  message_box.show()
217
290
 
218
291
  self.resize(self.pet_size, self.pet_size)
219
- self.motion = PetMotion(0, 0, self.pet_size, self.pet_size, (0, 0, 1920, 1080), speed, behaviour)
292
+ self.motion = PetMotion(
293
+ 0, 0, self.pet_size, self.pet_size, (0, 0, 1920, 1080), speed, behaviour, climb=climb
294
+ )
220
295
 
221
296
  self._timer = QTimer(self)
222
297
  self._timer.timeout.connect(self._on_tick)
223
298
 
299
+ self.menu = QMenu(self)
300
+ self.clone_action = QAction(language_wrapper.language_word_dict.get("pet_clone", "Clone"), self)
301
+ self.clone_action.triggered.connect(self.clone_requested.emit)
302
+ self.menu.addAction(self.clone_action)
224
303
  self.close_action = QAction(language_wrapper.language_word_dict.get("control_center_close_all", "Close"), self)
225
304
  self.close_action.triggered.connect(self.close)
226
- self.menu = QMenu(self)
227
305
  self.menu.addAction(self.close_action)
228
306
 
229
307
  def set_pet_window_flag(self) -> None:
@@ -2,7 +2,7 @@ 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,
5
+ QWidget, QGridLayout, QLabel, QPushButton, QMessageBox, QComboBox, QCheckBox,
6
6
  )
7
7
 
8
8
  from frontengine.show.pet.desktop_pet import (
@@ -62,6 +62,10 @@ class PetSettingUI(QWidget):
62
62
  self.behaviour_combobox.addItem(
63
63
  language_wrapper.language_word_dict.get("pet_behaviour_chase", "Chase cursor"), BEHAVIOUR_CHASE)
64
64
 
65
+ # Climb walls / ceiling (only meaningful in floor mode)
66
+ self.climb_checkbox = QCheckBox(language_wrapper.language_word_dict.get("pet_climb_label", "Climb walls"))
67
+ self.climb_checkbox.setChecked(True)
68
+
65
69
  # Start
66
70
  self.start_button = QPushButton(language_wrapper.language_word_dict.get("pet_start", "Spawn pet"))
67
71
  self.start_button.clicked.connect(self.start_play_pet)
@@ -83,20 +87,30 @@ class PetSettingUI(QWidget):
83
87
  self.grid_layout.addWidget(self.speed_combobox, 2, 1)
84
88
  self.grid_layout.addWidget(self.behaviour_label, 3, 0)
85
89
  self.grid_layout.addWidget(self.behaviour_combobox, 3, 1)
90
+ self.grid_layout.addWidget(self.climb_checkbox, 3, 2)
86
91
  self.grid_layout.addWidget(self.start_button, 4, 0)
87
92
  self.grid_layout.addWidget(self.recent_files_label, 5, 0)
88
93
  self.grid_layout.addWidget(self.recent_files_combobox, 5, 1)
89
94
 
90
- def _create_pet_widget(self) -> DesktopPetWidget:
95
+ def _spawn_pet(self) -> None:
96
+ """建立、顯示並開始移動一隻寵物(供 Start 與右鍵複製共用)。"""
97
+ if not self.pet_image_path:
98
+ return
91
99
  pet = DesktopPetWidget(
92
100
  image_path=self.pet_image_path,
93
101
  size=int(self.size_combobox.currentText()),
94
102
  speed=int(self.speed_combobox.currentText()),
95
103
  behaviour=self.behaviour_combobox.currentData(),
104
+ climb=self.climb_checkbox.isChecked(),
96
105
  )
106
+ pet.clone_requested.connect(self._spawn_pet)
97
107
  pet.set_pet_window_flag()
98
108
  self.pet_list.append(pet)
99
- return pet
109
+ screen = QGuiApplication.primaryScreen()
110
+ geometry = screen.availableGeometry() if screen is not None else None
111
+ pet.show()
112
+ if geometry is not None:
113
+ pet.start_moving((geometry.left(), geometry.top(), geometry.right(), geometry.bottom()))
100
114
 
101
115
  def start_play_pet(self) -> None:
102
116
  front_engine_logger.info("[PetSettingUI] start_play_pet")
@@ -105,13 +119,7 @@ class PetSettingUI(QWidget):
105
119
  message_box.setText(language_wrapper.language_word_dict.get("not_prepare"))
106
120
  message_box.exec()
107
121
  return
108
- pet = self._create_pet_widget()
109
- screen = QGuiApplication.primaryScreen()
110
- geometry = screen.availableGeometry() if screen is not None else None
111
- pet.show()
112
- if geometry is not None:
113
- bounds = (geometry.left(), geometry.top(), geometry.right(), geometry.bottom())
114
- pet.start_moving(bounds)
122
+ self._spawn_pet()
115
123
 
116
124
  def choose_and_play(self) -> None:
117
125
  front_engine_logger.info("[PetSettingUI] choose_and_play")
@@ -147,6 +155,7 @@ class PetSettingUI(QWidget):
147
155
  "size": self.size_combobox.currentText(),
148
156
  "speed": self.speed_combobox.currentText(),
149
157
  "behaviour": self.behaviour_combobox.currentData(),
158
+ "climb": self.climb_checkbox.isChecked(),
150
159
  }
151
160
 
152
161
  def set_state(self, state: dict) -> None:
@@ -166,3 +175,5 @@ class PetSettingUI(QWidget):
166
175
  index = self.behaviour_combobox.findData(behaviour)
167
176
  if index >= 0:
168
177
  self.behaviour_combobox.setCurrentIndex(index)
178
+ if "climb" in state:
179
+ self.climb_checkbox.setChecked(bool(state["climb"]))
@@ -223,6 +223,8 @@ english_word_dict = {
223
223
  "pet_behaviour_floor": "Walk on floor",
224
224
  "pet_behaviour_wander": "Free wander",
225
225
  "pet_behaviour_chase": "Chase cursor",
226
+ "pet_climb_label": "Climb walls",
227
+ "pet_clone": "Clone",
226
228
  "pet_start": "Spawn pet",
227
229
  "pet_message_box_text": "Pet image not found",
228
230
  }
@@ -224,6 +224,8 @@ traditional_chinese_word_dict = {
224
224
  "pet_behaviour_floor": "沿底部行走",
225
225
  "pet_behaviour_wander": "自由漫遊",
226
226
  "pet_behaviour_chase": "追逐游標",
227
+ "pet_climb_label": "攀爬牆壁",
228
+ "pet_clone": "複製",
227
229
  "pet_start": "召喚寵物",
228
230
  "pet_message_box_text": "找不到寵物圖片",
229
231
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.3
3
+ Version: 1.0.4
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.3"
9
+ version = "1.0.4"
10
10
  authors = [
11
11
  { name = "JE-Chen", email = "jechenmailman@gmail.com" },
12
12
  ]
File without changes
File without changes
File without changes