当前位置: 首页 > news >正文

qt ui 转python

系统 没有 pyside6-uic,只能自己搞一个; 同时将 pyside  和 pyqt 合并到代码\bin\pyside-uic.py里;

用法: python  pyside-uic.py  [PyQt6/PyQt5/PySide6/PySide2] <输入路径> [-o <输出路径>]  [--skip/--force]

可以根据自己的要求选择转为PyQt6 、PyQt5、PySide6、PySide2的python代码;

--skip:检测文件对应输出的python代码是否已生成,每个生成输出的py文件都包含ui文件的MD5,MD5用于判断ui文件是否有修改,如果没有修改就跳过;

--force:不检查ui文件是否有修改,强制将ui文件转换生成对应的py文件;

\bin\pyside6-uic

#!/bin/bash
#pyside-uic "PySide6" "$@"
exec /ucrt64/bin/python.exe /ucrt64/bin/pyside-uic.py "PySide6" "$@"

\bin\pyside2-uic

#!/bin/bash
# uic.exe -g python
#pyside-uic "PySide2" "$@"
exec /ucrt64/bin/python.exe /ucrt64/bin/pyside-uic.py "PySide2" "$@"

\bin\pyuic6

#!/bin/sh
#exec /ucrt64/bin/python.exe -m PyQt6.uic.pyuic ${1+"$@"}
#pyuic PyQt6 ${1+"$@"}
exec /ucrt64/bin/python.exe /ucrt64/bin/pyside-uic.py "PyQt6" "$@"

\bin\pyuic5

#!/bin/sh
#exec /ucrt64/bin/python.exe -m PyQt5.uic.pyuic ${1+"$@"}
#pyuic PyQt5 ${1+"$@"}
exec /ucrt64/bin/python.exe /ucrt64/bin/pyside-uic.py "PyQt5" "$@"

\bin\pyside-uic.py

#/bin/pyside-uic.pyimport sys
import os
import hashlib
import subprocess
import re# 颜色定义(使用ANSI转义码)
COLOR_RESET = '\033[0m'
COLOR_BLACK = '\033[30m'
COLOR_RED = '\033[31m'
COLOR_GREEN = '\033[32m'
COLOR_YELLOW = '\033[33m'
COLOR_BLUE = '\033[34m'
BG_BLACK = '\033[40m'
BG_RED = '\033[41m'
BG_GREEN = '\033[42m'
BG_YELLOW = '\033[43m'
BG_BLUE = '\033[44m'# 全局选项控制
global_force = False
global_skip = Falsedef display(color, message):"""带颜色的输出函数"""print(f"{color}{message}{COLOR_RESET}")def get_md5(file_path):"""计算文件MD5值"""md5_hash = hashlib.md5()with open(file_path, "rb") as f:for byte_block in iter(lambda: f.read(4096), b""):md5_hash.update(byte_block)return md5_hash.hexdigest()def parse_widget_info(ui_file):"""解析UI文件获取类信息"""class_value = ""widget_class = ""widget_name = ""with open(ui_file, "r", encoding="utf-8") as f:content = f.read()# 提取class元素值class_match = re.search(r'<class>(.*?)</class>', content)if class_match:class_value = class_match.group(1)# 提取第一个widget的class和name属性widget_match = re.search(r'<widget[^>]+class="([^"]+)"[^>]+name="([^"]+)"', content)if widget_match:widget_class = widget_match.group(1)widget_name = widget_match.group(2)return (class_value, widget_class)def convert_ui_to_python(pyside_version, input_file, output_file):"""调用uic.exe执行转换"""try:if pyside_version == "PyQt6":subprocess.run(["python.exe", "-m", "PyQt6.uic.pyuic", input_file, "-o", output_file],check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)elif pyside_version == "PyQt5":subprocess.run(["python.exe", "-m", "PyQt5.uic.pyuic", input_file, "-o", output_file],check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)else:	subprocess.run(["uic.exe", "-g", "python", input_file, "-o", output_file],check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)			display(f"{BG_GREEN}{COLOR_BLACK}", f"成功编译 {input_file}")return Trueexcept subprocess.CalledProcessError:display(f"{BG_RED}{COLOR_BLACK}", f"编译失败:{input_file}")return Falsedef modify_python_file(output_file, input_file, md5, class_info, pyside_version, first_charer="Ui_"):"""修改生成的Python文件(新增input_file参数)"""class_name, widget_class = class_infobase_name = os.path.splitext(os.path.basename(output_file))[0]new_class_name = f"{base_name}"# 构造新类定义new_class_code = (f"class {new_class_name}({widget_class}):\n"f"    def __init__(self, parent=None):\n"f"        super().__init__(parent)\n"f"        self.setupUi(self)\n")if pyside_version == "PyQt6" or pyside_version == "PyQt5":new_class_code = (f"class {new_class_name}(QtWidgets.{widget_class}):\n"f"    def __init__(self, parent=None):\n"f"        super().__init__(parent)\n"f"        self.setupUi(self)\n")# 读取文件内容with open(output_file, "r", encoding="utf-8") as f:content = f.read()# 替换类定义(匹配原class定义模式)content = re.sub(r'class \w+\(object\):', new_class_code, content, count=1)# 添加MD5和源文件注释(使用传入的input_file)content = f"# md5={md5}\n# src={os.path.abspath(input_file)}\n" + content# 替换PySide版本(根据目标版本)if pyside_version == "PyQt6":content = content.replace("PyQt5", "PyQt6")elif pyside_version == "PySide6":content = content.replace("PySide2", "PySide6")# 写入修改后的内容with open(output_file, "w", encoding="utf-8") as f:f.write(content)def prompt_user_action(file_path):"""提示用户选择操作"""global global_force, global_skip# 如果已有全局选项,直接返回if global_force:return 'force'if global_skip:return 'skip'while True:display(f"{BG_YELLOW}{COLOR_BLACK}", f"文件已存在:{file_path}")choice = input("请选择操作 [r]重新处理 [s]跳过 [a]全部重新处理 [q]全部跳过 [e]退出: ").lower()if choice == 'r':return 'force'elif choice == 's':return 'skip'elif choice == 'a':global_force = Truereturn 'force'elif choice == 'q':global_skip = Truereturn 'skip'elif choice == 'e':display(f"{BG_BLUE}{COLOR_YELLOW}", "操作已取消")sys.exit(0)else:display(f"{BG_RED}{COLOR_BLACK}", "无效选择,请重试")def process_file(pyside_version, input_file, output_dir, first_charer):"""处理单个UI文件"""input_basename = os.path.splitext(os.path.basename(input_file))[0]output_file = os.path.join(output_dir, f"{first_charer}{input_basename}.py")# 检查文件是否已存在且MD5匹配if os.path.exists(output_file):current_md5 = get_md5(input_file)with open(output_file, "r", encoding="utf-8") as f:if f.readline().strip() == f"# md5={current_md5}":action = prompt_user_action(input_file)if action == 'skip':display(f"{BG_YELLOW}{COLOR_BLACK}", f"跳过已处理文件:{input_file}")returnelif action == 'force':display(f"{BG_YELLOW}{COLOR_BLACK}", f"重新处理文件:{input_file}")# 执行编译if not convert_ui_to_python(pyside_version, input_file, output_file):return# 解析UI信息class_info = parse_widget_info(input_file)if not class_info[1]:display(f"{BG_RED}{COLOR_BLACK}", f"解析失败:{input_file} 中未找到widget信息")return# 计算MD5md5 = get_md5(input_file)# 修改生成的Python文件(新增input_file参数传递)modify_python_file(output_file, input_file, md5, class_info, pyside_version, first_charer)def process_directory(pyside_version, input_dir, output_dir, first_charer):"""处理目录下的所有UI文件"""for root, _, files in os.walk(input_dir):for file in files:if file.endswith(".ui"):input_path = os.path.join(root, file)rel_path = os.path.relpath(root, input_dir)output_subdir = os.path.join(output_dir, rel_path)os.makedirs(output_subdir, exist_ok=True)process_file(pyside_version, input_path, output_subdir, first_charer)def main():global global_force, global_skipfirst_charer = "Ui_"pyside_version = "PyQt6"input_path = ""output_path = ""# 参数解析if len(sys.argv) < 2:display(f"{BG_BLUE}{COLOR_YELLOW}", "用法: python ui_converter.py [PyQt6/PyQt5/PySide6/PySide2] <输入路径> [-o <输出路径>]")return# 检查是否有全局选项if '--force' in sys.argv:global_force = Truesys.argv.remove('--force')elif '--skip' in sys.argv:global_skip = Truesys.argv.remove('--skip')# 确定PySide版本version_arg = sys.argv[1].lower()if version_arg in ["pyqt6"]:pyside_version = "PyQt6"args = sys.argv[2:]elif version_arg in ["pyqt5"]:pyside_version = "PyQt5"args = sys.argv[2:]elif version_arg in ["pyside6"]:pyside_version = "PySide6"args = sys.argv[2:]elif version_arg in ["pyside2"]:pyside_version = "PySide2"args = sys.argv[2:]else:display(f"{BG_RED}{COLOR_BLACK}", "错误:无效的PySide版本")return# 处理输入输出路径input_path = args[0] if args else ""output_path = None# 处理-o选项if "-o" in args:idx = args.index("-o")if idx + 1 < len(args):output_path = args[idx+1]else:display(f"{BG_RED}{COLOR_BLACK}", "错误:-o选项需要指定输出路径")returninput_path = args[:idx][0] if args[:idx] else ""else:output_path = args[1] if len(args) > 1 else None# 处理默认输出路径if not output_path:if os.path.isfile(input_path):output_path = os.path.dirname(input_path)else:output_path = input_path# 检查输入路径有效性if not os.path.exists(input_path):display(f"{BG_RED}{COLOR_BLACK}", f"错误:路径不存在 - {input_path}")return# 处理文件或目录if os.path.isfile(input_path) and input_path.endswith(".ui"):os.makedirs(os.path.dirname(output_path), exist_ok=True)process_file(pyside_version, input_path, os.path.dirname(output_path), first_charer)elif os.path.isdir(input_path):os.makedirs(output_path, exist_ok=True)process_directory(pyside_version, input_path, output_path, first_charer)else:display(f"{BG_RED}{COLOR_BLACK}", "错误:输入必须是UI文件或目录")if __name__ == "__main__":main()

在QCerator中添加自动生成

 

相关文章:

  • 导航路径优化(一)——平滑
  • PX4 | 无人机关闭磁力计罗盘飞行(yaw estimate error报错解决方法)
  • Vue事件总线
  • windows命令行面板升级Git版本
  • 面试总结一
  • 【HarmonyOS 5】 社交行业详解以及 开发案例
  • Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(七):消息框交互功能添加
  • 第二章支线八 ·CSS终式:Tailwind与原子风暴
  • 一个基于Java的简单抢单功能实现示例,模拟多线程环境下的并发抢单场景
  • c#基础010(程序结构)
  • JavaSec-XSS
  • Mysql 身份认证绕过漏洞 CVE-2012-2122
  • OpenResty 安装指南
  • DNS攻击类型有哪些?如何应对DNS攻击威胁?
  • 12.MySQL视图特性
  • 高敏感应用如何保护自身不被逆向?iOS 安全加固策略与工具组合实战(含 Ipa Guard 等)
  • 无法下载CUDA,下载界面链接打开异常
  • Linux网络——socket网络通信udp
  • 13.4 AI颠覆语言学习:预录制视频+GPT-4评估如何实现60%成本降低与40%留存飙升
  • JSON Web Token (JWT) 详解:由来、原理与应用实践
  • 南宁网站建设加q.479185700/国内5大搜索引擎
  • wordpress搬站/抖音引流推广怎么做
  • 建旅游网站的意义/seo免费诊断联系方式
  • 小学生网站制作/seo教程网
  • 微信网站域名/北京百度推广排名优化
  • 郑州网站建设hndream/广州企业网站seo