基于PyQt5的图形化界面开发——打砖块

news/2024/7/7 18:37:51

0. 前言

本文使用 PyQt5实现一个打砖块小游戏

在这里插入图片描述

操作系统:Windows10 专业版

开发环境:Pycahrm Comunity 2022.3

Python解释器版本:Python3.8

第三方库:PyQt5

1. 砖块类定义

在构造函数__init__中,Brick类接受两个参数x和y,表示砖块初始化时的位置。通过调用super().__init__来初始化QGraphicsRectItem的基类,传入参数0, 0, 40, 20来设置砖块的初始大小。然后使用setPos方法将砖块的位置设置为给定的x和y。

接下来,使用setBrush方法设置砖块的颜色为红色。QColor(150, 0, 0)表示红色,其中150表示红色通道的亮度,而0表示绿色和蓝色通道的亮度为0。

使用setFlag方法设置QGraphicsItem.ItemIsSelectable标志,表示砖块可以被选中。

destroy方法用于移除砖块。在该方法中,使用self.scene.removeItem(self)将砖块从场景中移除。

class Brick(QtWidgets.QGraphicsRectItem):
    def __init__(self, x, y):
        super(Brick, self).__init__(0, 0, 40, 20)
        self.setPos(x, y)
        self.setBrush(QtGui.QColor(150, 0, 0))
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)

    def destroy(self):
        scene.removeItem(self)

2. 挡板类定义

这段代码定义了一个名为Paddle的类,该类继承自QGraphicsRectItem,用于表示打砖块游戏中的挡板。

在函数__init__中,Paddle类使用super().__init__方法来初始化QGraphicsRectItem的基类,设置挡板的初始大小为80像素宽和10像素高。然后使用setPos方法将挡板的位置设置为(360, 540)。

接下来,使用setBrush()方法设置挡板的颜色为蓝色。QColor(0, 0, 150)表示蓝色,其中0表示红色和绿色通道的亮度为0,而150表示蓝色通道的亮度为150。

使用setFlag方法设置QGraphicsItem.ItemIsMovable标志,表示挡板可以通过鼠标拖动来移动。

moveLeft方法用于将挡板向左移动。在该方法中,首先判断当前挡板的x坐标是否大于等于10,以确保挡板不会超出左边界。如果满足条件,则使用setPos方法将挡板的x坐标减去20,实现向左移动。

moveRight方法用于将挡板向右移动。在该方法中,首先判断当前挡板的x坐标是否小于等于710,以确保挡板不会超出右边界。如果满足条件,则使用setPos方法将挡板的x坐标加上20,实现向右移动。

class Paddle(QtWidgets.QGraphicsRectItem):
    def __init__(self):
        super(Paddle, self).__init__(0, 0, 80, 10)
        self.setPos(360, 540)
        self.setBrush(QtGui.QColor(0, 0, 150))
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)


    def moveLeft(self):
        if self.x() >= 10:
            self.setPos(self.x() - 20, self.y())

    def moveRight(self):
        if self.x() <= 710:
            self.setPos(self.x() + 20, self.y())

3. 碰撞检测

定义名为Ball的类,该类继承自QGraphicsEllipseItem,用于表示打砖块游戏中的小球。

在构造函数__init__中,Ball类使用super().__init__方法来初始化QGraphicsEllipseItem的基类,设置小球的初始大小为20像素直径。然后使用setPos方法将小球的位置设置为(390, 520)。

接下来,使用setBrush方法设置小球的颜色为绿色。QColor(0, 150, 0)表示绿色,其中0表示红色和蓝色通道的亮度为0,而150表示绿色通道的亮度为150。

Ball类还定义了dx和dy两个变量,分别表示小球在x轴和y轴上的速度。在构造函数中,将dx和dy初始化为-5,表示小球在水平和垂直方向上的速度为-5。

score变量用于记录游戏得分,初始值为0。

advance方法用于在每一帧更新时,更新小球的位置和状态。首先判断是否是更新阶段,如果不是,则直接返回。然后通过改变小球的位置,使用setPos方法将小球的x坐标增加dx,y坐标增加dy,实现小球的移动。

接下来,进行碰撞检测,判断小球是否与边界、挡板或砖块发生碰撞,并根据碰撞结果进行相应的处理。

class Ball(QtWidgets.QGraphicsEllipseItem):
    def __init__(self):
        super(Ball, self).__init__(0, 0, 20, 20)
        self.setPos(390, 520)
        self.setBrush(QtGui.QColor(0, 150, 0))
        self.dx = -5
        self.dy = -5
        self.score = 0

    def advance(self, phase):
        if not phase:
            return

        self.setPos(self.x() + self.dx, self.y() + self.dy)

        # 左右边界反弹
        if self.x() <= 0 or self.x() >= 780:
            self.dx *= -1

        # 上边界反弹
        if self.y() <= 0:
            self.dy *= -1

        # 下边界游戏结束
        if self.y() >= 600:
            exit()

        # 触碰挡板反弹
        if self.collidesWithItem(paddle):
            self.dy *= -1

        # 计分数组
        for brick in bricks:
            if self.collidesWithItem(brick):
                brick.destroy()
                bricks.remove(brick)
                self.dy *= -1
                self.score += 10
                break

4. 小球和游戏初始化

这段代码定义了一个名为MainScene的类,它是基于QtWidgets.QGraphicsScene的子类。这个类用来创建游戏场景,并包括初始化场景大小、添加挡板、添加球、初始化砖块位置和计分面板等功能。

在初始化方法__init__中,设置了场景的大小为800x600,并声明了全局变量bricks、paddle、ball和score。然后创建了一个Paddle对象,并将其添加到场景中,接着创建了一个Ball对象,并将其也添加到场景中。

接下来使用一个循环来初始化砖块的位置,创建了20个Brick对象,并根据位置将其添加到场景中,并将这些砖块对象存储在bricks列表中。

在代码的最后,创建了一个QLabel对象score用于显示得分,并设置其位置和字体样式,然后将其添加到场景中。同时定义了一个keyPressEvent方法,用来捕捉键盘按键事件,当按下左方向键时,调用paddle对象的moveLeft方法,当按下右方向键时,调用paddle对象的moveRight方法。

在advance方法中,利用父类的advance方法来更新场景状态,同时根据当前球的得分来更新显示的得分文本。

class MainScene(QtWidgets.QGraphicsScene):
    def __init__(self, parent=None):
        super(MainScene, self).__init__(parent)
        self.setSceneRect(0, 0, 800, 600)

        global bricks, paddle, ball, score

        paddle = Paddle()
        self.addItem(paddle)

        ball = Ball()
        self.addItem(ball)


        # 初始化砖块位置
        bricks = []
        for i in range(20):
            brick = Brick((i % 4) * 200, (i // 4) * 40)

            if random.randint(0,2):
                self.addItem(brick)
                bricks.append(brick)

        score = QtWidgets.QLabel()
        # 设置得分面板位置
        score.move(350,580)
        score.setText("Score: 00  ")
        score.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
        self.addWidget(score)

    def keyPressEvent(self, event):
        # 左方向键
        if event.key() == QtCore.Qt.Key_Left:
            paddle.moveLeft()
        # 右方向键
        elif event.key() == QtCore.Qt.Key_Right:
            paddle.moveRight()

    # 计分显示
    def advance(self):
        super(MainScene, self).advance()
        score.setText("Score: {}  ".format(str(ball.score)))

5. 完整代码

只需要这一个文件就能运行:
main.py

from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys
import random


class Brick(QtWidgets.QGraphicsRectItem):
    def __init__(self, x, y):
        super(Brick, self).__init__(0, 0, 40, 20)
        self.setPos(x, y)
        self.setBrush(QtGui.QColor(150, 0, 0))
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)

    def destroy(self):
        scene.removeItem(self)


class Paddle(QtWidgets.QGraphicsRectItem):
    def __init__(self):
        super(Paddle, self).__init__(0, 0, 80, 10)
        self.setPos(360, 540)
        self.setBrush(QtGui.QColor(0, 0, 150))
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)


    def moveLeft(self):
        if self.x() >= 10:
            self.setPos(self.x() - 20, self.y())

    def moveRight(self):
        if self.x() <= 710:
            self.setPos(self.x() + 20, self.y())


class Ball(QtWidgets.QGraphicsEllipseItem):
    def __init__(self):
        super(Ball, self).__init__(0, 0, 20, 20)
        self.setPos(390, 520)
        self.setBrush(QtGui.QColor(0, 150, 0))
        self.dx = -5
        self.dy = -5
        self.score = 0

    def advance(self, phase):
        if not phase:
            return

        self.setPos(self.x() + self.dx, self.y() + self.dy)

        # 左右边界反弹
        if self.x() <= 0 or self.x() >= 780:
            self.dx *= -1

        # 上边界反弹
        if self.y() <= 0:
            self.dy *= -1

        # 下边界游戏结束
        if self.y() >= 600:
            exit()

        # 触碰挡板反弹
        if self.collidesWithItem(paddle):
            self.dy *= -1

        # 计分数组
        for brick in bricks:
            if self.collidesWithItem(brick):
                brick.destroy()
                bricks.remove(brick)
                self.dy *= -1
                self.score += 10
                break


class MainScene(QtWidgets.QGraphicsScene):
    def __init__(self, parent=None):
        super(MainScene, self).__init__(parent)
        self.setSceneRect(0, 0, 800, 600)

        global bricks, paddle, ball, score

        paddle = Paddle()
        self.addItem(paddle)

        ball = Ball()
        self.addItem(ball)


        # 初始化砖块位置
        bricks = []
        for i in range(20):
            brick = Brick((i % 4) * 200, (i // 4) * 40)

            if random.randint(0,2):
                self.addItem(brick)
                bricks.append(brick)

        score = QtWidgets.QLabel()
        # 设置得分面板位置
        score.move(350,580)
        score.setText("Score: 00  ")
        score.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
        self.addWidget(score)

    def keyPressEvent(self, event):
        # 左方向键
        if event.key() == QtCore.Qt.Key_Left:
            paddle.moveLeft()
        # 右方向键
        elif event.key() == QtCore.Qt.Key_Right:
            paddle.moveRight()

    # 计分显示
    def advance(self):
        super(MainScene, self).advance()
        score.setText("Score: {}  ".format(str(ball.score)))


if __name__ == '__main__':
    app = QApplication([])
    window = QMainWindow()
    window.setWindowTitle("打砖块小游戏 ——By IoT_H2")

    view = QtWidgets.QGraphicsView()
    scene = MainScene(window)
    view.setScene(scene)
    window.setCentralWidget(view)

    timer = QtCore.QTimer()
    timer.timeout.connect(scene.advance)
    timer.start(20)

    window.show()
    sys.exit(app.exec_())

6. 运行效果演示

由于我是使用了random库生成砖块位置,所以每一局场景都是不同的:
在这里插入图片描述

在这里插入图片描述

7. Pyinstaller 编译exe程序

在这里插入图片描述
输入这个:

Pyinstaller -F main.py

在这里插入图片描述
等待编译完成

在./dist可以找到main.exe
在这里插入图片描述
双击它就能进入打砖块小游戏啦

PyQt5

基于PyQt5的图形化界面开发——天气应用

基于PyQt5的图形化界面开发——自制MQTT客户端

基于PyQt5的图形化界面开发——Windows内存资源监视助手[附带编译exe教程]

基于PyQt5的图形化界面开发——模拟医院管理系统

基于PyQt5的图形化界面开发——自制ssh工具

基于PyQt5的图形化界面开发——PyQt示例_计算器

基于PyQt5的图形化界面开发——PyQt示例_扫雷

基于PyQt5的图形化界面开发——自制Redis图形化客户端(文末附源码)

基于PyQt5的图形化界面开发——堆栈动画演示

基于PyQt5的图形化界面开发——队列动画演示


http://lihuaxi.xjx100.cn/news/1344008.html

相关文章

拖动排序功能的实现 - 使用HTML、CSS和JavaScript

引言 在现代Web应用程序中&#xff0c;拖动排序是一种常见的用户界面交互方式&#xff0c;它允许用户通过拖动元素来重新排列列表或项目的顺序。本文将介绍如何使用HTML、CSS和JavaScript来实现手动拖动排序功能。 一、HTML结构 首先&#xff0c;我们需要定义一个列表&#…

“AI in the Alps“:身体与精神的一场盛宴

作者&#xff1a;Christofer Dutz 得益于 Timecho 的组织和安排&#xff0c;我最近参加了一个精彩绝伦的活动 “AI in the Alps”&#xff0c;并从中收获颇丰。 这次活动是由德国工业界知名博客 “Industrial AI Podcast”&#xff08;http://aipod.de&#xff09;的组织者 Ro…

Vue3+Vite+Pinia+Naive后台管理系统搭建之八:构建 login.vue 登录页

前言 如果对 vue3 的语法不熟悉的&#xff0c;可以移步Vue3.0 基础入门&#xff0c;快速入门。 项目所需要的图片&#xff0c;icon图标&#xff08;推荐&#xff1a;阿里巴巴矢量图标库&#xff09;自行获取&#xff0c;命名一致就行。 1. 构建 src/components/CopyRight.vu…

【每日一题】2673. 使二叉树所有路径值相等的最小代价

【每日一题】2673. 使二叉树所有路径值相等的最小代价 2673. 使二叉树所有路径值相等的最小代价题目描述解题思路 2673. 使二叉树所有路径值相等的最小代价 题目描述 给你一个整数 n 表示一棵 满二叉树 里面节点的数目&#xff0c;节点编号从 1 到 n 。根节点编号为 1 &#…

云计算的学习(六)

六、云计算的发展趋势 1.云计算相关领域介绍 1.1物联网 物联网来源于互联网&#xff0c;是万物互联的结果&#xff0c;是人和物、物和物之间产生通信和交互。 物联网主要技术&#xff1a; RFID技术&#xff08;射频识别技术&#xff09;传感器技术嵌入式系统技术 1.2大数据…

scrapy集成selenium

前言 使用scrapy默认下载器---》类似于requests模块发送请求&#xff0c;不能执行js&#xff0c;有的页面拿回来数据不完整 想在scrapy中集成selenium&#xff0c;获取数据更完整&#xff0c;获取完后&#xff0c;自己组装成 Response对象&#xff0c;就会进爬虫解析&#xff0…

Pinia学习笔记 | 入门 - 映射辅助函数

文章目录 Pinia学习笔记简介Pinia是什么 代码分割机制案例1.挂载PiniaVue3Vue2&#xff1a;安装PiniaVuePlugin插件 2.定义store的两种方式options API 和 composition API使用options API模式定义使用composition API模式 2.业务组件对store的使用创建store实例解构访问Pinia容…

Java并发编程:解锁多线程魔法的奥秘

代码内容在最后 在当今并行和分布式计算的时代&#xff0c;Java作为一门强大的编程语言&#xff0c;在多线程编程方面扮演着重要的角色。本文将介绍Java并发编程的基础知识和最佳实践&#xff0c;并提供实际示例来演示多线程编程的应用和解决方案。 为什么需要并发编程&#xf…