PyQt5桌面应用开发(11):摸鱼也要讲基本法,两个字,16

news2024/9/29 3:22:51

本文目录

  • PyQt5桌面应用系列
  • 鼠标不要钱,手腕还不要钱吗?
  • PyQt5源程序
    • python文件
    • 资源定义
    • 界面定义文件
  • 技术要素
    • 资源文件
    • StyleSheets
    • QMainWindow设置
    • 窗体几何
  • 结论

PyQt5桌面应用系列

  • PyQt5桌面应用开发(1):需求分析
  • PyQt5桌面应用开发(2):事件循环
  • PyQt5桌面应用开发(3):并行设计
  • PyQt5桌面应用开发(4):界面设计
  • PyQt5桌面应用开发(5):对话框
  • PyQt5桌面应用开发(6):文件对话框
  • PyQt5桌面应用开发(7):文本编辑+语法高亮与行号
  • PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递
  • PyQt5桌面应用开发(9):经典布局QMainWindow
  • PyQt5桌面应用开发(10):界面布局基本支持
  • PyQt5桌面应用开发(11):摸鱼也要讲基本法,两个字,16

鼠标不要钱,手腕还不要钱吗?

每当打工人开始摸鱼的时候,打工人就会摸鱼。反正老板买的鼠标不要钱,我们来摸一个鼠标点击游戏的大鱼。我们要做个按钮精灵在屏幕上跑来跑去,鼠标点一下,另外一个数字精灵就显示点击次数。这样我们就可以开心的测试自己准确操作鼠标的能力,可以采取计时点击也可以采取点击FF次的时间。

游戏界面大概就是这样,由于录屏软件的功能和保密的原因,除了两个跑来跑去的烦人精之外的地方都被录成黑色,实际运行中,这两玩意就在当前显示屏的最上层跑来跑去。

在这里插入图片描述

当时鼠标不要钱 and 手腕也不要钱的时候,就可以玩出来这样的场景。程序键盘控制参见:1

在这里插入图片描述

这个程序有几个要点:

  1. 报表:点击次数显示在QLCDNumber中,当然我们都认识16进制,那就显示16进制,显得更酷!
  2. 数据:点击次数的数据就记录在QLCDNumber的值中。
  3. 交互要求:没有窗口,点击按钮在窗口范围内随机跑,计数控件追着点击按钮跑。

PyQt5源程序

这里首先就把代码发出来。

python文件

import importlib
import subprocess
import sys
from random import randint

from PyQt5 import uic
from PyQt5.QtCore import QSize, Qt, QFile, QIODevice, QRect
from PyQt5.QtGui import QIcon, QKeyEvent, QKeySequence
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QLCDNumber, QStyleFactory


# pyinstaller counting.py -w -y --add-data "countingmainwindow.ui;." --add-data "resources_rc.py;."
# produce distributed version in windows, run it first to get resouces_rc.py

# workaround with qrc
def make_rc(fn: str):
    if fn.endswith(".qrc"):
        name, _ = fn.split(".")
        with subprocess.Popen(["pyrcc5", fn, "-o", f"{name}_rc.py"], stdout=subprocess.PIPE) as proc:
            msg = proc.stdout.read()
            if len(msg) > 0:
                raise IOError(f"error compile qrc file: {msg}")


# load it or compile and load it
try:
    importlib.import_module(f"resources_rc")
except:
    make_rc("resources.qrc")
    importlib.import_module(f"resources_rc")


class CountingMainWindow(QMainWindow):
    def __init__(self):
        super(CountingMainWindow, self).__init__()

        uic.loadUi("countingmainwindow.ui", self)
        self.pushButton: QPushButton
        self.lcdNumber: QLCDNumber

        self.pushButton.setObjectName("btn")
        self.pushButton.setIconSize(QSize(48, 48))
        self.pushButton.setIcon(QIcon(":imgs/click.png"))
        self.pushButton.setText("")

        self.pushButton.setFixedSize(100, 100)
        self.lcdNumber.setFixedSize(100, 100)
        self.lcdNumber.setDigitCount(2)
        self.lcdNumber.setHexMode()

        self.pushButton.clicked.connect(self.move_widgets_like_crazy)
        self.pushButton.clicked.connect(lambda check: self.lcdNumber.display(self.lcdNumber.intValue() + 1))
        self.lcdNumber.overflow.connect(lambda: self.lcdNumber.display(0))

        self.setMenuBar(None)
        self.setStatusBar(None)

        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setWindowState(Qt.WindowFullScreen)

    def move_widgets_like_crazy(self, check: bool):
        self.pushButton: QPushButton
        self.lcdNumber: QLCDNumber

        # geometry return position related to its parent
        # relocated pushbutton
        rect_window: QRect = self.geometry()
        rect_button: QRect = self.pushButton.geometry()
        x = randint(10, rect_window.width() - rect_button.width() - 10)
        y = randint(10, rect_window.height() - rect_button.height() - 10)
        rect_button.setX(x)
        rect_button.setY(y)
        self.pushButton.setGeometry(rect_button)

        # lcdnumber moving toward pushbutton
        rect_lcd: QRect = self.lcdNumber.geometry()
        dx = rect_button.x() - rect_lcd.x()
        rect_lcd.setX(rect_lcd.x() + dx // 3)
        dy = rect_button.y() - rect_lcd.y()
        rect_lcd.setY(rect_lcd.y() + dy // 3)
        self.lcdNumber.setGeometry(rect_lcd)

    def keyPressEvent(self, event: QKeyEvent) -> None:
        if event.matches(QKeySequence.Cancel):
            self.close()
        if event.key() == Qt.Key_C:
            self.pushButton.clicked.emit(False)
        if event.key() == Qt.Key_R:
            self.lcdNumber.display(0)
        super(CountingMainWindow, self).keyPressEvent(event)


def read_qss_file(filename: str) -> str:
    file = QFile(filename)
    if not file.open(QIODevice.ReadOnly | QIODevice.Text):
        raise IOError(f"Cannot open file: {filename}")
    qss = str(file.readAll(), encoding="utf-8")
    return qss


if __name__ == '__main__':
    app = QApplication([])
    app.setStyle(QStyleFactory.create("Fusion"))
    app.setStyleSheet(read_qss_file(":stylesheets/style.qss"))
    mw = CountingMainWindow()
    mw.show()
    sys.exit(app.exec_())

资源定义

程序还包括了一个资源文件(附带一个png的图标,一个qss文件)和一个界面的ui文件。


<RCC>
    <qresource>
        <file>imgs/click.png</file>
        <file>stylesheets/style.qss</file>
    </qresource>
</RCC>

图标放于imgs/目录,qss文件放于stylesheets/目录。

在这里插入图片描述

QPushButton#btn {
    background-color: "cyan";
    border-radius: 15px;
    border: 1px solid black;
    padding: 5px;
}

QPushButton#btn:hover {
    background-color: "cyan";
    border-radius: 15px;
    border: 1px solid red;
    padding: 5px;
    padding-bottom: 1px;
}

QPushButton#btn:pressed {
    background-color: "#8E0000";
    border-radius: 15px;
    border: 1px solid red;
    padding: 5px;
    padding-bottom: 3px;
}

QLCDNumber {
    background-color: "lightgray";
    color: #FE5F01;
    border-radius: 15px;
    border: 1px solid black;
    padding: 5px;
}

程序运行时,如果pyrcc5在目录,那么很简单,只要上面的三个文件按照qrc中的目录放置,就能自动产生resource_rc.py,并且自动导入。

pyrcc5 resouces.qrc -o resources_rc.py

界面定义文件

designer生成的ui文件最简单,就随便拉一个QMainWindow,上面丢一个按钮QPushButton,一个LCDNumber,保存就行。

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
    <class>my_ui::CountingMainWindow</class>
    <widget class="QMainWindow" name="my_ui::CountingMainWindow">
        <property name="geometry">
            <rect>
                <x>0</x>
                <y>0</y>
                <width>803</width>
                <height>402</height>
            </rect>
        </property>
        <property name="windowTitle">
            <string>CountingMainWindow</string>
        </property>
        <widget class="QWidget" name="centralwidget">
            <widget class="QPushButton" name="pushButton">
                <property name="geometry">
                    <rect>
                        <x>10</x>
                        <y>10</y>
                        <width>136</width>
                        <height>41</height>
                    </rect>
                </property>
                <property name="sizePolicy">
                    <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
                        <horstretch>0</horstretch>
                        <verstretch>0</verstretch>
                    </sizepolicy>
                </property>
                <property name="font">
                    <font>
                        <pointsize>24</pointsize>
                    </font>
                </property>
                <property name="text">
                    <string>Clicked:</string>
                </property>
            </widget>
            <widget class="QLCDNumber" name="lcdNumber">
                <property name="geometry">
                    <rect>
                        <x>410</x>
                        <y>180</y>
                        <width>64</width>
                        <height>23</height>
                    </rect>
                </property>
                <property name="sizePolicy">
                    <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
                        <horstretch>0</horstretch>
                        <verstretch>0</verstretch>
                    </sizepolicy>
                </property>
                <property name="font">
                    <font>
                        <pointsize>24</pointsize>
                    </font>
                </property>
                <property name="smallDecimalPoint">
                    <bool>false</bool>
                </property>
                <property name="mode">
                    <enum>QLCDNumber::Hex</enum>
                </property>
            </widget>
        </widget>
        <widget class="QStatusBar" name="statusbar"/>
    </widget>
    <resources/>
    <connections/>
</ui>

在这里插入图片描述

技术要素

这个游戏中展示了大量好玩的东西。

资源文件

资源文件的定义和访问是一个很好的方式,pyrcc5命令把图形文件、数据文件编译成二进制的形式,在程序中就能够以:imgs/click.png:stylesheets/style.qss
这样的形式访问。在打包文件或者提供程序的时候,只需要提供pyrcc5生成的py文件就行。

这里值得注意的时读入qss文件的那个函数。

    with open(":stylesheets/style.qss") as fid:
    print(fid.read())

经过测试是不行的,QFile才行。但是QFile读出来的是QByteArray,要用str来转换成字符串。

def read_qss_file(filename: str) -> str:
    file = QFile(filename)
    if not file.open(QIODevice.ReadOnly | QIODevice.Text):
        raise IOError(f"Cannot open file: {filename}")
    qss = str(file.readAll(), encoding="utf-8")
    return qss

图形文件用QIcon(":imgs/click.png")可以访问。

StyleSheets

这个程序里面采用了setStyleSheet的方式来设置控件的显示方式,也是一个很有意思的话题。因为这个话题很大,就不再这里详细讲述。

QMainWindow设置

为了设置成背景透明,全屏疯跑,对QMainWindow进行了设置。

self.setAttribute(Qt.WA_TranslucentBackground)
self.setWindowFlags(Qt.FramelessWindowHint)
self.setWindowState(Qt.WindowFullScreen)

第一个函数是属性,WindowAttribute,这组枚举类型用WA开头,背景透明就是其中之一。这个Attribute列表那么长,我就不列了:Attribute。

setWindowFlags是设置窗口的特性。枚举列表在窗体特性。

setWindowState是设置窗口的状态。

  • Qt::WindowNoState 常规状态
  • Qt::WindowMinimized 最小化状态
  • Qt::WindowMaximized 最大化状态
  • Qt::WindowFullScreen 全屏状态
  • Qt::WindowActive 活动状态(例如键盘输入焦点)

这三个状态一设,就有了我们的按钮精灵可以到处跑。

窗体几何

这段让按钮到处跑的代码中使用geometrysetGeometry这一对函数来实现。这一对函数获得的是一个QRect,包括x,y,w,h,位置坐标(左上角)和长宽。这些尺寸都是相对于夫控件来定义的。

def move_widgets_like_crazy(self, check: bool):
    self.pushButton: QPushButton
    self.lcdNumber: QLCDNumber

    # geometry return position related to its parent
    # relocated pushbutton
    rect_window: QRect = self.geometry()
    rect_button: QRect = self.pushButton.geometry()
    x = randint(10, rect_window.width() - rect_button.width() - 10)
    y = randint(10, rect_window.height() - rect_button.height() - 10)
    rect_button.setX(x)
    rect_button.setY(y)
    self.pushButton.setGeometry(rect_button)

    # lcdnumber moving toward pushbutton
    rect_lcd: QRect = self.lcdNumber.geometry()
    dx = rect_button.x() - rect_lcd.x()
    rect_lcd.setX(rect_lcd.x() + dx // 3)
    dy = rect_button.y() - rect_lcd.y()
    rect_lcd.setY(rect_lcd.y() + dy // 3)
    self.lcdNumber.setGeometry(rect_lcd)

结论

  1. 实现桌面精灵在PyQt5里面很简单,设置窗口的属性就可以;
  2. PyQt5可以把资源文件整合到程序中,作为一个py文件,在Qt5中就直接编译到exe中;
  3. 控件的直接定位用geometrysetGoemetry完成,x,y的数值是相对于父节点的左上角定义的。

  1. 作弊码:按C,疯狂点击;按空格,相当于点击;按R重置技术;按ESC,退出。 ↩︎

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

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

相关文章

EC6110-Hi3798MV310-当贝纯净桌面-卡刷强刷固件包

EC6110-Hi3798MV310-当贝纯净桌面-卡刷强刷固件包-内有教程及短接点 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用的软件&a…

【信息安全案例】——网络攻击分析(学习笔记)

&#x1f4d6; 前言&#xff1a;随着信息技术的发展&#xff0c;网络空间的斗争可谓是兵家必争之地&#xff0c;网络攻击的事件层出不穷。道高一尺魔高一丈&#xff0c;了解常见的网络攻击类型有利于我们日后工作生活的安全稳定。 目录 &#x1f552; 1. 网络攻击威胁&#x1f…

CM211-2-YS-Hi3798MV300/MV310-当贝纯净桌面线刷固件包(可救砖)

CM211-2-YS-Hi3798MV300&#xff0f;MV310-当贝纯净桌面线刷固件包&#xff08;可救砖&#xff09;-内有教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&…

05- 算法解读 R-CNN (目标检测)

要点&#xff1a; R-CNN可以说是利用深度学习进行目标检测的开山之作。 一 R-CNN算法 R-CNN可以说是利用深度学习进行目标检测的开山之作。作者Ross Girshick多次 在PASCAL VOC的目标检测竞赛中折桂&#xff0c;曾在2010年带领团队获得终身成就奖。 RCNN算法流程可分为4个步…

ACM输入输出

写在前面 主要记录一下ACM输入输出的写法&#xff1b; 一、输入数值 1. 给定N的定长多行输入 题目&#xff1a;https://ac.nowcoder.com/acm/contest/5657/B&#xff1b; 代码&#xff1a; #include <iostream> #include <stdio.h> using namespace std;int m…

JavaWeb《后端内容:1. Tomcat - Servlet - Thymeleaf》

目录 1. 基础概念 1.1 BS架构和CS架构 1.2 Tomcat图解 2.TomCat 2.1 IDEA配置web项目和tomcat 2.2 idea启动TomCat因为端口号失败的问题 3.Servlet使用流程 3.1 Servlet简单图解 3.2 Servlet导入依赖 3.3 编写Servlet和add.html 3.4 试着使用Jdbc和Dao层连接水果库存…

如何是Jetpack Compose构建漂亮的应用程序

如何是Jetpack Compose构建漂亮的应用程序 Jetpack compose 是在 Android 上构建 UI 的未来。 如果您完全不熟悉 android 并且不知道 Jetpack Compose 是什么——它基本上是一种构建本机用户界面的新方法。 Jetpack compose官方站点 https://developer.android.com/jetpack/co…

MySQL执行顺序

MySQL执行顺序 MySQL语句的执行顺序也是在面试过程中经常问到的问题&#xff0c;并且熟悉执行顺序也有助于SQL语句的编写。 SELECT FROM JOIN ON WHERE GROUP BY HAVING ORDER BY LIMIT执行顺序如下&#xff1a; FROM ON JOIN WHERE GROUP BY # (开始使用别名) SUM # SUM等…

4.4——多重继承

在前面学习了一个派生类只有一个基类&#xff0c;这种派生方法称为单继承或单基派生。当一个派生类具有两个或多个基类时&#xff0c;这种派生方法称为多重继承或多基派生。 多重继承派生类的声明 在C中&#xff0c;声明具有两个以上基类的派生类与声明单基派生类的形式相似&a…

K8S:二进制安装K8S(单台master)

目录 一、安装K8S 1、拓扑图​编辑 2、系统初始化配置 3、部署docker引擎 4、部署etcd集群 ①etcd简介 ②准备签发证书环境 ③etcd部署 5、master部署组件 6、 部署 Worker Node 组件 7、 node节点部署flannel网络插件 一、安装K8S 1、拓扑图​​​​​​​ 2、系…

C++好难(4):类和对象(下)

okk我们终于来到了C类和对象的最后一节&#xff0c;大多都是对之前学习的内容做的补充 所以加油继续冲啦&#xff01; ∧_∧::   (&#xff65;ω&#xff65;):: /⌒  ⌒):: /へ_&#xff3f; / /:: (&#xff3f;&#xff3c;&#xff3c; &#xff90;)/::   &am…

一种轻松且客观介绍大模型方式,避免过度解读:一

这是我关于《一种轻松且客观介绍大模型方式&#xff0c;避免过度解读》第一篇 一、前言 这篇文章旨在为没有计算机科学背景的读者提供一些关于ChatGPT及其类似的人工智能系统&#xff08;如GPT-3、GPT-4、Bing Chat、Bard等&#xff09;如何工作的原理。ChatGPT是一种聊天机器…

【网络安全】mysql数据库提权

数据库提权 数据库提权适用场景前提条件提权步骤如何探查数据库服务是否启动 mysql提权mysql 获取数据库最高权限账号密码方法1.读取网站配置文件2. 读取数据库存储或备份文件3. 脚本暴力破解 利用udf提权udf定义udf提权原理udf常用函数1. 获取mysql版本号2.udf.dll放置到被攻击…

华为P60 Pro灵犀双Wi-Fi流畅通信,让你体验超凡网速

家里装有宽带的朋友&#xff0c;用手机进行无线网络连接时&#xff0c;经常会发现有2.4G和5G两个Wi-Fi信号网络。Wi-Fi信号是通过无线电波传输的&#xff0c;2.4G和5G是两个不同的频段&#xff0c;Wi-Fi信号就在这样的频段内进行传输。 2.4G信号频率低&#xff0c;在空气或障碍…

【机器学习】集成学习(理论)

集成学习&#xff08;理论&#xff09; 目录 一、何为集成学习二、集成学习最简单的模型&#xff1a;投票策略三、弱学习器的组合算法&#xff1a;自助聚合&#xff08;Bagging模型&#xff09;1、数据划分方法&#xff1a;自助法&#xff08;Bootstrap Method&#xff09;2、B…

决策树的介绍

一、介绍 决策树 (decision tree) 是一类常见的机器学习方法。它是一种树形结构&#xff0c;其中每个内部节点表示一个属性上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff0c;最后每个叶节点代表一种分类结果。 例如&#xff0c;我们要对"这是好瓜吗?&qu…

3.docker—应用部署MySQL

文章目录 1、mysql部署2、使用Navicat客户端来连接 docker应用部署 docker出现后&#xff0c;这些软件的安装会变得比较简单 1、mysql部署 四步走&#xff1a; 1️⃣搜索mysql镜像 2️⃣拉取mysql镜像 3️⃣创建容器 4️⃣操作 遇到问题&#xff1a; 容器内的网络服务和…

MySQL定时刷新数据

一、步骤 1.查看定时策略是否开启&#xff0c;查看命令: show variables like %event_sche%; 2.显示的 event_scheduler 为 OFF 时用以下命令开启: set global event_scheduler1; 3.创建存储过程 use toursim_platform; -- 选择数据库toursim_platform delimiter // create pro…

【刷题之路Ⅱ】LeetCode 86. 分隔链表

【刷题之路Ⅱ】LeetCode 86. 分隔链表 一、题目描述二、解题1、方法1——先分离再连接1.1、思路分析1.2、代码实现 2、方法2——将较大的节点后移2.1、思路分析2.2、代码实现 一、题目描述 原题连接&#xff1a; 86. 分隔链表 题目描述&#xff1a; 给你一个链表的头节点 head…