0%

不务正业系列:桌宠开发(偶尔更新)

桌宠开发

最近在学习 Python 一直想搞一个项目

爬虫之后我又盯上桌宠了(主要是想把爬虫搭载到桌宠上),经典不务正业

DesktopPets_v1.0

第一个桌宠是在网上抄的,主要是为了熟悉 PyQt5 的使用

这个桌宠的功能很弱,之后的版本都会在它的基础上进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import os
import sys
import random
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtWidgets, QtGui

'''配置信息'''
class Config():
ROOT_DIR = os.path.join(os.path.split(os.path.abspath(__file__))[0], 'resources') # 获取图片的路径
print(ROOT_DIR)
ACTION_DISTRIBUTION = [
['1', '2', '3'], # 行走
['4', '5', '6', '7', '8', '9', '10', '11'], # 坠落&拖动
['12', '13', '14'], # 爬
['15', '16', '17'], # 攻击1
['18', '19'], # 摔倒
['20', '21'], # 睡觉
['22'], # 飞
['23', '24', '25'], # 举手
['26', '27', '28', '29'], # 攻击2
['30', '31', '32', '33'], # 坐2
['34', '35', '36', '37'], # 攻击3
['42', '43', '44', '45', '46'] # 杂项
]
PET_ACTIONS_MAP = {'pet_1': ACTION_DISTRIBUTION}
for i in range(2, 5): PET_ACTIONS_MAP.update({'pet_%s' % i: ACTION_DISTRIBUTION})

'''桌面宠物'''
class DesktopPet(QWidget):
tool_name = '桌面宠物'
def __init__(self, parent=None, **kwargs):
super(DesktopPet, self).__init__(parent)
self.cfg = Config()
for key, value in kwargs.items():
if hasattr(self.cfg, key): setattr(self.cfg, key, value)
# 初始化
self.setWindowFlags(Qt.FramelessWindowHint|Qt.WindowStaysOnTopHint|Qt.SubWindow)
self.setAutoFillBackground(False)
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.repaint()
# 随机导入一个宠物
self.pet_images, iconpath = self.randomLoadPetImages()
# 设置退出选项
quit_action = QAction('退出', self, triggered=self.quit)
quit_action.setIcon(QIcon(iconpath))
self.tray_icon_menu = QMenu(self)
self.tray_icon_menu.addAction(quit_action)
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon(iconpath))
self.tray_icon.setContextMenu(self.tray_icon_menu)
self.tray_icon.show()
# 当前显示的图片
self.image = QLabel(self)
self.setImage(self.pet_images[0][0])
# 是否跟随鼠标
self.is_follow_mouse = False
# 宠物拖拽时避免鼠标直接跳到左上角
self.mouse_drag_pos = self.pos()
# 显示
self.resize(236, 260)
self.randomPosition()
self.show()
# 宠物动画动作执行所需的一些变量
self.is_running_action = False
self.action_images = []
self.action_pointer = 0
self.action_max_len = 0
# 每隔一段时间做个动作
self.timer = QTimer()
self.timer.timeout.connect(self.randomAct)
self.timer.start(500)
'''随机做一个动作'''
def randomAct(self):
if not self.is_running_action:
self.is_running_action = True
self.action_images = random.choice(self.pet_images)
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.runFrame()
'''完成动作的每一帧'''
def runFrame(self):
if self.action_pointer == self.action_max_len:
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
self.setImage(self.action_images[self.action_pointer])
self.action_pointer += 1
'''设置当前显示的图片'''
def setImage(self, image):
self.image.setPixmap(QPixmap.fromImage(image))
'''随机导入一个桌面宠物的所有图片'''
def randomLoadPetImages(self):
cfg = self.cfg
pet_name = random.choice(list(cfg.PET_ACTIONS_MAP.keys()))
actions = cfg.PET_ACTIONS_MAP[pet_name]
pet_images = []
for action in actions:
pet_images.append([self.loadImage(os.path.join(cfg.ROOT_DIR, pet_name, 'shime'+item+'.png')) for item in action])
iconpath = os.path.join(cfg.ROOT_DIR, pet_name, 'shime1.png')
return pet_images, iconpath
'''鼠标左键按下时, 宠物将和鼠标位置绑定'''
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.is_follow_mouse = True
self.mouse_drag_pos = event.globalPos() - self.pos()
event.accept()
self.setCursor(QCursor(Qt.OpenHandCursor))
'''鼠标移动, 则宠物也移动'''
def mouseMoveEvent(self, event):
if Qt.LeftButton and self.is_follow_mouse:
self.move(event.globalPos() - self.mouse_drag_pos)
event.accept()
'''鼠标释放时, 取消绑定'''
def mouseReleaseEvent(self, event):
self.is_follow_mouse = False
self.setCursor(QCursor(Qt.ArrowCursor))
'''导入图像'''
def loadImage(self, imagepath):
image = QImage()
image.load(imagepath)
return image
'''随机到一个屏幕上的某个位置'''
def randomPosition(self):
screen_geo = QDesktopWidget().screenGeometry()
pet_geo = self.geometry()
width = (screen_geo.width() - pet_geo.width()) * random.random()
height = (screen_geo.height() - pet_geo.height()) * random.random()
self.move(int(width), int(height))
'''退出程序'''
def quit(self):
self.close()
sys.exit()

if __name__=="__main__":
app = QApplication(sys.argv)
pets = DesktopPet()
pets.show()
sys.exit(app.exec_())

更新日志:

  • version:v1.0
  • date:2022.3.31
  • type:
    • Features:NULL
    • Changed:NULL
    • Removed:NULL
  • desc:
    • 第一代版本,功能很弱,急需解决的问题有:
      • 动作不连贯
      • 角色不会移动
      • 角色只能面向单一方向
      • 缺少互动性
      • 缺少“重力系统”
      • 缺少右键菜单选项
      • 缺少对话功能

DesktopPets_v1.1

在 DesktopPets_v1.0 的基础上进行了面积升级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import os
import sys
import random
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtWidgets, QtGui
import time

'''配置信息'''
class Config():
ROOT_DIR = os.path.join(os.path.split(os.path.abspath(__file__))[0], 'resources')
print(ROOT_DIR)
ACTION_DISTRIBUTION = [
['x'], # 吃撇_0
['1','1a','1b','1c','1d','1','1a','1b','1c','1d'], # 眨眼_1
['1','2','3','2','3','2','3','2','3','2','3','2','3','2','3','2','3''2','3','2','3''2','3','2','3','1','1a','1b'], # 行走_2
['4', '5', '6', '7', '8', '9', '10'], # 坠落&拖动1_3
['11','11a','11b','11c','11d','11e','11f','11g','11f','11g','11f','11g','11f','11g','11f','11g','11e','11d','11c','11b','11a','11'], # 打哈切_4
['12', '13', '14'], # 爬_5
['18', '19'], # 摔倒_6
['20', '21'], # 睡觉_7
['22', '22a'], # 飞_8
['23','23a','23','23a','23b','24','25','26','27','28','29','34','35','36','37','34','35','36','37'], # 举手_9
['15', '16','17','26','27','28','29','15','34','17','26','27','28','1','1a','1b'], # 攻击_10
['30', '30','30','30','30','30a','30b','30b','30b','30c','30c','30a','30b','30b','30b','30c','30c','31','32','33'], # 打喷嚏_11
['42', '43', '44', '45', '46'], # 杂项_12
]
PET_ACTIONS_MAP = {'pet_1': ACTION_DISTRIBUTION}
for i in range(1): PET_ACTIONS_MAP.update({'pet_%s' % i: ACTION_DISTRIBUTION})

'''桌面宠物'''
class DesktopPet(QWidget):
tool_name = '桌面宠物'
stat = [1,2,4,9,10,11]
def __init__(self, parent=None, **kwargs):
super(DesktopPet, self).__init__(parent)
self.cfg = Config()
for key, value in kwargs.items():
if hasattr(self.cfg, key): setattr(self.cfg, key, value)
# 初始化
self.setWindowFlags(Qt.FramelessWindowHint|Qt.WindowStaysOnTopHint|Qt.SubWindow)
self.setAutoFillBackground(False)
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.repaint()
# 随机导入一个宠物
self.pet_images, iconpath = self.randomLoadPetImages()
# 设置退出选项
quit_action = QAction('退出', self, triggered=self.quit)
quit_action.setIcon(QIcon(iconpath))
self.tray_icon_menu = QMenu(self)
self.tray_icon_menu.addAction(quit_action)
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon(iconpath))
self.tray_icon.setContextMenu(self.tray_icon_menu)
self.tray_icon.show()
# 当前显示的图片
self.image = QLabel(self)
self.setImage(self.pet_images[0][0])
# 是否跟随鼠标
self.is_follow_mouse = False
# 宠物拖拽时避免鼠标直接跳到左上角
self.mouse_drag_pos = self.pos()
# 显示
self.resize(236, 260)
self.randomPosition()
self.InitTimer()
self.show()
# 宠物动画动作执行所需的一些变量
self.is_running_action = False
self.action_images = []
self.action_pointer = 0
self.action_max_len = 0

'''初始化计时器'''
def InitTimer(self) -> None:
self.timer = QTimer()
self.timer.timeout.connect(self.randomAct)
self.timer.start(100)

'''随机做一个动作'''
def randomAct(self):
if not self.is_running_action:
self.is_running_action = True
self.key = random.randint(0,len(DesktopPet.stat)-1)
self.action = DesktopPet.stat[self.key]
print("action is:"+str(self.action))
self.action_images = self.pet_images[self.action]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.heading = random.randint(0, 1)
if self.action == 2:
if self.heading == 0:
print("now is Right")
elif self.heading == 1:
print("now is Left")
if self.action == 2:
self.moving(self.heading)
else:
self.runFrame(self.heading)

'''完成动作的每一帧'''
def runFrame(self,heading):
if self.action_pointer == self.action_max_len:
time.sleep(0.8)
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
if heading==1:
self.setImage(self.action_images[self.action_pointer].mirrored(heading, False))
self.action_pointer += 1
elif heading==0:
self.setImage(self.action_images[self.action_pointer])
self.action_pointer += 1

'''动作-移动'''
def moving(self,heading):
if self.action_pointer == self.action_max_len:
time.sleep(1.5)
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
else:
screenRect = QApplication.desktop().screenGeometry()
x = self.pos().x()
x += int(32 * (0.5 - heading))
if (heading == 1):
if ((x <= 0 and heading == 1) or (
x >= screenRect.width() - self.size().width() and heading == 1)):
self.RandomAction(hit_wall=True)
return None
else:
self.move(x, self.pos().y())
self.setImage(self.action_images[self.action_pointer].mirrored(0, False))
self.action_pointer += 1
elif (heading == 0):
if ((x <= 0 and heading == 1) or (
x >= screenRect.width() - self.size().width() and heading == 1)):
self.RandomAction(hit_wall=True)
return None
else:
self.move(x, self.pos().y())
self.setImage(self.action_images[self.action_pointer].mirrored(1, False))
self.action_pointer += 1

'''设置当前显示的图片'''
def setImage(self, image):
self.image.setPixmap(QPixmap.fromImage(image))
'''随机导入一个桌面宠物的所有图片'''
def randomLoadPetImages(self):
cfg = self.cfg
pet_name = random.choice(list(cfg.PET_ACTIONS_MAP.keys()))
actions = cfg.PET_ACTIONS_MAP[pet_name]
pet_images = []
self.flag = 0
for action in actions:
patch = [self.loadImage(os.path.join(cfg.ROOT_DIR, pet_name, 'shime'+item+'.png')) for item in action]
pet_images.append(patch)
iconpath = os.path.join(cfg.ROOT_DIR, pet_name, 'shimemove1.png')
return pet_images, iconpath
'''鼠标左键按下时, 宠物将和鼠标位置绑定'''
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.is_follow_mouse = True
self.mouse_drag_pos = event.globalPos() - self.pos()
event.accept()
self.setCursor(QCursor(Qt.OpenHandCursor))
'''鼠标移动, 则宠物也移动'''
def mouseMoveEvent(self, event):
if Qt.LeftButton and self.is_follow_mouse:
self.move(event.globalPos() - self.mouse_drag_pos)
event.accept()
'''鼠标释放时, 取消绑定'''
def mouseReleaseEvent(self, event):
self.is_follow_mouse = False
self.setCursor(QCursor(Qt.ArrowCursor))
'''导入图像'''
def loadImage(self, imagepath):
image = QImage()
image.load(imagepath)
return image
'''随机到一个屏幕上的某个位置'''
def randomPosition(self):
screen_geo = QDesktopWidget().screenGeometry()
pet_geo = self.geometry()
width = (screen_geo.width() - pet_geo.width()) * random.random()
height = (screen_geo.height() - pet_geo.height()) * random.random()
self.move(int(width), int(height))
'''退出程序'''
def quit(self):
self.close()
sys.exit()

if __name__=="__main__":
app = QApplication(sys.argv)
pets = DesktopPet()
pets.show()
sys.exit(app.exec_())

更新日志:

  • version:v1.1
  • date:2022.4.12
  • type:
    • Features:
      • 大大提升了动作的流畅性
      • 在执行“move”动作时,角色会真正的进行移动
      • 角色现在可以改变面向的方向
    • Changed:
      • 对程序的部分逻辑进行了修改
      • 修复了多次点击不出图片的BUG
    • Removed:
      • 削减了一些多余的动作
  • desc:
    • 第二代版本,功能大大增强
    • 但还有一些BUG(当角色move到桌面外时,程序会强制关闭,目前不知道怎么解决)

DesktopPets_v1.2

第三代版本,功能较为完善了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
import os
import sys
import random
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtWidgets, QtGui
import time

'''配置信息'''
class Config():
ROOT_DIR = os.path.join(os.path.split(os.path.abspath(__file__))[0], 'resources')
print(ROOT_DIR)
ACTION_DISTRIBUTION = [
['x'], # 吃撇_0
['1','1a','1b','1c','1d','1','1a','1b','1c','1d'], # 眨眼_1
['1','2','3','2','3','2','3','2','3','2','3','2','3','2','3','2','3''2','3','2','3''2','3','2','3','1','1a','1b'], # 行走_2
['4', '5', '6', '7', '8', '9', '10'], # 拖动_3
['11','11a','11b','11c','11d','11e','11f','11g','11f','11g','11f','11g','11f','11g','11f','11g','11e','11d','11c','11b','11a','11'], # 打哈切_4
['12', '13', '14'], # 爬_5
['5','19','4','18','4','19','4','18','4','19','4','18','4','19','4','18','4','5','1','x','x','x','1','1a','1b','1c'], # 触地_6
['20', '21'], # 睡觉_7
['22', '22a'], # 飞_8
['23','23a','23','23a','23b','24','25','26','27','28','29','34','35','36','37','34','35','36','37'], # 举手_9
['15', '16','17','26','27','28','29','15','34','17','26','27','28','1','1a','1b'], # 攻击_10
['30', '30','30','30','30','30a','30b','30b','30b','30c','30c','30a','30b','30b','30b','30c','30c','31','32','33'], # 打喷嚏_11
['9','19','4','18','x','1b','1c','1d'], # 撞墙_12
]
PET_ACTIONS_MAP = {'pet_1': ACTION_DISTRIBUTION}
for i in range(0): PET_ACTIONS_MAP.update({'pet_%s' % i+1: ACTION_DISTRIBUTION})

'''桌面宠物'''
class DesktopPet(QWidget):
tool_name = '桌面宠物'
stat = [1,2,4,9,10,11]
def __init__(self, parent=None, **kwargs):
super(DesktopPet, self).__init__(parent)
self.cfg = Config()
for key, value in kwargs.items():
if hasattr(self.cfg, key): setattr(self.cfg, key, value)
self.setWindowFlags(Qt.FramelessWindowHint|Qt.WindowStaysOnTopHint|Qt.SubWindow)
self.setAutoFillBackground(False)
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.repaint()
self.pet_images, iconpath = self.randomLoadPetImages()
quit_action = QAction('退出', self, triggered=self.quit)
quit_action.setIcon(QIcon(iconpath))
self.tray_icon_menu = QMenu(self)
self.tray_icon_menu.addAction(quit_action)
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon(iconpath))
self.tray_icon.setContextMenu(self.tray_icon_menu)
self.tray_icon.show()
self.image = QLabel(self)
self.setImage(self.pet_images[0][0])
self.is_follow_mouse = False
self.mouse_drag_pos = self.pos()
self.resize(236, 260)
self.randomPosition()
self.InitTimer(self.randomAct,100)
self.show()
self.is_running_action = False
self.action_images = []
self.action_pointer = 0
self.action_max_len = 0

'''初始化计时器'''
def InitTimer(self,Act,start) -> None:
print("timer starting !!!")
self.timer = QTimer()
self.timer.timeout.connect(Act)
self.timer.start(start)

'''随机做一个动作'''
def randomAct(self):
if not self.is_running_action:
self.is_running_action = True
self.key = random.randint(0,len(DesktopPet.stat)-1)
self.action = DesktopPet.stat[self.key]
print("action is:"+str(self.action))
self.action_images = self.pet_images[self.action]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.heading = random.randint(0, 1)
if self.action == 2:
if self.heading == 0:
print("now is Right")
elif self.heading == 1:
print("now is Left")
if self.action == 2:
self.moving(self.heading)
else:
self.runFrame(self.heading)

'''完成动作的每一帧'''
def runFrame(self,heading):
if self.action_pointer == self.action_max_len:
time.sleep(0.5)
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
self.setImage(self.action_images[self.action_pointer].mirrored(heading, False))
self.action_pointer += 1

'''动作-移动'''
def moving(self,heading):
if self.action_pointer == self.action_max_len:
time.sleep(0.8)
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
else:
screenRect = QApplication.desktop().screenGeometry()
x = self.pos().x()
x += int(32 * (0.5 - heading))
if (heading == 1):
if ((x <= 0 and heading == 1) or (
x >= screenRect.width() - self.size().width() and heading == 0)):
print('now is hit wall')
x = self.pos().x()
x += int(32)
self.action_images = self.pet_images[12]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.hitwall(1,x)
self.heading = 0
else:
self.move(x, self.pos().y())
self.setImage(self.action_images[self.action_pointer].mirrored(0, False))
self.action_pointer += 1
elif (heading == 0):
if ((x <= 0 and heading == 1) or (
x >= screenRect.width() - self.size().width() and heading == 0)):
print('now is hit wall')
x = self.pos().x()
x += int(32)
self.action_images = self.pet_images[12]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.hitwall(0,x)
time.sleep(0.001)
self.heading = 1
else:
self.move(x, self.pos().y())
self.setImage(self.action_images[self.action_pointer].mirrored(1, False))
self.action_pointer += 1

'''动作-撞墙'''
def hitwall(self,heading,x):
if self.action_pointer == self.action_max_len:
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
self.move(x, self.pos().y())
self.setImage(self.action_images[self.action_pointer].mirrored(heading, False))
self.action_pointer += 1

'''动作-坠落'''
def fall(self):
if self.action_pointer == self.action_max_len:
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
else:
y = self.pos().y()
x = self.pos().x()
y += int(16)
screenRect = QApplication.desktop().screenGeometry()
if (y >= screenRect.height() - self.size().height()):
print('now is kiss the ground')
self.action_images = self.pet_images[6]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.touchdown(x,y)
self.InitTimer(self.randomAct, 100)
else:
self.move(self.pos().x(), y)
self.setImage(self.pet_images[3][0].mirrored(self.heading, False))

'''动作-触地'''
def touchdown(self,x,y):
if self.action_pointer == self.action_max_len:
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
else:
x -= int(32)
y -= int(48)
self.move(x, y)
self.setImage(self.action_images[self.action_pointer].mirrored(self.heading, False))
self.action_pointer += 1

'''设置当前显示的图片'''
def setImage(self, image):
self.image.setPixmap(QPixmap.fromImage(image))

'''随机导入一个桌面宠物的所有图片'''
def randomLoadPetImages(self):
cfg = self.cfg
pet_name = random.choice(list(cfg.PET_ACTIONS_MAP.keys()))
actions = cfg.PET_ACTIONS_MAP[pet_name]
pet_images = []
self.flag = 0
for action in actions:
patch = [self.loadImage(os.path.join(cfg.ROOT_DIR, pet_name, 'shime'+item+'.png')) for item in action]
pet_images.append(patch)
iconpath = os.path.join(cfg.ROOT_DIR, pet_name, 'shimeX.png')
return pet_images, iconpath

'''鼠标左键按下时, 宠物将和鼠标位置绑定'''
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.is_follow_mouse = True
self.mouse_drag_pos = event.globalPos() - self.pos()
event.accept()
self.setCursor(QCursor(Qt.OpenHandCursor))

'''鼠标移动, 则宠物也移动'''
def mouseMoveEvent(self, event):
if Qt.LeftButton and self.is_follow_mouse:
self.move(event.globalPos() - self.mouse_drag_pos)
event.accept()

'''鼠标释放时, 取消绑定'''
def mouseReleaseEvent(self, event):
self.is_follow_mouse = False
self.setCursor(QCursor(Qt.ArrowCursor))
self.fallingBody()

"""宠物自由落体"""
def fallingBody(self):
x = self.pos().x()
y = self.pos().y()
print("x=%x : y=%x" %(x,y))
self.InitTimer(self.fall,25)

'''导入图像'''
def loadImage(self, imagepath):
image = QImage()
image.load(imagepath)
return image

'''随机到一个屏幕上的某个位置'''
def randomPosition(self):
screen_geo = QDesktopWidget().screenGeometry()
pet_geo = self.geometry()
width = (screen_geo.width() - pet_geo.width()) * random.random()
height = (screen_geo.height() - pet_geo.height()) * random.random()
self.move(int(width), int(height))

'''退出程序'''
def quit(self):
self.close()
sys.exit()

if __name__=="__main__":
app = QApplication(sys.argv)
pets = DesktopPet()
pets.show()
sys.exit(app.exec_())

更新日志:

  • version:v1.2
  • date:2022.4.13
  • type:
    • Features:
      • 在角色触碰屏幕左右边界时,会触发“hitwall”动作,并且改变角色的移动方向
      • 新添“重力系统”,在“左键绑定”结束以后,会触发“fall”动作
      • 在角色触碰屏幕下边界时,会触发“touchdown”动作
    • Changed:
      • 对部分图片的顺序进行了修改
    • Removed:NULL
  • desc:
    • 目前有一个棘手的BUG:在任意拖拽角色时,有小概率导致“计时器InitTimer”失效
    • 接下来打算加入右键菜单选项,并添加更多动作

DesktopPets_v1.3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
import os
import sys
import random
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtWidgets, QtGui
import time

'''配置信息'''
class Config():
ROOT_DIR = os.path.join(os.path.split(os.path.abspath(__file__))[0], 'resources')
print(ROOT_DIR)
ACTION_DISTRIBUTION = [
['X','X','X','X','5','19','4','18','4','19','4','18','4','19','X','X','X','X','5','6','7','8','9','10'], # 吃撇_0
['1','1a','1b','1c','1d','1','1a','1b','1c','1d'], # 眨眼_1
['1','2','3','2','3','2','3','2','3','2','3','2','3','2','3','2','3''2','3','2','3''2','3','2','3','1','1a','1b'], # 行走_2
['6','6','7','7','8','8','5','5','9','9','10','10','7','7','6','6'], # 拖动_3
['11','11a','11b','11c','11d','11e','11f','11g','11f','11g','11f','11g','11f','11g','11f','11g','11e','11d','11c','11b','11a','11'], # 打哈切_4
['12', '13', '14'], # 爬_5
['19','5','19','4','18','4','19','4','18','4','19','4','18','4','19','4','18','4','5','1','X','X','X','1','1a','1b','1c'], # 触地_6
['20', '21'], # 睡觉_7
['22','22a','22','22a','22','22a','22','22a','22','22a','22','22a','22','22a','22','22a','22','22a','22','22a','22','22a','22','22a','22','22a','22','22a'], # 跳跃_8
['23','23a','23','23a','23b','24','25','26','27','28','29','34','35','36','37','34','35','36','37'], # 举手_9
['15','16','17','26','27','28','29','15','34','17','26','27','28','1','1a','1b'], # 攻击_10
['30','30','30','30','30','30a','30b','30b','30b','30c','30c','30a','30b','30b','30b','30c','30c','31','32','33'], # 打喷嚏_11
['9','19','4','18','x','1b','1c','1d'], # 撞墙_12
]
PET_ACTIONS_MAP = {'pet_1': ACTION_DISTRIBUTION}
for i in range(0): PET_ACTIONS_MAP.update({'pet_%s' % i+1: ACTION_DISTRIBUTION})

'''桌面宠物'''
class DesktopPet(QWidget):
tool_name = '桌面宠物'
stat = [1,2,4,8,9,10,11]
def __init__(self, parent=None, **kwargs):
super(DesktopPet, self).__init__(parent)
self.cfg = Config()
for key, value in kwargs.items():
if hasattr(self.cfg, key): setattr(self.cfg, key, value)
self.setWindowFlags(Qt.FramelessWindowHint|Qt.WindowStaysOnTopHint|Qt.SubWindow)
self.setAutoFillBackground(False)
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.repaint()
self.pet_images, iconpath = self.randomLoadPetImages()
quit_action = QAction('退出', self, triggered=self.quit)
quit_action.setIcon(QIcon(iconpath))
self.tray_icon_menu = QMenu(self)
self.tray_icon_menu.addAction(quit_action)
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon(iconpath))
self.tray_icon.setContextMenu(self.tray_icon_menu)
self.tray_icon.show()
self.image = QLabel(self)
self.setImage(self.pet_images[0][0])
self.is_follow_mouse = False
self.mouse_drag_pos = self.pos()
self.resize(236, 260)
self.randomPosition()
self.show()
self.is_running_action = False
self.action_images = []
self.action_pointer = 0
self.action_max_len = 0
self.x = self.pos().x()
self.y = self.pos().y()
self.heading = 0
self.touchdown_key = 0
self.jumpping_key = 0
self.fallingBody()

'''初始化计时器'''
def InitTimer(self,Act,start) -> None:
self.timer = QTimer()
self.timer.timeout.connect(Act)
self.timer.start(start)

'''随机做一个动作'''
def randomAct(self):
if not self.is_running_action:
self.is_running_action = True
self.key = random.randint(0,len(DesktopPet.stat)-1)
self.action = DesktopPet.stat[self.key]
print("action is:"+str(self.action))
self.action_images = self.pet_images[self.action]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.heading = random.randint(0, 1)
if self.action == 2:
if self.heading == 0:
print("now is Right")
elif self.heading == 1:
print("now is Left")
elif self.action == 8:
self.jumpping_key = 1
self.InitTimer(self.randomAct,40)

if self.touchdown_key == 1:
self.action_images = self.pet_images[6]
self.action_max_len = len(self.action_images)
self.touchdown()
elif self.jumpping_key == 1:
self.action_images = self.pet_images[8]
self.action_max_len = len(self.action_images)
self.jumpping()
else:
if self.action == 2:
self.moving()
else:
self.runFrame()

'''完成动作的每一帧'''
def runFrame(self):
if self.action_pointer == self.action_max_len:
if self.action != 3:
time.sleep(0.5)
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
self.setImage(self.action_images[self.action_pointer].mirrored(self.heading, False))
self.action_pointer += 1

'''动作-移动'''
def moving(self):
if self.action_pointer == self.action_max_len:
time.sleep(0.8)
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
else:
screenRect = QApplication.desktop().screenGeometry()
self.x = self.pos().x()
self.x += int(32 * (0.5 - self.heading))
if (self.heading == 1):
if ((self.x <= 0 and self.heading == 1) or (
self.x >= screenRect.width() - self.size().width() and self.heading == 0)):
print('now is hit wall')
self.x = self.pos().x()
self.x += int(32)
self.action_images = self.pet_images[12]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.hitwall()
self.heading = 0
else:
self.move(self.x, self.pos().y())
self.setImage(self.action_images[self.action_pointer].mirrored(0, False))
self.action_pointer += 1
elif (self.heading == 0):
if ((self.x <= 0 and self.heading == 1) or (
self.x >= screenRect.width() - self.size().width() and self.heading == 0)):
print('now is hit wall')
self.x = self.pos().x()
self.x += int(32)
self.action_images = self.pet_images[12]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.hitwall()
self.heading = 1
else:
self.move(self.x, self.pos().y())
self.setImage(self.action_images[self.action_pointer].mirrored(1, False))
self.action_pointer += 1

'''动作-撞墙'''
def hitwall(self):
if self.action_pointer == self.action_max_len:
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
self.move(self.x, self.pos().y())
self.setImage(self.action_images[self.action_pointer].mirrored(self.heading, False))
self.action_pointer += 1

'''动作-坠落'''
def fall(self):
y = self.pos().y()
x = self.pos().x()
y += int(16)
screenRect = QApplication.desktop().screenGeometry()
if (y >= screenRect.height() - self.size().height()):
print('now is kiss the ground')
self.action_pointer = 0
self.touchdown_key = 1
self.InitTimer(self.randomAct,80)
else:
self.move(x,y)
self.setImage(self.pet_images[6][0].mirrored(self.heading, False))

'''动作-触地'''
def touchdown(self):
self.x = self.pos().x()
self.y = self.pos().y()
self.x -= 2
self.y -= 2
if self.action_pointer == self.action_max_len:
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
self.touchdown_key = 0
else:
self.move(self.x, self.y)
self.setImage(self.action_images[self.action_pointer].mirrored(self.heading, False))
self.action_pointer += 1

'''动作-吃瘪'''
def uncomfortable(self):
if not self.is_running_action:
self.is_running_action = True
self.action = 0
print("action is:" + str(self.action))
self.action_images = self.pet_images[self.action]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.heading = random.randint(0, 1)
else:
self.runFrame()

'''动作-拖动'''
def dragging(self):
if not self.is_running_action:
self.is_running_action = True
self.action = 3
print("action is:" + str(self.action))
self.action_images = self.pet_images[self.action]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.heading = random.randint(0, 1)
else:
self.runFrame()

'''动作-跳跃'''
def jumpping(self):
if self.action_pointer == 0:
self.z = 80
if self.action_pointer == self.action_max_len:
self.fallingBody()
self.is_running_action = False
self.action_pointer = 0
self.action_max_len = 0
self.jumpping_key = 0
self.z = 80
else:
screenRect = QApplication.desktop().screenGeometry()
self.x = self.pos().x()
self.y = self.pos().y()
self.x += int(48 * (0.5 - self.heading))
self.y -= self.z
self.z -= 5
if (self.heading == 1):
if ((self.x <= 0 and self.heading == 1) or (
self.x >= screenRect.width() - self.size().width() and self.heading == 0)):
print('now is hit wall')
self.x = self.pos().x()
self.x += int(32)
self.action_images = self.pet_images[12]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.hitwall()
self.fallingBody()
self.heading = 0
else:
self.move(self.x, self.y)
self.setImage(self.action_images[self.action_pointer].mirrored(0, False))
self.action_pointer += 1
elif (self.heading == 0):
if ((self.x <= 0 and self.heading == 1) or (
self.x >= screenRect.width() - self.size().width() and self.heading == 0)):
print('now is hit wall')
x = self.pos().x()
x += int(32)
self.action_images = self.pet_images[12]
self.action_max_len = len(self.action_images)
self.action_pointer = 0
self.hitwall()
self.fallingBody()
self.heading = 1
else:
self.move(self.x, self.y)
self.setImage(self.action_images[self.action_pointer].mirrored(0, False))
self.action_pointer += 1

'''设置当前显示的图片'''
def setImage(self, image):
self.image.setPixmap(QPixmap.fromImage(image))

'''随机导入一个桌面宠物的所有图片'''
def randomLoadPetImages(self):
cfg = self.cfg
pet_name = random.choice(list(cfg.PET_ACTIONS_MAP.keys()))
actions = cfg.PET_ACTIONS_MAP[pet_name]
pet_images = []
self.flag = 0
for action in actions:
patch = [self.loadImage(os.path.join(cfg.ROOT_DIR, pet_name, 'shime'+item+'.png')) for item in action]
pet_images.append(patch)
iconpath = os.path.join(cfg.ROOT_DIR, pet_name, 'shimeX.png')
return pet_images, iconpath

'''鼠标左右键功能'''
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.InitTimer(self.dragging,80)
self.is_follow_mouse = True
self.mouse_drag_pos = event.globalPos() - self.pos()
event.accept()
self.setCursor(QCursor(Qt.OpenHandCursor))
elif event.button() == Qt.RightButton:
self.InitTimer(self.uncomfortable,80)
self.menu = QMenu(self)
self.quit = self.menu.addAction("退出")
self.choice = self.menu.exec_(self.mapToGlobal(event.pos()))
if(self.choice == self.quit):
print("quit")
DesktopPet.quit()
self.randomAct()

'''鼠标移动, 则宠物也移动'''
def mouseMoveEvent(self, event):
if Qt.LeftButton and self.is_follow_mouse:
self.move(event.globalPos() - self.mouse_drag_pos)
event.accept()

'''鼠标释放时, 取消绑定'''
def mouseReleaseEvent(self, event):
self.is_follow_mouse = False
self.setCursor(QCursor(Qt.ArrowCursor))
self.fallingBody()

"""宠物自由落体"""
def fallingBody(self):
x = self.pos().x()
y = self.pos().y()
print("x=%x : y=%x" %(x,y))
self.InitTimer(self.fall,25)

'''导入图像'''
def loadImage(self, imagepath):
image = QImage()
image.load(imagepath)
return image

'''随机到一个屏幕上的某个位置'''
def randomPosition(self):
screen_geo = QDesktopWidget().screenGeometry()
pet_geo = self.geometry()
width = (screen_geo.width() - pet_geo.width()) * random.random()
height = (screen_geo.height() - pet_geo.height()) * random.random()
self.move(int(width), int(height))

'''退出程序'''
def quit(self):
self.close()
sys.exit()

if __name__=="__main__":
app = QApplication(sys.argv)
pets = DesktopPet()
pets.show()
sys.exit(app.exec_())

更新日志:

  • version:v1.3
  • date:2022.4.14
  • type:
    • Features:
      • 新添跳跃动作
      • 新添右键菜单
    • Changed:
      • 对程序运行逻辑进行的修改,提供了动作的流畅度
    • Removed:NULL
  • desc:
    • 修了一些BUG