【使用Python编写游戏辅助工具】第五篇:打造交互式游戏工具界面:PySide6/PyQT高效构建GUI工具

news2024/12/1 0:27:43

image.png

前言

这里是【使用Python编写游戏辅助工具】的第五篇:打造交互式游戏工具界面:PySide6/PyQT高效构建GUI工具。本文主要介绍使用PySide6来实现构建GUI工具。

在前面,我们实现了两个实用的游戏辅助功能:

  1. 由键盘监听事件触发的鼠标连击功能;
  2. 对Windows窗口的多种操作(如隐藏、显示、设置前台等

在这一篇文章中,笔者将带领读者朋友们使用 PySide6/PyQT 将前面实现的功能封装成一个GUI工具,这样一个简单的游戏辅助工具初具雏形了。

即使你完全没有 PySide6/PyQT 这方面的开发经验也没关系,学习就完事了;

即便不想学习,笔者也会提供源码,拿来用就完事了。

本文章为本系列文章最后一篇,【使用Python编写游戏辅助工具】系列文章到此结束。


文章脉络概述

该工具大致的组织架构如下图所示:

  • 包含 鼠标连击Windows窗口操作等功能!
辅助工具
鼠标连击
Windows窗口操作

在本系列文章中,我们将逐步构建一个游戏辅助工具,它将提供以下主要功能(所有功能都是借助Python的相关库和模块来帮助我们实现的;):

  1. 鼠标连击器:实现自动触发连续鼠标点击动作,帮助我们实现连击操作;

  2. Windows窗口操作:利用Python的相关库和模块,我们将探索如何操作和控制Windows窗口,包括隐藏窗口、显示窗口、置顶窗口等操作;

系列文章脉络

系列文章内容大体如下,后续可能会更新新的文章。

  • 点击直达:【使用Python编写游戏辅助工具】第一篇:概述

  • 点击直达:【使用Python编写游戏辅助工具】第二篇:键盘监听的应用

  • 点击直达:【使用Python编写游戏辅助工具】第三篇:鼠标连击器的实现

  • 点击直达:【使用Python编写游戏辅助工具】第四篇:Windows窗口操作

  • 点击直达:【使用Python编写游戏辅助工具】第五篇:打造交互式游戏工具界面:PySide6/PyQT高效构建GUI工具

知识点📖📖

库和模块描述
PySide6/PyQT提供了Python与Qt框架的绑定,用于创建图形用户界面(GUI)

安装依赖

pip install pyside6

这个模块就可以很好的实现本文的主题。

实现

因文章篇幅有限,抓重点来讲。

本文的代码实在是太多了,也不知从哪说起。干脆把代码都贴上来,

操作示例

视频介绍:咸鱼之王后台挂机软件和 鼠标连点器 Python实现

  • 下面的动图主要展示了该工具可以最小化到系统托盘,并从系统托盘恢复的操作。

展示最小化到系统托盘.gif

项目组织

使用PySide6/PyQT 构建的GUI工具组织架构如下所示:

  • 采用了MVC模型,通过MVC模式的应用,可以实现数据、用户界面和业务逻辑的解耦,提高代码的可读性、可维护性和可测试性;
  • 模型、视图和控制器各自担负不同的责任,使代码更具结构化,并允许团队成员分工合作,独立开发和测试各个组件。
GameAssistant/
├── controllers/
│   ├── __init__.py
│   └── controller_main.py
├── make/
│   └── 辅助小工具.spec
├── models/
│   ├── invoke_func/
│   │   ├── __init__.py
│   │   ├── mouse_click.py
│   │   └── window_operate.py
│   ├── __init__.py
│   └── model_main.py
├── views/
│   ├── resources/
│   │   ├── main.ui
│   │   ├── trash.png
│   │   └── utils.qrc
│   ├── ui/
│   │   ├── __init__.py
│   │   ├── main_ui.py
│   │   └── utils_rc.py
│   ├── widgets/
│   │   ├── __init__.py
│   │   └── view_main.py
│   └── __init__.py
├── main.py
├── README.md
└── requirements.txt

绘制GUI界面

designer.exe 软件中,绘制出下图所示的工具界面(路径在views/resources/main.ui

  • 使用了QTabWidget 控件,绘制了两个页面。
    1. 鼠标连击 页面,有一个输入框;
    2. 后台挂机 页面,有一个输入框和两个按钮

这个比较简单,动手拖拽需要的控件,拼凑起来就可以了(有手就行。

如下动图所示:

工具展示.gif

model(模型)

这里使用了 QRunnable + QThreadPool 实现的线程池,并将所有任务都给到 线程池中运行。

  • 鼠标点击Windows操作 的代码放置在(models/invoke_func/
# -*- coding: utf-8 -*-
# Name:         model_main.py
# Author:       小菜
# Date:         2023/6/14 20:00
# Description:


from collections import defaultdict
from PySide6.QtCore import (QObject, QRunnable, QThreadPool, Signal)

from models.invoke_func.mouse_click import click_mouse
from models.invoke_func.window_operate import (show_window, hide_window)

flag = True


class WorkerRunnable(QRunnable):

    def __init__(self, func, *args, **kwargs):
        super().__init__()
        self.func = func
        self.args = args
        self.status_signal = kwargs.get('status_signal')

    def run(self):
        if not self.args:
            self.func()
        while flag:
            self.func(*self.args)

    def win_run(self):
        res = self.func(*self.args)
        self.status_signal.emit({'status': res})


class ModelMain(QObject):
    win_status_signal: Signal = Signal(dict)

    def __init__(self):
        super().__init__()
        self.thread_pool = QThreadPool()
        self.thread_status_map = defaultdict(bool)

    def stop_keyboard_listener(self):
        global flag
        if flag:
            flag = False
            thread_name: str = 'click'
            self.thread_status_map[thread_name] = False

    def click_operate(self, frequency: int = 10):
        global flag
        thread_name: str = 'click'
        if self.thread_status_map[thread_name]:
            return
        flag = True
        self.thread_status_map[thread_name] = True
        if frequency < 10:
            frequency = 10
        print(flag, thread_name, self.thread_status_map[thread_name], frequency)
        click_frequency_map = {
            10: [0.09],
            20: [0.035],
            30: [0.027],
            40: [0.014],
            50: [0.013],
            60: [0.005],
            70: [0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0],
            80: [0.01, 0.01, 0.01, 0],
            90: [0.01, 0.01, 0],
            100: [0.01, 0.0001, 0.0001, 0, 0],
            150: [0.001, 0.001, 0, 0, 0]
        }
        task = WorkerRunnable(
            click_mouse,
            click_frequency_map.get(frequency, 150)
        )
        self.thread_pool.start(task)

    def show_win_operate(self, title: str = None):
        task = WorkerRunnable(show_window,
                              title,
                              status_signal=self.win_status_signal)
        self.thread_pool.start(task.win_run)

    def hide_win_operate(self, title: str = None):
        task = WorkerRunnable(hide_window,
                              title,
                              status_signal=self.win_status_signal)
        self.thread_pool.start(task.win_run)

view(视图)

视图代码实现了

  • 添加工具到系统托盘,从系统托盘退出或恢复工具;
  • 按下键盘的 Esc就最小化到系统托盘;
# -*- coding: utf-8 -*-
# Name:         view_main.py
# Author:       小菜
# Date:         2023/6/14 20:00
# Description:
import os

from PySide6.QtGui import (QAction, QIcon, QShortcut, QKeySequence)
from PySide6.QtWidgets import (QMainWindow, QSystemTrayIcon, QMenu)

from views import Ui_MainWindow


class ViewMain(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None) -> None:
        super().__init__(parent=parent)
        self.setupUi(self)

        # 创建系统托盘图标相关的变量和对象
        self._restore_action = QAction()
        self._quit_action = QAction()
        self._tray_icon_menu = QMenu()

        # 创建系统托盘图标
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon(u":/trash.png"))
        self.tray_icon.setToolTip("辅助小工具")

        # 创建系统托盘图标的菜单和动作
        self.create_actions()
        self.create_tray_icon()
        self.tray_icon.show()

        # 连接系统托盘图标的激活信号到槽函数
        self.tray_icon.activated.connect(self.tray_icon_activated)

        # 键盘监听
        self.listen_keyboard()

    def tray_icon_activated(self, reason):
        # 当系统托盘图标被激活时的操作
        if reason == QSystemTrayIcon.ActivationReason.Trigger:
            self.restore_from_tray()

    def restore_from_tray(self):
        # 还原窗口
        if self.isMinimized():
            self.showNormal()
        elif self.isMaximized():
            self.showMaximized()
        else:
            self.show()

    def create_actions(self):
        # 创建系统托盘图标菜单的动作
        self._restore_action = QAction("显示", self)
        self._restore_action.triggered.connect(self.restore_from_tray)  # "显示"菜单项触发还原窗口的操作

        self._quit_action = QAction("退出", self)
        self._quit_action.triggered.connect(lambda: os._exit(0))  # "退出"菜单项触发退出应用程序的操作

    def create_tray_icon(self):
        # 创建系统托盘图标的菜单
        self._tray_icon_menu = QMenu(self)
        self._tray_icon_menu.addAction(self._restore_action)
        self._tray_icon_menu.addSeparator()
        self._tray_icon_menu.addAction(self._quit_action)

        self.tray_icon.setContextMenu(self._tray_icon_menu)
        self.tray_icon.show()

    def show_notification(self, title: str = '连击信息⚠', text: str = None, icon=QSystemTrayIcon.MessageIcon.Information):
        # 显示系统通知
        self.tray_icon.showMessage(
            title,
            f"警告!!!{text}",
            icon,
            2000
        )

    def listen_keyboard(self):
        # 键盘监听
        shortcut = QShortcut(QKeySequence("Esc"), self)
        # 当按下 Esc 键时隐藏窗口
        shortcut.activated.connect(self.hide)

controller(控制器

这里做的操作是将 model与view连接起来,充当了一个中间人的活儿。

# -*- coding: utf-8 -*-
# Name:         controller_main.py
# Author:       小菜
# Date:         2023/6/14 20:00
# Description:

import keyboard
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QSystemTrayIcon

from views import ViewMain
from models import (ModelMain, WorkerRunnable)

round_to_nearest_10 = lambda number: min(round(number / 10) * 10, 150)


class ControllerMain:

    def __init__(self):
        self.view = ViewMain()
        self.model = ModelMain()
        # 显示窗口
        self.view.show()
        # 初始化 键盘监听
        self.init_operate()

        # 绑定按钮
        self.view.btn_show_win.clicked.connect(self.show_win_operate)
        self.view.btn_hide_win.clicked.connect(self.hide_win_operate)

        # 绑定信号到槽函数
        self.model.win_status_signal.connect(self.window_listen)

    def init_operate(self):
        task = WorkerRunnable(self.listen_keyboard)
        self.model.thread_pool.start(task)

    def listen_keyboard(self):
        keyboard.add_hotkey('Ctrl+Shift+A', self.click_operate)
        keyboard.add_hotkey('Ctrl+Shift+Q', self.model.stop_keyboard_listener)
        keyboard.wait()

    def click_operate(self):
        frequency = 10
        # 创建匿名函数
        try:
            frequency = round_to_nearest_10(int(self.view.line_edit_click.text()))
            print(frequency)
        except (ValueError, TypeError):
            ...
        finally:
            self.model.click_operate(frequency=frequency)
            self.view.show_notification(text='开始点击')

    def show_win_operate(self):
        win_title = self.view.line_edit_title.text()
        if not win_title:
            return
        self.model.show_win_operate(title=win_title)

    def hide_win_operate(self):
        win_title = self.view.line_edit_title.text()
        if not win_title:
            return
        self.model.hide_win_operate(title=win_title)

    @Slot(dict)
    def window_listen(self, item):
        if not item.get('status'):
            self.view.show_notification(
                title='警告警告⚠',
                text='找不到窗口!!!',
                icon=QSystemTrayIcon.MessageIcon.Warning
            )

main函数

函数启动入口

# -*- coding: utf-8 -*-
# Name:         main.py
# Author:       小菜
# Date:         2023/6/14 20:00
# Description:


import sys
from ctypes import windll

from PySide6.QtWidgets import QApplication

from controllers import ControllerMain

if __name__ == '__main__':
    # 同步图标
    windll.shell32.SetCurrentProcessExplicitAppUserModelID('nothing')
    app = QApplication()
    # 关闭窗口时候不退出程序
    app.setQuitOnLastWindowClosed(False)
    controller = ControllerMain()
    # 事件循环
    sys.exit(app.exec())

总结🎈🎈

本文介绍了 PySide6/PyQT 打包游戏辅助工具的操作。

将前面介绍的 鼠标连点Windows窗口操作等功能打包成一个GUI工具,从更加方便后续的使用。

总而言之,本文通过详细介绍使用 PySide6/PyQT 打包GUI的操作,帮助读者理解和应用Python在游戏辅助工具开发中的打包功能。

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

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

相关文章

XSAN数据恢复-存储空间架构迁移时误格式化存储系统的XSAN数据恢复案例

XSAN数据恢复环境&#xff1a; 昆腾存储&#xff0c;MAC OS操作系统&#xff0c;存放视频类数据&#xff08;MXF、MOV等格式文件&#xff09;。 XSAN故障&检测&#xff1a; 将存储空间从XSAN架构迁移到STORNEXT架构后&#xff0c;存储空间中数据全部丢失。 故障存储中一共…

【3D图像分割】基于 Pytorch 的 VNet 3D 图像分割3(3D UNet 模型篇)

在本文中&#xff0c;主要是对3D UNet 进行一个学习和梳理。对于3D UNet 网上的资料和GitHub直接获取的代码很多&#xff0c;不需要自己从0开始。那么本文的目的是啥呢&#xff1f; 本文就是想拆解下其中的结构&#xff0c;看看对于一个3D的UNet&#xff0c;和2D的UNet&#x…

QCC TX 音频输入切换+提示声音

QCC TX 音频输入切换提示声音 QCC蓝牙芯片&#xff08;QCC3040 QCC3056 等等&#xff09;&#xff0c;AUX、I2S、USB输入 蓝牙音频输入&#xff0c;模拟输出是最常见的方式。 也可以再此基础上动态切换输入方式。 针对TX切换EQ,调节音量不能出提示声音问题&#xff0c;可以增…

Spring底层原理(五)

Spring底层原理(五) 本章内容 介绍Aware接口与InitializingBean接口、Bean的初始化与销毁、Scope Aware接口 作用:用于注入一些与容器相关的信息 类名作用BeanNameAware注入Bean的名称BeanFactoryAware注入BeanFactory容器ApplicationContextAware注入ApplicationContext容…

Pycharm 对容器中的 Python 程序断点远程调试

pycharm如何连接远程服务器的docker容器有两种方法&#xff1a; 第一种&#xff1a;pycharm通过ssh连接已在运行中的docker容器 第二种&#xff1a;pycharm连接docker镜像&#xff0c;pycharm运行代码再自动创建容器 本文是第一种方法的教程&#xff0c;第二种请点击以上的链接…

JavaScript中BOM与DOM

BOM window对象 所有的浏览器都支持window对象&#xff0c;他表示浏览器窗口&#xff0c; 所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员。 全局变量是 window 对象的属性。全局函数是 window 对象的方法。 接下来要讲的HTML DOM 的 document 也是…

遥遥领先,免费开源的django4-vue3前后端分离项目

星域后台管理系统前端介绍 &#x1f33f;项目简介 本项目前端基于当下流行且常用的vue3作为主要技术栈进行开发&#xff0c;融合了typescript和element-plus-ui&#xff0c;提供暗黑模式和白昼模式两种主题以及全屏切换&#xff0c;开发bug少&#xff0c;简单易学&#xff0c…

攻略合集!游戏要领你一定要知道!

大家好&#xff01;作为游戏玩家&#xff0c;我们都希望在游戏中能够成为顶尖的高手。为了帮助大家更好地掌握游戏的要领&#xff0c;我要分享一些实用的攻略和技巧。 首先&#xff0c;不同游戏有不同的技巧和要领。对于《绝地求生》来说&#xff0c;我们需要注重观察和战略规划…

C++和 C 混合编程处理

原因是因为有很多功能是用 C 语言开发的&#xff0c;而 C是兼容 C 的&#xff0c;C应该能直接使用这些功能&#xff0c;那么我们把 C调用 C 实现的功能的这个做法&#xff0c;称为混合编程 但是用 C 开发的功能&#xff0c;很可能已经用 C 编译器编程成目标文件(或打包成库了)…

Zinx框架-游戏服务器开发001:zinx框架的安装

文章目录 1 zinx下载地址1.1 zinx框架的源码路径&#xff1a;1.2 安装好之后动态库的位置 2 Zinx框架运行的基本概况3 测试Zinx-框架的基本使用3.0 流程预览3.1 初始化框架3.2 标准输入回显标准输出的编写思路3.2.1 回显Echo3.2.2 写标准输入stdin通道类&#xff0c;用通道输入…

协同办公系统:企业提质增效的利器

随着科技的不断发展&#xff0c;企业对于提高工作效率、优化管理流程、降低成本的需求日益迫切。协同办公系统应运而生&#xff0c;成为了许多企业提质增效的利器。那么&#xff0c;协同办公系统究竟是如何帮助企业实现这些目标的呢&#xff1f;本文将从以下几个方面进行详细阐…

【基于MRA:自适应高频融合和注入系数优化:Pansharpening】

Pansharpening Based on Adaptive High-Frequency Fusion and Injection Coefficients Optimization &#xff08;基于自适应高频融合和注入系数优化的全色锐化&#xff09; 全色锐化的目的是将多光谱&#xff08;MS&#xff09;图像与全色&#xff08;PAN&#xff09;图像融…

【实战Flask API项目指南】之六 数据库集成 SQLAlchemy

实战Flask API项目指南之 数据库集成 本系列文章将带你深入探索实战Flask API项目指南&#xff0c;通过跟随小菜的学习之旅&#xff0c;你将逐步掌握 Flask 在实际项目中的应用。让我们一起踏上这个精彩的学习之旅吧&#xff01; 前言 在上一篇文章中&#xff0c;我们实现了…

私有化部署即时通讯软件WorkPlus,全面适配信创环境

对于企业而言&#xff0c;保护数据的安全至关重要。WorkPlus即时通讯软件允许企业在自己的服务器上部署一套私有化的聊天工具&#xff0c;确保数据完全受控于企业内部。通过私有化部署&#xff0c;企业可以有效地管理和保护敏感信息&#xff0c;防止数据泄露和滥用。 另外&…

React基础知识02

一、通过属性来传值&#xff08;props&#xff09; react中可以使用属性&#xff08;props&#xff09;可以传递给子组件&#xff0c;子组件可以使用这些属性值来控制其行为和呈现输出。 例子&#xff1a; // 1.1 父组件 import React, { useState } from react // 1.2引入子…

浅谈安科瑞直流电表在荷兰光伏充电桩系统中的应用

摘要&#xff1a;本文介绍了安科瑞直流电表在荷兰光伏充电桩系统中的应用。主要用于充电桩的电流电压电能的计量。 Abstract: This article introduces the application of Acrel DC meters in PV charging pile system in Netherlands.The device is measuring current,volt…

腾讯云域名备案后,如何解析到华为云服务器Linux宝塔面板

一、购买域名并且进行备案和解析&#xff0c;正常情况下&#xff0c;购买完域名&#xff0c;如果找不到去哪备案&#xff0c;可以在腾讯云上搜索“备案”关键词就会出现了&#xff0c;所以这里不做详细介绍&#xff0c;直接进行步骤提示&#xff1a; 二、申请ssl证书&#xff0…

mysql简单备份和恢复

版本&#xff1a;mysql8.0 官方文档 &#xff1a;MySQL :: MySQL 8.0 Reference Manual :: 7 Backup and Recovery 1.物理备份恢复 物理备份是以数据文件形式备份。这种方式效率高点&#xff0c;适合大型数据库备份。物理备份可冷备可热备。 使用mysqlbackup 命令进行物理备…

命名数据网络(NDN)介绍

命名数据网络的由来 IP网络最开始其解决的问题是两个实体间点对点通信需求&#xff0c;实现资源共享。&#xff08;简单知道即可&#xff09; 随着互联网的发展&#xff0c;互联网用户对internet的需求现已经发生了巨大变化。目前面临着以下挑战 首先是随着以内容为中心&…

力扣刷题 day63:11-02

1.字符串中的第一个唯一字符 给定一个字符串 s &#xff0c;找到 它的第一个不重复的字符&#xff0c;并返回它的索引 。如果不存在&#xff0c;则返回 -1 。 方法一&#xff1a;两次遍历哈希表 #方法一&#xff1a;两次遍历哈希表 def firstUniqChar(s):d{}for i in s:if …