frontengine 1.0.2__tar.gz → 1.0.3__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.
- {frontengine-1.0.2 → frontengine-1.0.3}/PKG-INFO +1 -1
- frontengine-1.0.3/frontengine/show/pet/desktop_pet.py +297 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/pet/pet_setting_ui.py +24 -10
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/english.py +4 -1
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/traditional_chinese.py +4 -1
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine.egg-info/PKG-INFO +1 -1
- {frontengine-1.0.2 → frontengine-1.0.3}/pyproject.toml +1 -1
- frontengine-1.0.2/frontengine/show/pet/desktop_pet.py +0 -178
- {frontengine-1.0.2 → frontengine-1.0.3}/LICENSE +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/README.md +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/__main__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/base_widget.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/gif/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/gif/paint_gif.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/image/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/image/paint_image.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/load/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/load/load_someone_make_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/overlay_factory.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/particle/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/particle/particle_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/pet/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/scene/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/scene/extend_graphic_scene.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/scene/extend_graphic_view.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/scene/scene.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/sound_player/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/sound_player/sound_effect.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/sound_player/sound_player.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/text/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/text/draw_text.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/video/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/video/video_player.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/web/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/web/webview.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/show/window_helpers.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/system_tray/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/system_tray/extend_system_tray.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/color/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/color/global_color.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/dialog/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/dialog/save_file_dialog.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/main_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/menu/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/menu/help_menu.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/menu/how_to_menu.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/menu/language_menu.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/menu/preset_menu.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/menu/settings_menu.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/control_center/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/gif/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/image/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/image/image_setting_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/particle/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/pet/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/sound_player/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/text/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/text/text_setting_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/utils.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/video/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/video/video_setting_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/web/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/web/web_setting_ui.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/user_setting/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/user_setting/preset_repository.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/user_setting/scene_setting.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/user_setting/user_setting_file.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/browser/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/browser/browser.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/critical_exit/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/critical_exit/critical_exit.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/critical_exit/win32_vk.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/exception/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/exception/exception_tags.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/exception/exceptions.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/file/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/file/open/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/file/open/open_file.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/file/save/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/file/save/save_file.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/hotkey/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/hotkey/hotkey_service.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/json/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/json/json_file.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/json/json_repository.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/json_format/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/json_format/json_process.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/logging/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/logging/loggin_instance.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/france.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/germany.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/italy.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/language_wrapper.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/russian.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/preset_schedule/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/redirect_manager/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/smart_pause/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/theme_schedule/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/web_url.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/worker/__init__.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine/worker/qthread_worker.py +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine.egg-info/SOURCES.txt +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine.egg-info/dependency_links.txt +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine.egg-info/entry_points.txt +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine.egg-info/requires.txt +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/frontengine.egg-info/top_level.txt +0 -0
- {frontengine-1.0.2 → frontengine-1.0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frontengine
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
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
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import random as _random_module
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from PySide6.QtCore import Qt, QRect, QTimer
|
|
6
|
+
from PySide6.QtGui import QAction, QCursor, QMovie, QPainter, QPixmap, QTransform
|
|
7
|
+
from PySide6.QtWidgets import QMenu, QMessageBox
|
|
8
|
+
|
|
9
|
+
from frontengine.show.base_widget import BaseWidget
|
|
10
|
+
from frontengine.show.window_helpers import apply_overlay_window_flags
|
|
11
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
12
|
+
from frontengine.utils.multi_language.language_wrapper import language_wrapper
|
|
13
|
+
|
|
14
|
+
# 行為模式 / Behaviour modes
|
|
15
|
+
BEHAVIOUR_FLOOR = "floor" # walk on the floor with gravity, throw & bounce
|
|
16
|
+
BEHAVIOUR_WANDER = "wander" # free 2D bounce, no gravity
|
|
17
|
+
BEHAVIOUR_CHASE = "chase" # chase the mouse cursor, sleep when caught
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PetMotion:
|
|
21
|
+
"""
|
|
22
|
+
純邏輯的寵物移動模型(不依賴 Qt,完全可測試)。包含:
|
|
23
|
+
- 重力落下、落地、彈跳(受動量丟出後)
|
|
24
|
+
- 走路/發呆的隨機行為狀態機(rng 可注入以利測試)
|
|
25
|
+
- 追逐游標模式(追到後進入睡眠,游標移動即醒)
|
|
26
|
+
A pure, Qt-free motion model: gravity fall/land/bounce (after a throw),
|
|
27
|
+
a random walk/idle behaviour state machine (injectable rng), and a
|
|
28
|
+
cursor-chase mode that sleeps once it catches the target.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
GRAVITY = 2.0
|
|
32
|
+
BOUNCE_DAMPING = 0.55
|
|
33
|
+
WALL_DAMPING = 0.6
|
|
34
|
+
BOUNCE_THRESHOLD = 8.0
|
|
35
|
+
CHASE_STOP_DISTANCE = 10.0
|
|
36
|
+
CHASE_SPEED_FACTOR = 2.0
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
x: int,
|
|
41
|
+
y: int,
|
|
42
|
+
width: int,
|
|
43
|
+
height: int,
|
|
44
|
+
bounds: Tuple[int, int, int, int],
|
|
45
|
+
speed: int = 3,
|
|
46
|
+
behaviour: str = BEHAVIOUR_FLOOR,
|
|
47
|
+
rng=None,
|
|
48
|
+
) -> None:
|
|
49
|
+
self.x = float(x)
|
|
50
|
+
self.y = float(y)
|
|
51
|
+
self.width = int(width)
|
|
52
|
+
self.height = int(height)
|
|
53
|
+
self.bounds = bounds # (left, top, right, bottom)
|
|
54
|
+
self.speed = max(1, int(speed))
|
|
55
|
+
self.behaviour = behaviour
|
|
56
|
+
self._rng = rng if rng is not None else _random_module.SystemRandom()
|
|
57
|
+
self.vx = float(self.speed)
|
|
58
|
+
self.vy = 0.0 if behaviour != BEHAVIOUR_WANDER else float(self.speed)
|
|
59
|
+
self._airborne = False
|
|
60
|
+
self.state = "walk" # walk | idle
|
|
61
|
+
self._state_ticks = 0
|
|
62
|
+
self.target: Optional[Tuple[float, float]] = None
|
|
63
|
+
self.asleep = False
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def facing_left(self) -> bool:
|
|
67
|
+
return self.vx < 0
|
|
68
|
+
|
|
69
|
+
def set_bounds(self, bounds: Tuple[int, int, int, int]) -> None:
|
|
70
|
+
self.bounds = bounds
|
|
71
|
+
|
|
72
|
+
def set_target(self, x: float, y: float) -> None:
|
|
73
|
+
self.target = (float(x), float(y))
|
|
74
|
+
|
|
75
|
+
def throw(self, vx: float, vy: float) -> None:
|
|
76
|
+
"""拖曳放開後以動量丟出(只在重力模式有意義)。"""
|
|
77
|
+
self.vx = float(vx)
|
|
78
|
+
self.vy = float(vy)
|
|
79
|
+
self._airborne = True
|
|
80
|
+
self.asleep = False
|
|
81
|
+
|
|
82
|
+
def step(self) -> Tuple[int, int]:
|
|
83
|
+
left, top, right, bottom = self.bounds
|
|
84
|
+
floor_y = bottom - self.height
|
|
85
|
+
if self.behaviour == BEHAVIOUR_CHASE:
|
|
86
|
+
return self._step_chase(left, top, right, bottom)
|
|
87
|
+
if self.behaviour == BEHAVIOUR_WANDER:
|
|
88
|
+
return self._step_wander(left, top, right, bottom)
|
|
89
|
+
return self._step_floor(left, top, right, bottom, floor_y)
|
|
90
|
+
|
|
91
|
+
# --- floor / gravity ---
|
|
92
|
+
def _step_floor(self, left, top, right, bottom, floor_y) -> Tuple[int, int]:
|
|
93
|
+
airborne = self._airborne or self.y < floor_y - 0.5
|
|
94
|
+
if airborne:
|
|
95
|
+
self.vy += self.GRAVITY
|
|
96
|
+
self.x += self.vx
|
|
97
|
+
self.y += self.vy
|
|
98
|
+
if self.x <= left:
|
|
99
|
+
self.x = float(left)
|
|
100
|
+
self.vx = abs(self.vx) * self.WALL_DAMPING
|
|
101
|
+
elif self.x + self.width >= right:
|
|
102
|
+
self.x = float(right - self.width)
|
|
103
|
+
self.vx = -abs(self.vx) * self.WALL_DAMPING
|
|
104
|
+
if self.y <= top:
|
|
105
|
+
self.y = float(top)
|
|
106
|
+
self.vy = abs(self.vy) * self.WALL_DAMPING
|
|
107
|
+
if self.y >= floor_y:
|
|
108
|
+
self.y = float(floor_y)
|
|
109
|
+
if abs(self.vy) > self.BOUNCE_THRESHOLD:
|
|
110
|
+
self.vy = -self.vy * self.BOUNCE_DAMPING
|
|
111
|
+
else:
|
|
112
|
+
self.vy = 0.0
|
|
113
|
+
self._airborne = False
|
|
114
|
+
self.vx = float(self.speed) if self.vx >= 0 else float(-self.speed)
|
|
115
|
+
self._new_state(force_walk=True)
|
|
116
|
+
return int(self.x), int(self.y)
|
|
117
|
+
|
|
118
|
+
# grounded: random walk / idle
|
|
119
|
+
self.y = float(floor_y)
|
|
120
|
+
if self._state_ticks <= 0:
|
|
121
|
+
self._new_state()
|
|
122
|
+
self._state_ticks -= 1
|
|
123
|
+
if self.state == "walk":
|
|
124
|
+
self.x += self.vx
|
|
125
|
+
if self.x <= left:
|
|
126
|
+
self.x = float(left)
|
|
127
|
+
self.vx = abs(self.vx)
|
|
128
|
+
elif self.x + self.width >= right:
|
|
129
|
+
self.x = float(right - self.width)
|
|
130
|
+
self.vx = -abs(self.vx)
|
|
131
|
+
return int(self.x), int(self.y)
|
|
132
|
+
|
|
133
|
+
def _new_state(self, force_walk: bool = False) -> None:
|
|
134
|
+
if force_walk or self._rng.random() < 0.65:
|
|
135
|
+
self.state = "walk"
|
|
136
|
+
if not force_walk:
|
|
137
|
+
self.vx = float(self.speed) if self._rng.random() < 0.5 else float(-self.speed)
|
|
138
|
+
self._state_ticks = self._rng.randint(30, 120)
|
|
139
|
+
else:
|
|
140
|
+
self.state = "idle"
|
|
141
|
+
self._state_ticks = self._rng.randint(20, 80)
|
|
142
|
+
|
|
143
|
+
# --- free wander ---
|
|
144
|
+
def _step_wander(self, left, top, right, bottom) -> Tuple[int, int]:
|
|
145
|
+
if abs(self.vy) < 1e-9:
|
|
146
|
+
self.vy = float(self.speed)
|
|
147
|
+
self.x += self.vx
|
|
148
|
+
self.y += self.vy
|
|
149
|
+
if self.x <= left:
|
|
150
|
+
self.x = float(left)
|
|
151
|
+
self.vx = abs(self.vx)
|
|
152
|
+
elif self.x + self.width >= right:
|
|
153
|
+
self.x = float(right - self.width)
|
|
154
|
+
self.vx = -abs(self.vx)
|
|
155
|
+
if self.y <= top:
|
|
156
|
+
self.y = float(top)
|
|
157
|
+
self.vy = abs(self.vy)
|
|
158
|
+
elif self.y + self.height >= bottom:
|
|
159
|
+
self.y = float(bottom - self.height)
|
|
160
|
+
self.vy = -abs(self.vy)
|
|
161
|
+
return int(self.x), int(self.y)
|
|
162
|
+
|
|
163
|
+
# --- chase cursor ---
|
|
164
|
+
def _step_chase(self, left, top, right, bottom) -> Tuple[int, int]:
|
|
165
|
+
if self.target is None:
|
|
166
|
+
return int(self.x), int(self.y)
|
|
167
|
+
target_x, target_y = self.target
|
|
168
|
+
dx = target_x - (self.x + self.width / 2)
|
|
169
|
+
dy = target_y - (self.y + self.height / 2)
|
|
170
|
+
distance = (dx * dx + dy * dy) ** 0.5
|
|
171
|
+
if distance <= self.CHASE_STOP_DISTANCE:
|
|
172
|
+
self.asleep = True
|
|
173
|
+
return int(self.x), int(self.y)
|
|
174
|
+
self.asleep = False
|
|
175
|
+
step = float(self.speed) * self.CHASE_SPEED_FACTOR
|
|
176
|
+
self.x += step * (dx / distance)
|
|
177
|
+
self.y += step * (dy / distance)
|
|
178
|
+
self.vx = abs(self.vx) if dx >= 0 else -abs(self.vx)
|
|
179
|
+
self.x = min(max(self.x, float(left)), float(right - self.width))
|
|
180
|
+
self.y = min(max(self.y, float(top)), float(bottom - self.height))
|
|
181
|
+
return int(self.x), int(self.y)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class DesktopPetWidget(BaseWidget):
|
|
185
|
+
"""
|
|
186
|
+
桌面寵物:可自主走動、可拖曳丟出、可追游標的動畫精靈。支援 GIF 與靜態圖。
|
|
187
|
+
A draggable, throwable, cursor-chasing animated sprite (GIF or image).
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
def __init__(self, image_path: str, size: int = 128, speed: int = 3,
|
|
191
|
+
behaviour: str = BEHAVIOUR_FLOOR):
|
|
192
|
+
front_engine_logger.info(
|
|
193
|
+
f"[DesktopPetWidget] Init | path={image_path}, size={size}, speed={speed}, behaviour={behaviour}"
|
|
194
|
+
)
|
|
195
|
+
super().__init__()
|
|
196
|
+
self.opacity = 1.0
|
|
197
|
+
self.pet_size: int = max(16, int(size))
|
|
198
|
+
self.image_path: Path = Path(image_path)
|
|
199
|
+
self.movie: Optional[QMovie] = None
|
|
200
|
+
self.pixmap: Optional[QPixmap] = None
|
|
201
|
+
self._dragging: bool = False
|
|
202
|
+
self._drag_offset = None
|
|
203
|
+
self._last_move_delta: Tuple[int, int] = (0, 0)
|
|
204
|
+
|
|
205
|
+
if self.image_path.exists() and self.image_path.is_file():
|
|
206
|
+
if self.image_path.suffix.lower() in (".gif", ".webp"):
|
|
207
|
+
self.movie = QMovie(str(self.image_path))
|
|
208
|
+
self.movie.frameChanged.connect(self.update)
|
|
209
|
+
self.movie.start()
|
|
210
|
+
else:
|
|
211
|
+
self.pixmap = QPixmap(str(self.image_path))
|
|
212
|
+
else:
|
|
213
|
+
front_engine_logger.error(f"[DesktopPetWidget] File not found: {self.image_path}")
|
|
214
|
+
message_box = QMessageBox(self)
|
|
215
|
+
message_box.setText(language_wrapper.language_word_dict.get("pet_message_box_text", "Pet image not found"))
|
|
216
|
+
message_box.show()
|
|
217
|
+
|
|
218
|
+
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)
|
|
220
|
+
|
|
221
|
+
self._timer = QTimer(self)
|
|
222
|
+
self._timer.timeout.connect(self._on_tick)
|
|
223
|
+
|
|
224
|
+
self.close_action = QAction(language_wrapper.language_word_dict.get("control_center_close_all", "Close"), self)
|
|
225
|
+
self.close_action.triggered.connect(self.close)
|
|
226
|
+
self.menu = QMenu(self)
|
|
227
|
+
self.menu.addAction(self.close_action)
|
|
228
|
+
|
|
229
|
+
def set_pet_window_flag(self) -> None:
|
|
230
|
+
"""寵物需接收滑鼠以便拖曳,故 allow_input=True 並置頂。"""
|
|
231
|
+
apply_overlay_window_flags(self, show_on_bottom=False, allow_input=True)
|
|
232
|
+
|
|
233
|
+
def _current_pixmap(self) -> Optional[QPixmap]:
|
|
234
|
+
if self.movie is not None:
|
|
235
|
+
return self.movie.currentPixmap()
|
|
236
|
+
return self.pixmap
|
|
237
|
+
|
|
238
|
+
def draw_content(self, painter: QPainter) -> None:
|
|
239
|
+
pixmap = self._current_pixmap()
|
|
240
|
+
if pixmap is None or pixmap.isNull():
|
|
241
|
+
return
|
|
242
|
+
if self.motion.facing_left:
|
|
243
|
+
pixmap = pixmap.transformed(QTransform().scale(-1, 1))
|
|
244
|
+
painter.drawPixmap(QRect(0, 0, self.width(), self.height()), pixmap)
|
|
245
|
+
|
|
246
|
+
def start_moving(self, bounds: Tuple[int, int, int, int], interval_ms: int = 33) -> None:
|
|
247
|
+
front_engine_logger.info(f"[DesktopPetWidget] start_moving | bounds={bounds}")
|
|
248
|
+
left, top, right, bottom = bounds
|
|
249
|
+
self.motion.set_bounds(bounds)
|
|
250
|
+
self.motion.x = float(left)
|
|
251
|
+
self.motion.y = float(bottom - self.pet_size)
|
|
252
|
+
self.move(int(self.motion.x), int(self.motion.y))
|
|
253
|
+
self._timer.start(max(10, int(interval_ms)))
|
|
254
|
+
|
|
255
|
+
def _on_tick(self) -> None:
|
|
256
|
+
if self._dragging:
|
|
257
|
+
return
|
|
258
|
+
if self.motion.behaviour == BEHAVIOUR_CHASE:
|
|
259
|
+
cursor = QCursor.pos()
|
|
260
|
+
self.motion.set_target(cursor.x(), cursor.y())
|
|
261
|
+
x, y = self.motion.step()
|
|
262
|
+
self.move(x, y)
|
|
263
|
+
|
|
264
|
+
# --- dragging + throwing ---
|
|
265
|
+
def mousePressEvent(self, event) -> None:
|
|
266
|
+
if event.button() == Qt.MouseButton.LeftButton:
|
|
267
|
+
self._dragging = True
|
|
268
|
+
self._last_move_delta = (0, 0)
|
|
269
|
+
self._drag_offset = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
|
|
270
|
+
super().mousePressEvent(event)
|
|
271
|
+
|
|
272
|
+
def mouseMoveEvent(self, event) -> None:
|
|
273
|
+
if self._dragging and self._drag_offset is not None:
|
|
274
|
+
new_top_left = event.globalPosition().toPoint() - self._drag_offset
|
|
275
|
+
self._last_move_delta = (new_top_left.x() - self.x(), new_top_left.y() - self.y())
|
|
276
|
+
self.move(new_top_left)
|
|
277
|
+
super().mouseMoveEvent(event)
|
|
278
|
+
|
|
279
|
+
def mouseReleaseEvent(self, event) -> None:
|
|
280
|
+
if self._dragging:
|
|
281
|
+
self._dragging = False
|
|
282
|
+
self.motion.x = float(self.x())
|
|
283
|
+
self.motion.y = float(self.y())
|
|
284
|
+
# Fling with the momentum of the last drag movement (floor mode).
|
|
285
|
+
if self.motion.behaviour == BEHAVIOUR_FLOOR:
|
|
286
|
+
self.motion.throw(self._last_move_delta[0], self._last_move_delta[1])
|
|
287
|
+
super().mouseReleaseEvent(event)
|
|
288
|
+
|
|
289
|
+
def contextMenuEvent(self, event) -> None:
|
|
290
|
+
self.menu.popup(event.globalPos())
|
|
291
|
+
|
|
292
|
+
def closeEvent(self, event) -> None:
|
|
293
|
+
if self._timer.isActive():
|
|
294
|
+
self._timer.stop()
|
|
295
|
+
if self.movie is not None:
|
|
296
|
+
self.movie.stop()
|
|
297
|
+
super().closeEvent(event)
|
|
@@ -2,10 +2,12 @@ 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,
|
|
5
|
+
QWidget, QGridLayout, QLabel, QPushButton, QMessageBox, QComboBox,
|
|
6
6
|
)
|
|
7
7
|
|
|
8
|
-
from frontengine.show.pet.desktop_pet import
|
|
8
|
+
from frontengine.show.pet.desktop_pet import (
|
|
9
|
+
DesktopPetWidget, BEHAVIOUR_FLOOR, BEHAVIOUR_WANDER, BEHAVIOUR_CHASE,
|
|
10
|
+
)
|
|
9
11
|
from frontengine.ui.dialog.choose_file_dialog import choose_pet
|
|
10
12
|
from frontengine.ui.page.utils import (
|
|
11
13
|
build_recent_combobox,
|
|
@@ -50,9 +52,15 @@ class PetSettingUI(QWidget):
|
|
|
50
52
|
self.speed_combobox.addItems([str(n) for n in range(1, 11)])
|
|
51
53
|
self.speed_combobox.setCurrentText("3")
|
|
52
54
|
|
|
53
|
-
#
|
|
54
|
-
self.
|
|
55
|
-
self.
|
|
55
|
+
# Behaviour
|
|
56
|
+
self.behaviour_label = QLabel(language_wrapper.language_word_dict.get("pet_behaviour_label", "Behaviour"))
|
|
57
|
+
self.behaviour_combobox = QComboBox()
|
|
58
|
+
self.behaviour_combobox.addItem(
|
|
59
|
+
language_wrapper.language_word_dict.get("pet_behaviour_floor", "Walk on floor"), BEHAVIOUR_FLOOR)
|
|
60
|
+
self.behaviour_combobox.addItem(
|
|
61
|
+
language_wrapper.language_word_dict.get("pet_behaviour_wander", "Free wander"), BEHAVIOUR_WANDER)
|
|
62
|
+
self.behaviour_combobox.addItem(
|
|
63
|
+
language_wrapper.language_word_dict.get("pet_behaviour_chase", "Chase cursor"), BEHAVIOUR_CHASE)
|
|
56
64
|
|
|
57
65
|
# Start
|
|
58
66
|
self.start_button = QPushButton(language_wrapper.language_word_dict.get("pet_start", "Spawn pet"))
|
|
@@ -73,7 +81,8 @@ class PetSettingUI(QWidget):
|
|
|
73
81
|
self.grid_layout.addWidget(self.size_combobox, 1, 1)
|
|
74
82
|
self.grid_layout.addWidget(self.speed_label, 2, 0)
|
|
75
83
|
self.grid_layout.addWidget(self.speed_combobox, 2, 1)
|
|
76
|
-
self.grid_layout.addWidget(self.
|
|
84
|
+
self.grid_layout.addWidget(self.behaviour_label, 3, 0)
|
|
85
|
+
self.grid_layout.addWidget(self.behaviour_combobox, 3, 1)
|
|
77
86
|
self.grid_layout.addWidget(self.start_button, 4, 0)
|
|
78
87
|
self.grid_layout.addWidget(self.recent_files_label, 5, 0)
|
|
79
88
|
self.grid_layout.addWidget(self.recent_files_combobox, 5, 1)
|
|
@@ -83,7 +92,7 @@ class PetSettingUI(QWidget):
|
|
|
83
92
|
image_path=self.pet_image_path,
|
|
84
93
|
size=int(self.size_combobox.currentText()),
|
|
85
94
|
speed=int(self.speed_combobox.currentText()),
|
|
86
|
-
|
|
95
|
+
behaviour=self.behaviour_combobox.currentData(),
|
|
87
96
|
)
|
|
88
97
|
pet.set_pet_window_flag()
|
|
89
98
|
self.pet_list.append(pet)
|
|
@@ -137,7 +146,7 @@ class PetSettingUI(QWidget):
|
|
|
137
146
|
"pet_image_path": self.pet_image_path,
|
|
138
147
|
"size": self.size_combobox.currentText(),
|
|
139
148
|
"speed": self.speed_combobox.currentText(),
|
|
140
|
-
"
|
|
149
|
+
"behaviour": self.behaviour_combobox.currentData(),
|
|
141
150
|
}
|
|
142
151
|
|
|
143
152
|
def set_state(self, state: dict) -> None:
|
|
@@ -150,5 +159,10 @@ class PetSettingUI(QWidget):
|
|
|
150
159
|
index = combobox.findText(str(state[key]))
|
|
151
160
|
if index >= 0:
|
|
152
161
|
combobox.setCurrentIndex(index)
|
|
153
|
-
|
|
154
|
-
|
|
162
|
+
behaviour = state.get("behaviour")
|
|
163
|
+
if behaviour is None and "gravity" in state: # back-compat with old presets
|
|
164
|
+
behaviour = BEHAVIOUR_FLOOR if state.get("gravity") else BEHAVIOUR_WANDER
|
|
165
|
+
if behaviour is not None:
|
|
166
|
+
index = self.behaviour_combobox.findData(behaviour)
|
|
167
|
+
if index >= 0:
|
|
168
|
+
self.behaviour_combobox.setCurrentIndex(index)
|
|
@@ -219,7 +219,10 @@ english_word_dict = {
|
|
|
219
219
|
"pet_choose_file": "Choose pet sprite",
|
|
220
220
|
"pet_size_label": "Size",
|
|
221
221
|
"pet_speed_label": "Speed",
|
|
222
|
-
"
|
|
222
|
+
"pet_behaviour_label": "Behaviour",
|
|
223
|
+
"pet_behaviour_floor": "Walk on floor",
|
|
224
|
+
"pet_behaviour_wander": "Free wander",
|
|
225
|
+
"pet_behaviour_chase": "Chase cursor",
|
|
223
226
|
"pet_start": "Spawn pet",
|
|
224
227
|
"pet_message_box_text": "Pet image not found",
|
|
225
228
|
}
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/traditional_chinese.py
RENAMED
|
@@ -220,7 +220,10 @@ traditional_chinese_word_dict = {
|
|
|
220
220
|
"pet_choose_file": "選擇寵物圖片",
|
|
221
221
|
"pet_size_label": "大小",
|
|
222
222
|
"pet_speed_label": "速度",
|
|
223
|
-
"
|
|
223
|
+
"pet_behaviour_label": "行為",
|
|
224
|
+
"pet_behaviour_floor": "沿底部行走",
|
|
225
|
+
"pet_behaviour_wander": "自由漫遊",
|
|
226
|
+
"pet_behaviour_chase": "追逐游標",
|
|
224
227
|
"pet_start": "召喚寵物",
|
|
225
228
|
"pet_message_box_text": "找不到寵物圖片",
|
|
226
229
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frontengine
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
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,178 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Optional, Tuple
|
|
3
|
-
|
|
4
|
-
from PySide6.QtCore import Qt, QRect, QTimer
|
|
5
|
-
from PySide6.QtGui import QAction, QMovie, QPainter, QPixmap, QTransform
|
|
6
|
-
from PySide6.QtWidgets import QMenu, QMessageBox
|
|
7
|
-
|
|
8
|
-
from frontengine.show.base_widget import BaseWidget
|
|
9
|
-
from frontengine.show.window_helpers import apply_overlay_window_flags
|
|
10
|
-
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
11
|
-
from frontengine.utils.multi_language.language_wrapper import language_wrapper
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class PetMotion:
|
|
15
|
-
"""
|
|
16
|
-
純邏輯的移動模型(不依賴 Qt,便於測試)。gravity=True 時寵物沿著底部
|
|
17
|
-
行走並在左右邊緣轉向;否則在四邊之間自由彈跳。
|
|
18
|
-
Pure movement model (no Qt, easy to test). With gravity the pet walks
|
|
19
|
-
along the floor and turns at the left/right edges; otherwise it bounces
|
|
20
|
-
freely between all four edges.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
def __init__(
|
|
24
|
-
self,
|
|
25
|
-
x: int,
|
|
26
|
-
y: int,
|
|
27
|
-
width: int,
|
|
28
|
-
height: int,
|
|
29
|
-
bounds: Tuple[int, int, int, int],
|
|
30
|
-
speed: int = 3,
|
|
31
|
-
gravity: bool = True,
|
|
32
|
-
) -> None:
|
|
33
|
-
self.x = float(x)
|
|
34
|
-
self.y = float(y)
|
|
35
|
-
self.width = int(width)
|
|
36
|
-
self.height = int(height)
|
|
37
|
-
self.bounds = bounds # (left, top, right, bottom)
|
|
38
|
-
self.speed = max(1, int(speed))
|
|
39
|
-
self.gravity = bool(gravity)
|
|
40
|
-
self.vx = float(self.speed)
|
|
41
|
-
self.vy = 0.0 if gravity else float(self.speed)
|
|
42
|
-
|
|
43
|
-
@property
|
|
44
|
-
def facing_left(self) -> bool:
|
|
45
|
-
return self.vx < 0
|
|
46
|
-
|
|
47
|
-
def set_bounds(self, bounds: Tuple[int, int, int, int]) -> None:
|
|
48
|
-
self.bounds = bounds
|
|
49
|
-
|
|
50
|
-
def step(self) -> Tuple[int, int]:
|
|
51
|
-
left, top, right, bottom = self.bounds
|
|
52
|
-
self.x += self.vx
|
|
53
|
-
if self.gravity:
|
|
54
|
-
self.y = float(bottom - self.height)
|
|
55
|
-
else:
|
|
56
|
-
self.y += self.vy
|
|
57
|
-
if self.y <= top:
|
|
58
|
-
self.y = float(top)
|
|
59
|
-
self.vy = abs(self.vy)
|
|
60
|
-
elif self.y + self.height >= bottom:
|
|
61
|
-
self.y = float(bottom - self.height)
|
|
62
|
-
self.vy = -abs(self.vy)
|
|
63
|
-
if self.x <= left:
|
|
64
|
-
self.x = float(left)
|
|
65
|
-
self.vx = abs(self.vx)
|
|
66
|
-
elif self.x + self.width >= right:
|
|
67
|
-
self.x = float(right - self.width)
|
|
68
|
-
self.vx = -abs(self.vx)
|
|
69
|
-
return int(self.x), int(self.y)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class DesktopPetWidget(BaseWidget):
|
|
73
|
-
"""
|
|
74
|
-
桌面寵物:可自主走動、可拖曳的動畫精靈。支援 GIF(QMovie)與靜態圖。
|
|
75
|
-
A draggable, self-walking animated sprite. Supports GIF (QMovie) and
|
|
76
|
-
static images.
|
|
77
|
-
"""
|
|
78
|
-
|
|
79
|
-
def __init__(self, image_path: str, size: int = 128, speed: int = 3, gravity: bool = True):
|
|
80
|
-
front_engine_logger.info(
|
|
81
|
-
f"[DesktopPetWidget] Init | path={image_path}, size={size}, speed={speed}, gravity={gravity}"
|
|
82
|
-
)
|
|
83
|
-
super().__init__()
|
|
84
|
-
self.opacity = 1.0
|
|
85
|
-
self.pet_size: int = max(16, int(size))
|
|
86
|
-
self.image_path: Path = Path(image_path)
|
|
87
|
-
self.movie: Optional[QMovie] = None
|
|
88
|
-
self.pixmap: Optional[QPixmap] = None
|
|
89
|
-
self._dragging: bool = False
|
|
90
|
-
self._drag_offset = None
|
|
91
|
-
|
|
92
|
-
if self.image_path.exists() and self.image_path.is_file():
|
|
93
|
-
if self.image_path.suffix.lower() in (".gif", ".webp"):
|
|
94
|
-
self.movie = QMovie(str(self.image_path))
|
|
95
|
-
self.movie.frameChanged.connect(self.update)
|
|
96
|
-
self.movie.start()
|
|
97
|
-
else:
|
|
98
|
-
self.pixmap = QPixmap(str(self.image_path))
|
|
99
|
-
else:
|
|
100
|
-
front_engine_logger.error(f"[DesktopPetWidget] File not found: {self.image_path}")
|
|
101
|
-
message_box = QMessageBox(self)
|
|
102
|
-
message_box.setText(language_wrapper.language_word_dict.get("pet_message_box_text", "Pet image not found"))
|
|
103
|
-
message_box.show()
|
|
104
|
-
|
|
105
|
-
self.resize(self.pet_size, self.pet_size)
|
|
106
|
-
self.motion = PetMotion(0, 0, self.pet_size, self.pet_size, (0, 0, 1920, 1080), speed, gravity)
|
|
107
|
-
|
|
108
|
-
self._timer = QTimer(self)
|
|
109
|
-
self._timer.timeout.connect(self._on_tick)
|
|
110
|
-
|
|
111
|
-
# Right-click to close
|
|
112
|
-
self.close_action = QAction(language_wrapper.language_word_dict.get("control_center_close_all", "Close"), self)
|
|
113
|
-
self.close_action.triggered.connect(self.close)
|
|
114
|
-
self.menu = QMenu(self)
|
|
115
|
-
self.menu.addAction(self.close_action)
|
|
116
|
-
|
|
117
|
-
def set_pet_window_flag(self) -> None:
|
|
118
|
-
"""寵物需接收滑鼠以便拖曳,故 allow_input=True 並置頂。"""
|
|
119
|
-
apply_overlay_window_flags(self, show_on_bottom=False, allow_input=True)
|
|
120
|
-
|
|
121
|
-
def _current_pixmap(self) -> Optional[QPixmap]:
|
|
122
|
-
if self.movie is not None:
|
|
123
|
-
return self.movie.currentPixmap()
|
|
124
|
-
return self.pixmap
|
|
125
|
-
|
|
126
|
-
def draw_content(self, painter: QPainter) -> None:
|
|
127
|
-
pixmap = self._current_pixmap()
|
|
128
|
-
if pixmap is None or pixmap.isNull():
|
|
129
|
-
return
|
|
130
|
-
if self.motion.facing_left:
|
|
131
|
-
# Mirror horizontally so the sprite faces its walking direction.
|
|
132
|
-
pixmap = pixmap.transformed(QTransform().scale(-1, 1))
|
|
133
|
-
painter.drawPixmap(QRect(0, 0, self.width(), self.height()), pixmap)
|
|
134
|
-
|
|
135
|
-
def start_moving(self, bounds: Tuple[int, int, int, int], interval_ms: int = 33) -> None:
|
|
136
|
-
"""在指定範圍內開始走動 / Start walking within `bounds` (left, top, right, bottom)."""
|
|
137
|
-
front_engine_logger.info(f"[DesktopPetWidget] start_moving | bounds={bounds}")
|
|
138
|
-
left, top, right, bottom = bounds
|
|
139
|
-
self.motion.set_bounds(bounds)
|
|
140
|
-
self.motion.x = float(left)
|
|
141
|
-
self.motion.y = float(bottom - self.pet_size)
|
|
142
|
-
self.move(int(self.motion.x), int(self.motion.y))
|
|
143
|
-
self._timer.start(max(10, int(interval_ms)))
|
|
144
|
-
|
|
145
|
-
def _on_tick(self) -> None:
|
|
146
|
-
if self._dragging:
|
|
147
|
-
return
|
|
148
|
-
x, y = self.motion.step()
|
|
149
|
-
self.move(x, y)
|
|
150
|
-
|
|
151
|
-
# --- dragging ---
|
|
152
|
-
def mousePressEvent(self, event) -> None:
|
|
153
|
-
if event.button() == Qt.MouseButton.LeftButton:
|
|
154
|
-
self._dragging = True
|
|
155
|
-
self._drag_offset = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
|
|
156
|
-
super().mousePressEvent(event)
|
|
157
|
-
|
|
158
|
-
def mouseMoveEvent(self, event) -> None:
|
|
159
|
-
if self._dragging and self._drag_offset is not None:
|
|
160
|
-
self.move(event.globalPosition().toPoint() - self._drag_offset)
|
|
161
|
-
super().mouseMoveEvent(event)
|
|
162
|
-
|
|
163
|
-
def mouseReleaseEvent(self, event) -> None:
|
|
164
|
-
if self._dragging:
|
|
165
|
-
self._dragging = False
|
|
166
|
-
self.motion.x = float(self.x())
|
|
167
|
-
self.motion.y = float(self.y())
|
|
168
|
-
super().mouseReleaseEvent(event)
|
|
169
|
-
|
|
170
|
-
def contextMenuEvent(self, event) -> None:
|
|
171
|
-
self.menu.popup(event.globalPos())
|
|
172
|
-
|
|
173
|
-
def closeEvent(self, event) -> None:
|
|
174
|
-
if self._timer.isActive():
|
|
175
|
-
self._timer.stop()
|
|
176
|
-
if self.movie is not None:
|
|
177
|
-
self.movie.stop()
|
|
178
|
-
super().closeEvent(event)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/control_center/control_center_ui.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/image.py
RENAMED
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/registry.py
RENAMED
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/sound.py
RENAMED
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/text.py
RENAMED
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_page/video.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/scene_setting/scene_setting_ui.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/ui/page/sound_player/sound_player_setting_ui.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/critical_exit/check_key_is_press.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/language_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/multi_language/simplified_chinese.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/preset_schedule/preset_schedule_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/redirect_manager/redirect_manager_class.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/smart_pause/smart_pause_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{frontengine-1.0.2 → frontengine-1.0.3}/frontengine/utils/theme_schedule/theme_schedule_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|