1、创作背景
今天如往常一样来上班,一进公司就看见财务小姐姐闷闷不乐,就走过去跟她聊天,她说她很想养一只小狗,但是家里面不同意,怕把家里弄得乱七八糟。看着小姐姐悲伤的样子,我恏大力最舍不得小姐姐不开熏,所以我恏大力就要出来帮助美丽的财务小姐姐咯。
2、技术选择
Python (对于目前的打工人来说,学会python真的是可以提高大部分的工作效率)
3、程序设计
1.对于一个桌面小宠物来说,首先得有这只宠物的样式,但是如果用素材的话那可玩性就很低了,我这边的方法就是可以无素材独立创作宠物样式!!!
首先先来写一个创建宠物样式的程序。【create_folders.py】
代码直接付给大家,注释都在代码中,各位可随意修改。
import os
from PIL import Image, ImageDraw
def create_folder_structure():
# 创建主文件夹
os.makedirs("images", exist_ok=True)
def create_dog_image(filename, is_sleeping=False, is_walking=False, is_happy=False, is_eating=False, frame=1):
# 创建一个128x128的图片(加大尺寸以容纳更多细节)
img = Image.new('RGBA', (128, 128), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
# 颜色定义
white = (255, 255, 255) # 主体颜色
pink = (255, 218, 233) # 耳朵内部
black = (0, 0, 0) # 眼睛和鼻子
if is_sleeping:
# 睡觉状态:趴着的狗
# 身体
draw.ellipse([40, 70, 90, 95], fill=white) # 身体
# 尾巴(趴着)
draw.ellipse([85, 65, 95, 75], fill=white)
# 头
draw.ellipse([25, 60, 60, 90], fill=white) # 头部
# 耳朵(趴下)
draw.ellipse([30, 55, 45, 70], fill=white) # 左耳外部
draw.ellipse([35, 58, 42, 67], fill=pink) # 左耳内部
draw.ellipse([40, 55, 55, 70], fill=white) # 右耳外部
draw.ellipse([45, 58, 52, 67], fill=pink) # 右耳内部
# 闭着的眼睛
draw.line([35, 75, 40, 75], fill=black, width=2) # 左眼
draw.line([45, 75, 50, 75], fill=black, width=2) # 右眼
# 鼻子
draw.ellipse([40, 78, 45, 83], fill=black)
# 腿(趴着)
draw.ellipse([45, 90, 55, 98], fill=white) # 前腿
draw.ellipse([75, 90, 85, 98], fill=white) # 后腿
else:
# 正常状态:站着的狗
# 身体
draw.ellipse([45, 60, 85, 90], fill=white) # 身体
# 尾巴(摇动效果)
if frame == 1:
draw.arc([75, 45, 95, 65], 0, 180, fill=white, width=4)
else:
draw.arc([75, 45, 95, 65], -30, 150, fill=white, width=4)
# 腿
if is_walking:
# 走路状态:交替抬腿
if frame == 1:
draw.ellipse([50, 85, 60, 100], fill=white) # 左前腿
draw.ellipse([70, 88, 80, 103], fill=white) # 右前腿
else:
draw.ellipse([50, 88, 60, 103], fill=white) # 左前腿
draw.ellipse([70, 85, 80, 100], fill=white) # 右前腿
else:
# 站立状态
draw.ellipse([50, 85, 60, 100], fill=white) # 左前腿
draw.ellipse([70, 85, 80, 100], fill=white) # 右前腿
# 头
draw.ellipse([30, 45, 65, 75], fill=white) # 头部
# 耳朵
draw.ellipse([35, 35, 50, 50], fill=white) # 左耳外部
draw.ellipse([38, 38, 47, 47], fill=pink) # 左耳内部
draw.ellipse([45, 35, 60, 50], fill=white) # 右耳外部
draw.ellipse([48, 38, 57, 47], fill=pink) # 右耳内部
# 眼睛
draw.ellipse([38, 55, 43, 60], fill=black) # 左眼
draw.ellipse([52, 55, 57, 60], fill=black) # 右眼
# 鼻子和嘴
draw.ellipse([45, 62, 50, 67], fill=black) # 鼻子
draw.arc([40, 62, 55, 72], 0, 180, fill=black, width=1) # 微笑
if is_happy:
# 开心状态:尾巴摇动幅度更大,眼睛变成月牙形
# 基本形态与正常状态相同,但有以下变化:
# 身体
draw.ellipse([45, 60, 85, 90], fill=white)
# 摇动的尾巴(幅度更大)
if frame == 1:
draw.arc([75, 40, 95, 65], -30, 150, fill=white, width=4)
else:
draw.arc([75, 45, 95, 70], 30, 210, fill=white, width=4)
# 月牙形眼睛
draw.arc([38, 55, 43, 60], 0, 180, fill=black, width=2)
draw.arc([52, 55, 57, 60], 0, 180, fill=black, width=2)
# 微笑
draw.arc([40, 62, 55, 72], 0, 180, fill=black, width=2)
elif is_eating:
# 吃东西状态:低头,嘴巴张开
# 身体
draw.ellipse([45, 60, 85, 90], fill=white)
# 低头的姿势
draw.ellipse([30, 55, 65, 85], fill=white)
# 张开的嘴
if frame == 1:
draw.arc([40, 70, 55, 80], 0, 180, fill=black, width=2)
else:
draw.arc([40, 70, 55, 80], 30, 150, fill=black, width=2)
img.save(f"images/{filename}")
# 创建各种状态的小狗图片
# 正常状态
create_dog_image("normal_1.png", frame=1)
create_dog_image("normal_2.png", frame=2)
# 睡觉状态
create_dog_image("sleep_1.png", is_sleeping=True, frame=1)
create_dog_image("sleep_2.png", is_sleeping=True, frame=2)
# 走路状态
create_dog_image("walk_1.png", is_walking=True, frame=1)
create_dog_image("walk_2.png", is_walking=True, frame=2)
# 开心状态
create_dog_image("happy_1.png", is_happy=True, frame=1)
create_dog_image("happy_2.png", is_happy=True, frame=2)
# 吃东西状态
create_dog_image("eat_1.png", is_eating=True, frame=1)
create_dog_image("eat_2.png", is_eating=True, frame=2)
if __name__ == "__main__":
create_folder_structure()
print("文件夹和小狗图片创建完成!")
好的,这段程序运行后就会在当前目录下生成一个images文件夹来存放生成的素材。
我设置的小狗样式就是这个样子的,大家可以发挥自己的想法设计自己的宠物,可以在评论区分享各位的宠物样式。
4、主程序
2.然后现在开始实现主程序设计,主要是包括宠物自身的动作,然后活动的范围,以及与人的互动过程。
代码依旧附下:(注释都在代码中)
import sys
import random
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QMenu,
QSystemTrayIcon, QAction, QMessageBox)
from PyQt5.QtCore import Qt, QTimer, QPoint
from PyQt5.QtGui import QPixmap, QIcon
class DesktopPet(QMainWindow):
def __init__(self):
super().__init__()
# 初始化状态
self.is_following_mouse = False
self.is_sleeping = False
self.current_action = "normal"
# 加载动画帧并验证
self.animations = {}
try:
self.animations = {
"normal": [
self.load_and_verify_image("images/normal_1.png"),
self.load_and_verify_image("images/normal_2.png")
],
"sleeping": [
self.load_and_verify_image("images/sleep_1.png"),
self.load_and_verify_image("images/sleep_2.png")
],
"walking": [
self.load_and_verify_image("images/walk_1.png"),
self.load_and_verify_image("images/walk_2.png")
],
"happy": [
self.load_and_verify_image("images/happy_1.png"),
self.load_and_verify_image("images/happy_2.png")
],
"eating": [
self.load_and_verify_image("images/eat_1.png"),
self.load_and_verify_image("images/eat_2.png")
]
}
except Exception as e:
print(f"加载图片时出错: {e}")
sys.exit(1)
self.current_frame = 0
self.initUI()
self.setupTimers()
# 获取屏幕尺寸
self.screen = QApplication.primaryScreen().geometry()
# 设置活动范围(默认为整个屏幕)
self.movement_area = {
'left': self.screen.width() // 2, # 从屏幕中间开始
'top': 0, # 从顶部开始
'right': self.screen.width(), # 到屏幕右边
'bottom': self.screen.height() // 2 # 到屏幕中间
}
# 添加自动移动定时器
self.movement_timer = QTimer(self)
self.movement_timer.timeout.connect(self.auto_move)
self.movement_timer.start(3000) # 每3秒移动一次
# 移动目标点
self.target_pos = None
# 添加新的状态和计数器
self.is_dragging = False
self.is_happy = False
self.pet_mood = 100 # 心情值(最大100)
self.energy = 100 # 能量值(最大100)
# 添加状态计时器
self.status_timer = QTimer(self)
self.status_timer.timeout.connect(self.update_status)
self.status_timer.start(10000) # 每10秒更新一次状态
def initUI(self):
# 设置窗口属性
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.SubWindow)
self.setAttribute(Qt.WA_TranslucentBackground)
# 创建标签用于显示图片
self.pet_label = QLabel(self)
self.pet_label.setPixmap(self.animations["normal"][0])
# 设置标签大小为图片大小
self.pet_label.setFixedSize(128, 128)
# 确保标签从窗口左上角开始
self.pet_label.move(0, 0)
# 设置窗口大小为图片大小
self.resize(self.pet_label.size())
# 创建系统托盘图标
self.create_tray_icon()
# 显示在屏幕上
self.show()
def setupTimers(self):
# 动画计时器
self.animation_timer = QTimer(self)
self.animation_timer.timeout.connect(self.update_animation)
self.animation_timer.start(16) # 设置为16毫秒以达到60帧每秒
# 随机行为计时器
self.action_timer = QTimer(self)
self.action_timer.timeout.connect(self.random_action)
self.action_timer.start(5000) # 5秒执行一次随机行为
def create_tray_icon(self):
# 创建托盘图标
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon(self.animations["normal"][0]))
# 创建托盘菜单
tray_menu = QMenu()
# 添加菜单项
action_wake = QAction("唤醒", self)
action_wake.triggered.connect(self.wake_up)
action_sleep = QAction("睡觉", self)
action_sleep.triggered.connect(self.sleep)
action_quit = QAction("退出", self)
action_quit.triggered.connect(QApplication.quit)
action_feed = QAction("喂食", self)
action_feed.triggered.connect(self.feed)
action_pat = QAction("摸摸头", self)
action_pat.triggered.connect(self.pat)
action_status = QAction("查看状态", self)
action_status.triggered.connect(self.show_status)
tray_menu.addAction(action_wake)
tray_menu.addAction(action_sleep)
tray_menu.addAction(action_quit)
tray_menu.addAction(action_feed)
tray_menu.addAction(action_pat)
tray_menu.addAction(action_status)
tray_menu.addSeparator() # 添加分隔线
self.tray_icon.setContextMenu(tray_menu)
self.tray_icon.show()
def update_animation(self):
"""更新动画帧"""
print(f"当前动作: {self.current_action}, 当前帧: {self.current_frame}") # 调试信息
if not self.is_sleeping:
animations = self.animations[self.current_action]
self.current_frame = (self.current_frame + 1) % len(animations)
pixmap = animations[self.current_frame]
# 确保图片大小正确
if pixmap.width() != 128 or pixmap.height() != 128:
pixmap = pixmap.scaled(128, 128, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.pet_label.setPixmap(pixmap)
else:
# 如果是睡觉状态,确保显示睡觉的第一帧
self.pet_label.setPixmap(self.animations["sleeping"][0])
def random_action(self):
if not self.is_sleeping and not self.is_following_mouse:
# 根据心情和能量值决定行为
if self.energy < 30:
self.sleep()
elif self.pet_mood < 30:
# 心情不好时更容易睡觉
if random.random() < 0.3:
self.sleep()
else:
actions = ["normal", "walking"]
self.current_action = random.choice(actions)
if self.current_action == "walking":
self.random_walk()
def random_walk(self):
if self.current_action == "walking":
current_pos = self.pos()
# 在当前位置周围小范围随机移动
random_x = max(self.movement_area['left'],
min(self.movement_area['right'] - self.width(),
current_pos.x() + random.randint(-30, 30)))
random_y = max(self.movement_area['top'],
min(self.movement_area['bottom'] - self.height(),
current_pos.y() + random.randint(-30, 30)))
self.move(random_x, random_y)
def sleep(self):
print("进入睡觉状态") # 调试信息
self.is_sleeping = True
self.current_action = "sleeping"
self.update_animation()
def wake_up(self):
self.is_sleeping = False
self.current_action = "normal"
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.is_dragging = True
self.dragPosition = event.globalPos() - self.frameGeometry().topLeft()
if self.energy > 30: # 只有在能量充足时才会开心
self.current_action = "happy"
event.accept()
elif event.button() == Qt.RightButton:
self.tray_icon.contextMenu().exec_(event.globalPos())
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton:
self.move(event.globalPos() - self.dragPosition)
self.current_action = "walking"
event.accept()
def mouseDoubleClickEvent(self, event):
if event.button() == Qt.LeftButton:
if self.is_sleeping:
self.wake_up()
else:
self.sleep()
def auto_move(self):
"""自动在区域内移动"""
if not self.is_sleeping and self.current_action != "walking":
# 设置随机目标点
target_x = random.randint(
self.movement_area['left'],
self.movement_area['right'] - self.width()
)
target_y = random.randint(
self.movement_area['top'],
self.movement_area['bottom'] - self.height()
)
self.target_pos = QPoint(target_x, target_y)
self.current_action = "walking"
# 开始移动动画
self.start_movement_animation()
def start_movement_animation(self):
"""创建平滑移动动画"""
if self.target_pos:
# 计算当前位置到目标位置的向量
current_pos = self.pos()
dx = self.target_pos.x() - current_pos.x()
dy = self.target_pos.y() - current_pos.y()
# 计算移动步长(每次移动的距离)
step = 5
distance = (dx**2 + dy**2)**0.5
if distance > step:
# 计算规范化的方向向量
dx = dx / distance * step
dy = dy / distance * step
# 移动一步
new_x = current_pos.x() + dx
new_y = current_pos.y() + dy
self.move(int(new_x), int(new_y))
# 继续移动
QTimer.singleShot(50, self.start_movement_animation)
else:
# 到达目标点
self.move(self.target_pos)
self.target_pos = None
self.current_action = "normal"
def set_movement_area(self, left, top, right, bottom):
"""设置活动范围"""
self.movement_area = {
'left': max(0, left),
'top': max(0, top),
'right': min(self.screen.width(), right),
'bottom': min(self.screen.height(), bottom)
}
def load_and_verify_image(self, path):
"""加载并验证图片"""
pixmap = QPixmap(path)
if pixmap.isNull():
raise Exception(f"无法加载图片: {path}")
return pixmap
def update_status(self):
"""定期更新宠物状态"""
if not self.is_sleeping:
self.energy -= 1
if self.energy < 30:
self.pet_mood -= 2
if self.pet_mood < 0:
self.pet_mood = 0
if self.energy < 0:
self.energy = 0
self.sleep()
def feed(self):
"""喂食"""
if not self.is_sleeping:
self.current_action = "eating"
self.energy = min(100, self.energy + 30)
self.pet_mood = min(100, self.pet_mood + 10)
QTimer.singleShot(2000, self.back_to_normal) # 2秒后恢复正常
def pat(self):
"""摸头"""
if not self.is_sleeping:
self.current_action = "happy"
self.pet_mood = min(100, self.pet_mood + 15)
QTimer.singleShot(2000, self.back_to_normal) # 2秒后恢复正常
def back_to_normal(self):
"""恢复正常状态"""
if not self.is_sleeping:
self.current_action = "normal"
def show_status(self):
"""显示状态信息"""
status = f"心情值: {self.pet_mood}%\n能量值: {self.energy}%"
QMessageBox.information(self, "宠物状态", status)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.is_dragging = False
if not self.is_sleeping:
self.current_action = "normal"
if __name__ == '__main__':
app = QApplication(sys.argv)
pet = DesktopPet()
# 示例:设置活动范围(比如限制在屏幕右下角区域)
screen = app.primaryScreen().geometry()
pet.set_movement_area(
screen.width() // 2, # 左边界:屏幕中间
screen.height() // 2, # 上边界:屏幕中间
screen.width(), # 右边界:屏幕右边
screen.height() # 下边界:屏幕底部
)
sys.exit(app.exec_())
在运行代码前一定先安装一下代码中所需的库文件,然后将文件路径设置好,直接运行即可。无素材纯代码。
各位也可以做完之后打包起来发给你的小姐姐们,也算是你们共同创造的一个小生命哦!
标签:draw,python,赛博,self,宠物,current,action,ellipse,fill From: https://blog.csdn.net/m0_73747952/article/details/143705456