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

news2024/11/23 21:19:16

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

  • 前言
  • 1. 效果演示
  • 2. minesweeper.py
  • 3.图片文件
  • 其他文章

前言

今天来学习PyQt5的示例,其中主要涉及到一些触发函数窗口切换函数。

操作系统:Windows10 专业版

开发环境:Pycahrm Comunity 2022.3

Python解释器版本:Python3.8

第三方库:PyQt5

此项目只涉及到1个py文件,但是涉及到一堆图片文件,需要将我第四节中的图片保存下来,工程目录结构如下:
在这里插入图片描述

1. 效果演示

开局电脑会为我们走第一步,为的是更好的初始化棋盘
在这里插入图片描述

很不幸,喜欢走角落的我直接踩雷了
在这里插入图片描述

点击这个重置棋盘
在这里插入图片描述

右键单击标记地雷
在这里插入图片描述

这里还会显示游戏时间,当走出第一步将会开始计时
在这里插入图片描述

2. minesweeper.py

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

import random
import time

IMG_BOMB = QImage("./images/bug.png")
IMG_FLAG = QImage("./images/flag.png")
IMG_START = QImage("./images/rocket.png")
IMG_CLOCK = QImage("./images/clock-select.png")

NUM_COLORS = {
    1: QColor('#f44336'),
    2: QColor('#9C27B0'),
    3: QColor('#3F51B5'),
    4: QColor('#03A9F4'),
    5: QColor('#00BCD4'),
    6: QColor('#4CAF50'),
    7: QColor('#E91E63'),
    8: QColor('#FF9800')
}

LEVELS = [
    (8, 10),
    (16, 40),
    (24, 99)
]

STATUS_READY = 0
STATUS_PLAYING = 1
STATUS_FAILED = 2
STATUS_SUCCESS = 3

STATUS_ICONS = {
    STATUS_READY: "./images/plus.png",
    STATUS_PLAYING: "./images/smiley.png",
    STATUS_FAILED: "./images/cross.png",
    STATUS_SUCCESS: "./images/smiley-lol.png",
}


class Pos(QWidget):
    expandable = pyqtSignal(int, int)
    clicked = pyqtSignal()
    ohno = pyqtSignal()

    def __init__(self, x, y, *args, **kwargs):
        super(Pos, self).__init__(*args, **kwargs)

        self.setFixedSize(QSize(20, 20))

        self.x = x
        self.y = y

    def reset(self):
        self.is_start = False
        self.is_mine = False
        self.adjacent_n = 0

        self.is_revealed = False
        self.is_flagged = False

        self.update()

    def paintEvent(self, event):
        p = QPainter(self)
        p.setRenderHint(QPainter.Antialiasing)

        r = event.rect()

        if self.is_revealed:
            color = self.palette().color(QPalette.Background)
            outer, inner = color, color
        else:
            outer, inner = Qt.gray, Qt.lightGray

        p.fillRect(r, QBrush(inner))
        pen = QPen(outer)
        pen.setWidth(1)
        p.setPen(pen)
        p.drawRect(r)

        if self.is_revealed:
            if self.is_start:
                p.drawPixmap(r, QPixmap(IMG_START))

            elif self.is_mine:
                p.drawPixmap(r, QPixmap(IMG_BOMB))

            elif self.adjacent_n > 0:
                pen = QPen(NUM_COLORS[self.adjacent_n])
                p.setPen(pen)
                f = p.font()
                f.setBold(True)
                p.setFont(f)
                p.drawText(r, Qt.AlignHCenter | Qt.AlignVCenter, str(self.adjacent_n))

        elif self.is_flagged:
            p.drawPixmap(r, QPixmap(IMG_FLAG))

    def flag(self):
        self.is_flagged = True
        self.update()

        self.clicked.emit()

    def reveal(self):
        self.is_revealed = True
        self.update()

    def click(self):
        if not self.is_revealed:
            self.reveal()
            if self.adjacent_n == 0:
                self.expandable.emit(self.x, self.y)

        self.clicked.emit()

    def mouseReleaseEvent(self, e):

        if (e.button() == Qt.RightButton and not self.is_revealed):
            self.flag()

        elif (e.button() == Qt.LeftButton):
            self.click()

            if self.is_mine:
                self.ohno.emit()


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.b_size, self.n_mines = LEVELS[1]

        w = QWidget()
        hb = QHBoxLayout()

        self.mines = QLabel()
        self.mines.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.clock = QLabel()
        self.clock.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.setWindowTitle("扫雷")

        f = self.mines.font()
        f.setPointSize(24)
        f.setWeight(75)
        self.mines.setFont(f)
        self.clock.setFont(f)

        self._timer = QTimer()
        self._timer.timeout.connect(self.update_timer)
        self._timer.start(1000)  # 1 second timer

        self.mines.setText("%03d" % self.n_mines)
        self.clock.setText("000")

        self.button = QPushButton()
        self.button.setFixedSize(QSize(32, 32))
        self.button.setIconSize(QSize(32, 32))
        self.button.setIcon(QIcon("./images/smiley.png"))
        self.button.setFlat(True)

        self.button.pressed.connect(self.button_pressed)

        l = QLabel()
        l.setPixmap(QPixmap.fromImage(IMG_BOMB))
        l.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        hb.addWidget(l)

        hb.addWidget(self.mines)
        hb.addWidget(self.button)
        hb.addWidget(self.clock)

        l = QLabel()
        l.setPixmap(QPixmap.fromImage(IMG_CLOCK))
        l.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        hb.addWidget(l)

        vb = QVBoxLayout()
        vb.addLayout(hb)

        self.grid = QGridLayout()
        self.grid.setSpacing(5)

        vb.addLayout(self.grid)
        w.setLayout(vb)
        self.setCentralWidget(w)

        self.init_map()
        self.update_status(STATUS_READY)

        self.reset_map()
        self.update_status(STATUS_READY)

        self.show()

    def init_map(self):
        # Add positions to the map
        for x in range(0, self.b_size):
            for y in range(0, self.b_size):
                w = Pos(x, y)
                self.grid.addWidget(w, y, x)
                # Connect signal to handle expansion.
                w.clicked.connect(self.trigger_start)
                w.expandable.connect(self.expand_reveal)
                w.ohno.connect(self.game_over)

    def reset_map(self):
        # Clear all mine positions
        for x in range(0, self.b_size):
            for y in range(0, self.b_size):
                w = self.grid.itemAtPosition(y, x).widget()
                w.reset()

        # Add mines to the positions
        positions = []
        while len(positions) < self.n_mines:
            x, y = random.randint(0, self.b_size - 1), random.randint(0, self.b_size - 1)
            if (x, y) not in positions:
                w = self.grid.itemAtPosition(y, x).widget()
                w.is_mine = True
                positions.append((x, y))

        def get_adjacency_n(x, y):
            positions = self.get_surrounding(x, y)
            n_mines = sum(1 if w.is_mine else 0 for w in positions)

            return n_mines

        # Add adjacencies to the positions
        for x in range(0, self.b_size):
            for y in range(0, self.b_size):
                w = self.grid.itemAtPosition(y, x).widget()
                w.adjacent_n = get_adjacency_n(x, y)

        # Place starting marker
        while True:
            x, y = random.randint(0, self.b_size - 1), random.randint(0, self.b_size - 1)
            w = self.grid.itemAtPosition(y, x).widget()
            # We don't want to start on a mine.
            if (x, y) not in positions:
                w = self.grid.itemAtPosition(y, x).widget()
                w.is_start = True

                # Reveal all positions around this, if they are not mines either.
                for w in self.get_surrounding(x, y):
                    if not w.is_mine:
                        w.click()
                break

    def get_surrounding(self, x, y):
        positions = []

        for xi in range(max(0, x - 1), min(x + 2, self.b_size)):
            for yi in range(max(0, y - 1), min(y + 2, self.b_size)):
                positions.append(self.grid.itemAtPosition(yi, xi).widget())

        return positions

    def button_pressed(self):
        if self.status == STATUS_PLAYING:
            self.update_status(STATUS_FAILED)
            self.reveal_map()

        elif self.status == STATUS_FAILED:
            self.update_status(STATUS_READY)
            self.reset_map()

    def reveal_map(self):
        for x in range(0, self.b_size):
            for y in range(0, self.b_size):
                w = self.grid.itemAtPosition(y, x).widget()
                w.reveal()

    def expand_reveal(self, x, y):
        for xi in range(max(0, x - 1), min(x + 2, self.b_size)):
            for yi in range(max(0, y - 1), min(y + 2, self.b_size)):
                w = self.grid.itemAtPosition(yi, xi).widget()
                if not w.is_mine:
                    w.click()

    def trigger_start(self, *args):
        if self.status != STATUS_PLAYING:
            # First click.
            self.update_status(STATUS_PLAYING)
            # Start timer.
            self._timer_start_nsecs = int(time.time())

    def update_status(self, status):
        self.status = status
        self.button.setIcon(QIcon(STATUS_ICONS[self.status]))

    def update_timer(self):
        if self.status == STATUS_PLAYING:
            n_secs = int(time.time()) - self._timer_start_nsecs
            self.clock.setText("%03d" % n_secs)

    def game_over(self):
        self.reveal_map()
        self.update_status(STATUS_FAILED)


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    app.exec_()

3.图片文件

接下来就是图片文件了,全部都要保存,注意文件名(你没看错,这一大堆小图标都是):
smiley-lol.png
smiley-lol.png
smiley.png
在这里插入图片描述
rocket.py
在这里插入图片描述
plus.png
在这里插入图片描述
mushroom.png
在这里插入图片描述
ice-cream-sprinkles.png

在这里插入图片描述
hambuger.png
在这里插入图片描述
fruit.png
在这里插入图片描述
flag.png
在这里插入图片描述
cup.png
在这里插入图片描述
cross.png
在这里插入图片描述
clock-select.png
在这里插入图片描述
cake.png
在这里插入图片描述
cactus.png
在这里插入图片描述
bug.png
在这里插入图片描述
bomb.png
在这里插入图片描述

其他文章

如果你喜好学习PyQt5,还有以下文章值得你一看:
基于PyQt5的图形化界面开发——自制MQTT客户端软件
基于PyQt5的图形化界面开发——自制Redis图形化客户端
基于PyQt5的图形化界面开发——模拟医院管理系统
基于PyQt5的图形化界面开发——PyQt示例_计算器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/530675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三分钟了解Spring Boot启动原理

大家通常只需要给一个类添加一个SpringBootApplication 注解&#xff0c;然后再加一个main 方法里面固定的写法 SpringApplication.run(Application.class, args); 那么spring boot 到底是如何启动服务的呢。 接下来咱们通过源码解析。 Spring Boot 的启动原理可以概括为以下几…

一篇吃透布隆过滤器(Bloom Filter)及其使用场景

目录 1、什么是布隆过滤器 2、布隆过滤器的原理 2.1 布隆过滤器的数据结构 2.2 布隆过滤器的检索和插入原理 2.3 布隆过滤器元素的修改和删除 3、布隆过滤器的使用场景 3.1 Redis通过布隆过滤器防止缓存穿透 3.2 RocketMQ通过布隆过滤器防止消息重复消费 4、布隆过滤器…

DAPP开发(三)——智能合约开发

智能合约 Remix IDE 是开发以太坊智能合约的在线IDE工具&#xff0c;部署简单的智能合约非常方便。 http://remix.ethereum.org truffle 一个世界级的智能合约开发框架&#xff0c;专为智能合约而生。 管理智能合约的生命周期自动化合约测试可编程&#xff0c;可部署&…

linux安装jupyter notebook

目录 使用miniconda的conda安装 切换conda镜像源有两种方法: 设置密码: 修改配置文件: 启动 关闭进程: 使用miniconda的conda安装 conda install jupyter 如果镜像不好用则切换conda镜像源 切换conda镜像源有两种方法: 1. [shuqiqshuqiq bin]$ ./conda config --add…

MyBatis - 基础使用Ⅰ

这篇文章将讲解MyBatis的基础使用&#xff0c;MyBatis的学习是非常重要的&#xff0c;在前面学习servlet的时候&#xff0c;我们就能感受到将数据持久化存储的重要性&#xff0c;当时在使用JDBC的时候非常繁琐麻烦&#xff0c;但是在Spring里&#xff0c;提供了一种框架可以轻松…

真题详解(传引用)-软件设计(七十五)

真题详解&#xff08;补码转换&#xff09;-软件设计&#xff08;七十四)https://blog.csdn.net/ke1ying/article/details/130674214 分治算法技术设计______。 答案&#xff1a;1、问题划分 2、递归求解 3、合并解 虚拟存储体系_____两级构成。 解析&#xff1a;主存 和 辅…

vue项目打包成桌面应用并修改图标

目录 1. 打包为桌面应用 2.修改图标 1. 打包为桌面应用 1.在vux项目的终端执行打包 npm run build 2.会在项目文件夹里面出现一个dist文件夹 里面有这几个文件组成 3.在这里需要添加一个 package.json 文件 package.json 内容 {"name": "鼠标放图标上面的提…

环形链表解释约瑟夫问题

环形链表解释约瑟夫问题 来自尚硅谷开放课程的迁移学习&#xff0c;致敬尚硅谷的各位老师&#xff0c;受益匪浅&#xff01;&#xff01;&#xff01; 单向链表&#xff0c;双向链表&#xff0c;环形链表对比介绍 单向链表、双向链表和环形链表都是常见的链表数据结构&#…

介绍如何在 MySQL 中创建新用户并授予权限?

MySQL 是一个开源的关系型数据库管理系统&#xff0c;常用于存储和管理大量的结构化数据。在使用 MySQL 进行数据管理时&#xff0c;为了安全和方便管理&#xff0c;通常需要创建新用户并授予相应的权限。本文将介绍如何在 MySQL 中创建新用户并授予权限的方法。 创建新用户 在…

第十一章结构性模式—组合模式

文章目录 组合模式解决的问题概念结构 实例组合模式的分类优点使用场景 结构型模式描述如何将类或对象按某种布局组成更大的结构&#xff0c;有以下两种&#xff1a; 类结构型模式&#xff1a;采用继承机制来组织接口和类。 对象结构型模式&#xff1a;釆用组合或聚合来组合对…

shell脚本之“awk“命令

文章目录 1.awk工作原理2.awk命令演示操作部分2.1 按行输出文本演示操作2.2 BEGIN模式演示操作2.3 按字段输出文本演示操作2.4 通过管道、双引号调用Shell命令2.5 date命令演示操作2.6 getline参数详解2.7 awk命令的数组操作 3. 总结 1.awk工作原理 逐行读取文本&#xff0c;默…

位域和字节对齐

结构体中的位域 位域是一种特殊的结构体成员&#xff0c;它允许将一个字节或多个字节中的每个位作为一个独立的成员来使用。位域的语法形式为&#xff1a; struct {type [member_name] : width; }; 其中&#xff0c;type 表示位域成员的类型&#xff0c;可以是 int、unsigne…

网络编程——TCP编程

TCP编程 流程服务器客户端 函数接口1、socket2、bind3、listen4、accept5、recv6、send7、connet 实现双工通信server.ccelient.c优化代码 流程 在C语言中进行TCP编程的一般步骤如下&#xff1a; &#xff08;1&#xff09;包含头文件&#xff1a; 在代码中包含必要的头文件&a…

面对象QgsPolygon

几何对象中的面用QgsPolygon进行封装&#xff0c;也称为多边形简单的多边形是由一串点连接而成&#xff0c;并首尾闭合多边形的结构更复杂&#xff0c;除了有一个外部轮廓&#xff0c;还可能包括内部多个轮廓 创建面对象 QgsPolygon() #创建一个空的面 使用setExteriorRing设…

Spring AOP 实践指南

Spring AOP 实践指南 文章目录 Spring AOP 实践指南一、概述1、简介2、官方资料3、本文档说明 二、基本使用1、引入依赖2、定义切面3、定义切点4、创建 HelloController5、启动项目&#xff0c;访问测试 三、通知1、概述五种通知通知的顺序 2、通知方法接受的参数3、前置通知代…

Thread线程学习(1) 了解线程的基本知识——什么是线程

本专栏将记录有关线程方面的知识 在计算机科学领域中&#xff0c;线程&#xff08;Thread&#xff09;是一种执行计算机程序的基本单元。对于初学者来说&#xff0c;理解线程是学习并发编程的关键一步。本文将带你了解线程的基础知识&#xff0c;包括线程的定义、线程与进程的关…

GPT神奇应用:给孩子做每日安排

正文共 1163 字&#xff0c;阅读大约需要 4 分钟 家长必备技巧&#xff0c;您将在4分钟后获得以下超能力&#xff1a; 快速生成每日安排计划 Beezy评级 &#xff1a;B级 *经过简单的寻找&#xff0c; 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●图片…

JavaScript实现打印倒金字塔的代码

以下为实现打印倒金字塔的程序代码和运行截图 目录 前言 一、实现打印倒金字塔 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可以根据题目要求实现相关使用功…

动态规划专题

动态规划专题 最长递增子序列LeetCode 300. 最长递增子序列解题思路代码实现 LeetCode 354. 俄罗斯套娃信封问题解题思路代码实现 总结 不要纠结&#xff0c;干就完事了&#xff0c;熟练度很重要&#xff01;&#xff01;&#xff01;多练习&#xff0c;多总结&#xff01;&…

【Redis】Redis中bitmap的原理和使用

文章目录 一、原理二、BitMap 相关命令三、BitMap 空间计算四、使用场景1. 用户签到2. 统计活跃用户&#xff08;用户登陆情况&#xff09;3. 统计用户在线状态4. 实现布隆过滤器 五、总结 一、原理 先声明一下&#xff1a;Redis 有5种数据类型&#xff0c;而 BitMap 在 Redis…