PyQt5桌面应用开发(11):摸鱼也要讲基本法之桌面精灵

news2024/9/23 12:39:49

本文目录

  • 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):摸鱼也要讲基本法之桌面精灵

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

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

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

相关文章

orbslam3 编译时 Thirdparty sophus 库多种错误 redefinition, not declared in this scope

问题 在装了 ROS 的机器人系统里编译 orbslam3 时, 发现 Thirdparty sophus 库密集报错, 导致 orbslam3 无法完成编译 排查 同样的代码在装了 ROS 的笔记本 ubuntu18.04 系统里可以成功通过编译, 但是在装了同版本 ROS 的机器人 ubuntu18.04 系统里无法编译 Sophus 库本身…

探秘力扣之谜:如何轻松解决最长公共前缀问题?

本篇博客我会讲解力扣中的“14. 最长公共前缀”这道题&#xff0c;这是题目链接。 先来审题&#xff1a; 以下是几个输出示例&#xff1a; 提示&#xff1a; 这道题的思路其实并不难&#xff0c;也是一些字符串的常规操作的结合。大家可以先思考一下&#xff0c;再来听我讲…

TCP和UDP数据报文详解(区别及三次握手四次挥手详解)

总结TCP和UDP详解在后文 相同点&#xff1a; 1.都是传输层协议 2.都是全双工通信 区别&#xff1a; TCPUDP面向连接无连接一对一一对一&#xff0c;一对多&#xff0c;多对多&#xff0c;多对一(单播&#xff0c;多播&#xff0c;广播)可靠不保证可靠交付面向字节流面向报…

java变量与方法

方法 构造方法 定义&#xff1a;构造方法是一个特殊的成员方法&#xff0c;名字必须与类相同&#xff0c;在创建对象时由编译器自动调用&#xff0c;并且在生命周期内只调用一次 演示&#xff1a; 特性&#xff1a; 1.构造方法名字必须与类名相同&#xff08;如果不同&#…

Ajax XML

文章目录 AJAX XML 实例AJAX XML 实例实例解析 loadXMLDoc() 函数AJAX 服务器页面 AJAX XML 实例 AJAX 可用来与 XML 文件进行交互式通信。 AJAX XML 实例 下面的例子将演示网页如何使用 AJAX 来读取来自 XML 文件的信息&#xff1a; 代码部分 <!DOCTYPE html> <h…

Oracle—数据恢复

文档结构 1、恢复原理1.1、recyclebin&#xff08;回收站&#xff09;相关操作 2、恢复场景2.1、定义删除2.2、记录删除2.2.1、undo恢复2.2.2、redo恢复2.2.3、dbms_logmnr 场景&#xff1a;在 Oracle数据库使用过程中&#xff0c;误删对象或误删记录的情况时有发生&#xff1b…

07- 算法解读 Faster_R-CNN (目标检测)

要点&#xff1a; Faster_R-CNN RPN Fast R-CNN GitHub地址&#xff1a;vision/torchvision/models/detection at main pytorch/vision GitHub 三 Faster_R-CNN Faster R-CNN 是作者 Ross Girshick 继 Fast R-CNN 后的又一力作。同样使用 VGG16 作为网络的 backbone &am…

Windows 10 安装安卓子系统 WSA(Magisk/KernelSU)使用 WSA 工具箱安装 APK

from https://blog.zhjh.top/archives/XokySA7Rc1pkVvnxAEP5E 前提是系统为 Windows 10 22H2 10.0.19045.2311 或更高版本&#xff0c;尽量新。 步骤 使用 WSAPatch 和 MagiskOnWSALocal 安装 WSA 在 Microsoft Store 中安装 Ubuntu LTS&#xff08;或你喜欢的发行版&#xf…

Linux的进程

目录 一、进程占用的内存资源 二、进程的系统环境 三、进程一直在切换 四、父进程和子进程 五、进程状态 六、查看进程 1.ps -ef 列出所有进程 2.ps -lax 列出所有进程 3.ps aux列出所有进程 4.树形列出所有进程 七、作业&#xff08;用来查看管理进程&#xff09; …

windows环境安装运行kafka

一、配置java环境变量 1、下载安装包 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 2、添加系统变量&#xff1a;JAVA_HOMED:\Program Files (x86)\Java\jdk1.8.0_144 二、安装zooKeeper 1、 下载安装包 http://zookeeper.apache.…

【Java笔试强训 34】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;收件人列…

eks实践案例

Eks&#xff1a; ami-0c23197c88296c1b5 eks集群&#xff1a; 拉面 - 知乎 https://blog.csdn.net/saynaihe/category_12204222.html 什么是 Amazon EKS&#xff1f; - Amazon EKS kubectl 使用指南 https://zhuanlan.zhihu.com/p/364994610 k8s HPA自动伸缩 手把手教你 K8…

centOS7忘记登录密码该如何重新修改登录密码

文章目录 前言一、重新修改登录密码1.1、第一步1.2、第二步1.3、第三步1.4、第四步1.5、第五步1.6、第六步1.7、第七步1.8、第八步 前言 忘记密码并不可怕&#xff0c;只要学会方法&#xff0c;密码随时都可以找回。 一、重新修改登录密码 1.1、第一步 当打开centOS7之后忘记…

flutter系列之:做一个修改组件属性的动画

文章目录 简介flutter中的动画widgetAnimatedContainers使用举例总结 简介 什么是动画呢&#xff1f;动画实际上就是不同的图片连续起来形成的。flutter为我们提供了一个AnimationController来对动画进行详尽的控制&#xff0c;不过直接是用AnimationController是比较复杂的&a…

Colab使用教程(自己胡乱弄了半天,发现不行,还是得学一下)

文章目录 在google云盘中创建笔记本&#xff0c;并红设置笔记本的运行环境常见设置挂载云硬盘更改工作目录移动训练数据防止断链设置方法一、控制台设置方法二、安装Colab Alive 训练结果保存到硬盘中相关链接 在google云盘中创建笔记本&#xff0c;并红 云盘链接&#xff0c;…

K公司项目文件管理系统的分析与设计_kaic

摘 要 2020年的新冠疫情促进了线上办公市场的发展&#xff0c;加快了企业进入全面数字化时代的脚步。办公自动化是当今的大趋势&#xff0c;越来越多的企业采用电子文档的形式存储内外部资料。K公司是一家致力于为政府和企业提供数据安全服务的小型B2B企业&#xff0c;公司承…

Nessus 10.5 Auto Install for macOS Ventura(自动化安装 Nessus 试用版)

发布 Nessus 试用版自动化安装程序&#xff0c;支持 macOS Ventura、RHEL 9 和 Ubuntu 22.04 请访问原文链接&#xff1a;https://sysin.org/blog/nessus-auto-install-for-macos/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.o…

STM32输入捕获之快速构建频率计

简介&#xff1a;配置好STM32 CUBE IDE后只需要额外7行代码就可以构建一个频率计&#xff0c;目前只计算测频&#xff0c;占空比测量需要加入下降沿捕获标记&#xff08;暂时没做&#xff09;。 一、原理 频率&#xff1a;单位时间内完成周期性变化的次数&#xff0c;f 1/T。…

如何裁剪图片大小尺寸?

如何裁剪图片大小尺寸&#xff1f;平时我们在工作或者学习的时候&#xff0c;会经常需要将图片上传到不同的网站或者平台上&#xff0c;然而上传的时候经常会受到尺寸的限制&#xff0c;有时候尺寸太大就需要变小&#xff0c;为了确保上传成功&#xff0c;我们需要将图片进行裁…

Vue Router 最新版惊现使用 BUG

就在刚刚&#xff0c;当我在 写 Vue3 的项目的时候&#xff0c;使用到了 Vue Router 4 的最新版本&#xff08;4.1.6&#xff09;&#xff0c;然而在使用过程中&#xff0c;发现了一个使用问题&#xff0c;起初我还以为&#xff0c;是我这边的代码逻辑有问题&#xff0c;然而在…