基于Pyside6开发一个通用的在线升级工具

news2024/12/26 21:03:52

UI

main.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>562</width>
    <height>96</height>
   </rect>
  </property>
  <property name="font">
   <font>
    <family>微软雅黑</family>
    <pointsize>10</pointsize>
   </font>
  </property>
  <property name="windowTitle">
   <string>在线升级</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QProgressBar" name="pbDownload">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>60</y>
      <width>541</width>
      <height>31</height>
     </rect>
    </property>
    <property name="value">
     <number>0</number>
    </property>
    <property name="alignment">
     <set>Qt::AlignCenter</set>
    </property>
    <property name="textVisible">
     <bool>false</bool>
    </property>
   </widget>
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>10</y>
      <width>541</width>
      <height>41</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <family>微软雅黑</family>
      <pointsize>10</pointsize>
     </font>
    </property>
    <property name="text">
     <string>在线升级中,请稍等...</string>
    </property>
    <property name="alignment">
     <set>Qt::AlignCenter</set>
    </property>
   </widget>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

main.py

# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'main.ui'
##
## Created by: Qt User Interface Compiler version 6.6.3
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
    QMetaObject, QObject, QPoint, QRect,
    QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
    QFont, QFontDatabase, QGradient, QIcon,
    QImage, QKeySequence, QLinearGradient, QPainter,
    QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QProgressBar,
    QSizePolicy, QWidget)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(562, 96)
        font = QFont()
        font.setFamilies([u"\u5fae\u8f6f\u96c5\u9ed1"])
        font.setPointSize(10)
        MainWindow.setFont(font)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.pbDownload = QProgressBar(self.centralwidget)
        self.pbDownload.setObjectName(u"pbDownload")
        self.pbDownload.setGeometry(QRect(10, 60, 541, 31))
        self.pbDownload.setValue(0)
        self.pbDownload.setAlignment(Qt.AlignCenter)
        self.pbDownload.setTextVisible(False)
        self.label = QLabel(self.centralwidget)
        self.label.setObjectName(u"label")
        self.label.setGeometry(QRect(10, 10, 541, 41))
        self.label.setFont(font)
        self.label.setAlignment(Qt.AlignCenter)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)
    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"\u5728\u7ebf\u5347\u7ea7", None))
        self.label.setText(QCoreApplication.translate("MainWindow", u"\u5728\u7ebf\u5347\u7ea7\u4e2d,\u8bf7\u7a0d\u7b49...", None))
    # retranslateUi

PyDesigner

主程序

upgrade_service.py

import time

import configparser
import requests
import sys
import threading
import psutil
import os
import zipfile
import warnings

from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QIcon, QMouseEvent
from ui.main import Ui_MainWindow
from qt_material import apply_stylesheet

'''
pip install pyinstaller pyside6 qt_material psutil requests

pyside6-uic -o ../../ui/main.py ../../ui/main.ui

pyinstaller --clean --icon=upgrade.ico D:\\1_local_project\IMVision\IMVDXUpgrade\\upgrade_service.py
pyinstaller upgrade_service.spec --noconfirm
'''

warnings.filterwarnings('ignore', category=DeprecationWarning)


class UpgradeInfo(object):
    def __init__(self):
        self.username = ''
        self.version = ''
        self.url = ''
        self.extra_file = 0
        self.extra_path = ''
        self.remark = ''


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.setWindowFlags(Qt.FramelessWindowHint)

        # 开启鼠标追踪
        self.setMouseTracking(True)
        # 记录鼠标按下时的位置
        self.drag_start_position = None

        self.setWindowIcon(QIcon('icons/upgrade.ico'))
        self.label.setStyleSheet(self.label.styleSheet() + 'font-size: 20px;')

        self.upgrade_finished = False
        self.finished_chunk_size = 0
        self.total_file_count = 0
        threading.Thread(target=self.download_file).start()

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.listen_download_state)
        self.timer.start(100)

    def mousePressEvent(self, event: QMouseEvent) -> None:
        if event.button() == Qt.LeftButton:
            self.drag_start_position = event.pos()

    def mouseMoveEvent(self, event: QMouseEvent) -> None:
        if self.drag_start_position is not None:
            delta = event.pos() - self.drag_start_position
            self.move(self.pos() + delta)

    def mouseReleaseEvent(self, event: QMouseEvent) -> None:
        if event.button() == Qt.LeftButton:
            self.drag_start_position = None

    def listen_download_state(self):
        if self.upgrade_finished:
            self.timer.stop()
            cf.set('upgrade', 'state', '0')
            cf.set('upgrade', 'currentVersion', new_version)
            cf.write(open('service.ini', 'w'))
            if self.total_file_count > 0:
                os.startfile(main_file)
            time.sleep(0.2)
            self.close()

    def updateLabel(self, c, t, overwrite=False):
        self.label.setText('正在升级...(' + c + '/' + t + ')') if not overwrite else self.label.setText(t)

    def download_file(self):
        upgrade_files = []
        self.finished_count = 0
        self.total_file_count = 0
        self.weight_file = 0
        self.extra_file = 0
        response = requests.get(upgrade_url + 'upgrade/v1/info?username=' + username, timeout=2)
        if response.status_code == 200:
            res = response.json()
            if res['code'] == 0 and res['data'] is not None:
                data = res['data']
                self.weight_file_tag = data['weight_file']
                self.extra_file_tag = data['extra_file']
                self.total_file_count = data['total_file_count']
                upgrade_files = (data['extra_path'].replace('[', '').replace(']', '').replace(' ', '').split(','))

        if self.total_file_count == 0:
            self.updateLabel('0', '当前已是最新版本!', True)
            time.sleep(1)
            self.upgrade_finished = True
            return

        if self.weight_file_tag == 1:
            self.total_file_count += len(weight_files)
        self.updateLabel('0', str(self.total_file_count))
        time.sleep(0.2)
        # 下载主程序
        try:
            response = requests.get(upgrade_url + 'upgrade/v1/download?filename=' + main_file + '&username=' + username,
                                    stream=True)
            end_process(main_file)
            time.sleep(0.5)
            end_process(main_file)
            time.sleep(0.5)

            self.pbDownload.setMaximum(int(response.headers.get('Content-Length', 0)))

            with open(main_file_tmp, 'wb') as file:
                self.finished_chunk_size = 0
                for chunk in response.iter_content(chunk_size=8192 * 10):
                    if chunk:
                        file.write(chunk)
                        self.finished_chunk_size += len(chunk)
                        self.pbDownload.setValue(self.finished_chunk_size)
                        QApplication.processEvents()

            time.sleep(0.5)
            # 下载完成,删除main_file,将临时文件改成main_file
            os.remove(main_file)
            os.rename(main_file_tmp, main_file)
        except:
            # 下载失败,删除临时文件
            os.remove(main_file_tmp)
            self.updateLabel('0', '升级失败!请检查网络连接!', True)
            time.sleep(1)
            self.upgrade_finished = True
            return

        self.updateLabel('1', str(self.total_file_count))

        time.sleep(0.2)
        self.finished_count = 1
        if self.weight_file_tag == 1:
            for weight_file in weight_files:
                weight_file_tmp = weight_file + '.tmp'
                try:
                    # 下载权重文件
                    response = requests.get(
                        upgrade_url + 'upgrade/v1/download?filename=' + weight_file + '&username=' + username,
                        stream=True)
                    self.pbDownload.setMaximum(int(response.headers.get('Content-Length', 0)))

                    with open('weights/' + weight_file_tmp, 'wb') as file:
                        self.finished_chunk_size = 0
                        for chunk in response.iter_content(chunk_size=8192 * 10):
                            if chunk:
                                file.write(chunk)
                                self.finished_chunk_size += len(chunk)
                                self.pbDownload.setValue(self.finished_chunk_size)
                                QApplication.processEvents()

                    time.sleep(0.5)
                    # 下载完成,删除weight_file,将临时文件改成weight_file
                    os.remove('weights/' + weight_file)
                    os.rename('weights/' + weight_file_tmp, 'weights/' + weight_file)
                    self.finished_count += 1
                except:
                    # 下载失败,删除临时文件
                    os.remove('weights/' + weight_file_tmp)
                    self.updateLabel('0', '升级失败!请检查网络连接!', True)
                    time.sleep(1)
                    self.upgrade_finished = True
                    return

                self.updateLabel(str(self.finished_count), str(self.total_file_count))

        # 最后下载第三方文件(zip格式)
        if self.extra_file_tag == 1:
            try:
                for idx, filename in enumerate(upgrade_files):
                    response = requests.get(
                        upgrade_url + 'upgrade/v1/download?filename=' + filename + '&username=' + username, stream=True)

                    extra_file_zip = '_internal\\' + filename
                    self.pbDownload.setMaximum(int(response.headers.get('Content-Length', 0)))
                    with open(extra_file_zip, 'wb') as file:
                        self.finished_chunk_size = 0
                        for chunk in response.iter_content(chunk_size=8192):
                            if chunk:
                                file.write(chunk)
                                self.finished_chunk_size += len(chunk)
                                self.pbDownload.setValue(self.finished_chunk_size)
                                QApplication.processEvents()

                    # 解压缩zip
                    unzip_file(extra_file_zip, './_internal')
                    self.updateLabel(str(idx + self.finished_count + 1), str(self.total_file_count))
            except:
                self.updateLabel('0', '升级失败!请检查网络连接!', True)
                time.sleep(1)
                self.upgrade_finished = True
                return

        self.updateLabel('0', '升级完成!启动新版本...', True)
        time.sleep(0.5)
        self.upgrade_finished = True

    def close(self):
        super(MainWindow, self).close()


def end_process(process_name):
    try:
        if is_process_running_by_name(process_name):
            os.system('taskkill /f /im ' + process_name)
    except:
        pass


def is_process_running_by_name(process_name):
    for proc in psutil.process_iter(['name']):
        if proc.info['name'].find(process_name) == 0:
            return True
    return False


def unzip_file(zip_path, extract_path='.'):
    if not os.path.exists(extract_path):
        os.makedirs(extract_path)

    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        zip_ref.extractall(extract_path)

    os.remove(zip_path)


extra = {
    'font_family': '微软雅黑',
    'density_scale': '0',
    'button_shape': 'default',
    'pyside6': True
}

if __name__ == '__main__':
    cf = configparser.ConfigParser()
    cf_file = 'service.ini'
    if os.path.exists(cf_file):
        cf.read(cf_file)
        upgrade_url = cf.get('server', 'url')
        main_file = cf.get('upgrade', 'mainfile')
        weight_files = cf.get('upgrade', 'weightfiles').split(',')
        main_file_tmp = main_file + '.tmp'
        current_version = cf.get('upgrade', 'currentversion')
        new_version = cf.get('upgrade', 'newversion')
        state = cf.getint('upgrade', 'state')
        username = cf.get('sys', 'username')
    else:
        sys.exit(0)

    if state == 0 or (current_version == new_version):
        sys.exit(0)

    app = QApplication(sys.argv)
    main_window = MainWindow()
    apply_stylesheet(app, theme='dark_teal2.xml', extra=extra)
    main_window.show()
    sys.exit(app.exec())

配置文件 service.ini

后台服务(Java)

    @ApiOperation("下载升级文件")
    @RequestMapping("download")
    public void download(@RequestParam String filename, @RequestParam String username) {
        UpgradeSetDO upgradeSetDO = upgradeSetMapper.selectOne(
                new QueryWrapper<UpgradeSetDO>().eq("username", username).eq("state", 1)
                        .last(" order by id desc limit 1"));

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;
        HttpServletResponse response = requestAttributes.getResponse();
        assert response != null;

        String filePath = "C:\\IMVD\\UPGRADE\\" + username + "\\" + upgradeSetDO.getVersion() + "\\" + filename;
        File file = new File(filePath);
        response.setHeader("Content-Disposition", "attachment;filename=" + filename);
        response.setHeader("Content-Length", String.valueOf(file.length()));
        try (OutputStream outputStream = response.getOutputStream();
             BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath))) {
            byte[] buff = new byte[1024];
            int i = bis.read(buff);
            while (i != -1) {
                outputStream.write(buff, 0, buff.length);
                outputStream.flush();
                i = bis.read(buff);
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

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

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

相关文章

开源 - Ideal库 - Excel帮助类,ExcelHelper实现(四)

书接上回&#xff0c;前面章节已经实现Excel帮助类的第一步TableHeper的对象集合与DataTable相互转换功能&#xff0c;今天实现进入其第二步的核心功能ExcelHelper实现。 01、接口设计 下面我们根据第一章中讲解的核心设计思路&#xff0c;先进行接口设计&#xff0c;确定Exce…

嵌入式系统应用-LVGL的应用-平衡球游戏 part1

平衡球游戏 part1 1 平衡球游戏的界面设计2 界面设计2.1 背景设计2.2 球的设计2.3 移动球的坐标2.4 用鼠标移动这个球2.5 增加边框规则2.6 效果图2.7 游戏失败重启游戏 3 为小球增加增加动画效果3.1 增加移动效果代码3.2 具体效果图片 平衡球游戏 part2 第二部分文章在这里 1 …

《Python基础》之Pandas库

目录 一、简介 二、Pandas的核心数据结构 1、Series 2、DataFrame 三、数据读取与写入 1、数据读取 2、数据写入 四、数据清洗与处理 1、处理缺失值 2、处理重复值 3、数据转换 五、数据分析与可视化 1、统计描述 2、分组聚合 3、数据可视化 六、高级技巧 1、时…

网络安全-夜神模拟器如何通过虚拟机的Burp Suite代理应用程序接口

第一步、查看虚拟机的IP地址 我们可以通过ifconfig命令来查看虚拟机的IP地址,如下图所示。 第二步、在Burp Suite上设置代理 打开虚拟机上的Burp Suite,进入到代理模块中,进入到代理设置中心 打开系统代理设置中心之后,将我们虚拟机的地址添加到上面,作为新的代理。 第…

PyTorch 2.5.1: Bugs修复版发布

一&#xff0c;前言 在深度学习框架的不断迭代中&#xff0c;PyTorch 社区始终致力于提供更稳定、更高效的工具。最近&#xff0c;PyTorch 2.5.1 版本正式发布&#xff0c;这个版本主要针对 2.5.0 中发现的问题进行了修复&#xff0c;以提升用户体验。 二&#xff0c;PyTorch 2…

SpringAi整合大模型(进阶版)

进阶版是在基础的对话版之上进行新增功能。 如果还没弄出基础版的&#xff0c;请参考 https://blog.csdn.net/weixin_54925172/article/details/144143523?sharetypeblogdetail&sharerId144143523&sharereferPC&sharesourceweixin_54925172&spm1011.2480.30…

Python实现网站资源批量下载【可转成exe程序运行】

Python实现网站资源批量下载【可转成exe程序运行】 背景介绍解决方案转为exe可执行程序简单点说详细了解下 声明 背景介绍 发现 宣讲家网 的PPT很好&#xff0c;作为学习资料使用很有价值&#xff0c;所以想下载网站的PPT课件到本地&#xff0c;但是由于网站限制&#xff0c;一…

CSS函数

目录 一、背景 二、函数的概念 1. var()函数 2、calc()函数 三、总结 一、背景 今天我们就来说一说&#xff0c;常用的两个css自定义属性&#xff0c;也称为css函数。本文中就成为css函数。先来看一下官方对其的定义。 自定义属性&#xff08;有时候也被称作CSS 变量或者级…

UG NX二次开发(C#)-选择对象居中(不是全部居中)

文章目录 1、前言2、什么是对象居中3、功能实现代码3.1 对象居中3.1 恢复原视图1、前言 在UG NX二次开发过程中,我们经常会用到居中以查看完整的模型,但是对于如果想展示某些对象,而不是全部模型时,那么我们就想将选择的对象(如体对象)居中查看,当查看结束后还能恢复到…

动态规划-----路径问题

动态规划-----路径问题 下降最小路径和1&#xff1a;状态表示2&#xff1a;状态转移方程3 初始化4 填表顺序5 返回值6 代码实现 总结&#xff1a; 下降最小路径和 1&#xff1a;状态表示 假设&#xff1a;用dp[i][j]表示&#xff1a;到达[i,j]的最小路径 2&#xff1a;状态转…

[C++设计模式] 为什么需要设计模式?

文章目录 什么是设计模式&#xff1f;为什么需要设计模式&#xff1f;GOF 设计模式再次理解面向对象软件设计固有的复杂性软件设计复杂性的根本原因如何解决复杂性&#xff1f;分解抽象 结构化 VS 面向对象(封装)结构化设计代码示例&#xff1a;面向对象设计代码示例&#xff1…

级联树结构TreeSelect和上级反查

接口返回结构 前端展示格式 前端组件 <template><div ><el-scrollbar height"70vh"><el-tree :data"deptOptions" :props"{ label: label, children: children }" :expand-on-click-node"false":filter-node-me…

Figma入门-自动布局

Figma入门-自动布局 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&#xff0c;对…

【Unity基础】使用InputSystem实现物体跳跃

要在Unity中使用 InputSystem 实现小球按空格键跳起的效果&#xff0c;可以按照以下步骤进行&#xff1a; 1. 安装 InputSystem 包 首先&#xff0c;确保你已经安装了 Input System 包。你可以通过以下步骤安装&#xff1a; 打开 Unity 编辑器&#xff0c;点击菜单 Window -…

【ArkTS】使用AVRecorder录制音频 --内附录音机开发详细代码

系列文章目录 【ArkTS】关于ForEach的第三个参数键值 【ArkTS】“一篇带你读懂ForEach和LazyForEach” 【小白拓展】 【ArkTS】“一篇带你掌握TaskPool与Worker两种多线程并发方案” 【ArkTS】 一篇带你掌握“语音转文字技术” --内附详细代码 【ArkTS】技能提高–“用户授权”…

一种多功能调试工具设计方案开源

一种多功能调试工具设计方案开源 设计初衷设计方案具体实现HUB芯片采用沁恒微CH339W。TF卡功能网口功能SPI功能IIC功能JTAG功能下行USB接口 安路FPGA烧录器功能Xilinx FPGA烧录器功能Jlink OB功能串口功能RS232串口RS485和RS422串口自适应接口 CAN功能烧录器功能 目前进度后续计…

浏览器的事件循环机制

浏览器和Node的事件循环机制 引言浏览器的事件循环机制 引言 由于JS是单线程的脚本语言&#xff0c;所以在同一时间只能做一件事情&#xff0c;当遇到多个任务时&#xff0c;我们不可能一直等待任务完成&#xff0c;这会造成巨大的资源浪费。为了协调时间&#xff0c;用户交互…

Zabbix添加防火墙温度监控值实战

我们在Zabbix监控系统会监控诸如Server、network device、application等实例&#xff0c;通常我们在监控某个具体产品时&#xff0c;我们会找到具体的监控模板&#xff0c;在设备添加到平台以后&#xff0c;将模板链接到该设备&#xff0c;但很多时候我们企业内部的设备是没有标…

【k8s】创建基于sa的token的kubeconfig

需求 创建一个基于sa的token的kubeconfig文件&#xff0c;并用这个文件来访问集群。 具体创建sa 和sa的token请参考文章: 【k8s】给ServiceAccount 创建关联的 Secrets-CSDN博客 创建sa apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata:namespace: jtkjdevnam…

Fastapi + vue3 自动化测试平台---移动端App自动化篇

概述 好久写文章了&#xff0c;专注于新框架&#xff0c;新UI界面的实践&#xff0c;废话不多说&#xff0c;开搞 技术架构 后端&#xff1a; Fastapi Airtest multiprocessing 前端&#xff1a; 基于 Vue3、Vite、TypeScript、Pinia、Pinia持久化插件、Unocss 和 Elemen…