当前位置: 首页 > 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} \n  {output_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}"if widget_class in ["QMainWindow", "QtWidgets.QMainWindow"]:widget_setupUi = "\n"else: widget_setupUi ="        self.setupUi(self)\n"# 构造新类定义new_class_code = (f"class {new_class_name}({widget_class}):\n"f"    def __init__(self, parent=None):\n"f"        super().__init__(parent)\n"f"{widget_setupUi}")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"{widget_setupUi}")# 读取文件内容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中添加自动生成

 

http://www.xdnf.cn/news/891793.html

相关文章:

  • 三维GIS开发cesium智慧地铁教程(3)entity实体
  • 岩石三轴试验机
  • Spring Boot-面试题(52)
  • 每日算法刷题Day23 6.5:leetcode二分答案3道题,用时1h40min(有点慢)
  • JS深入学习 — 循环、函数、数组、字符串、Date对象,Math对象
  • 前端面试四之Fetch API同步和异步
  • c++算法学习3——深度优先搜索
  • 【java面试】框架篇
  • snprintf函数用法及注意事项详解
  • Redisson简明教程—你家的锁芯该换了
  • 71 LV信息查看
  • DeepSeek私有化部署的理性抉择:谁需要?谁不必?
  • SSH 和 Telnet 介绍、区别与使用方法
  • JAVA-springboot JUnit单元测试
  • Qt实现一个悬浮工具箱源码分享
  • LeetCode_LCR 509 斐波拉契
  • 经济学顶刊QJE:构建从非结构化文本数据中挖掘经济规律的新框架!
  • 【QT】qtdesigner中将控件提升为自定义控件后,css设置样式不生效(已解决,图文详情)
  • 实测报告:设备 AI 知识库如何帮助新手快速掌握巡检技巧?
  • 在嵌入式中C语言中static修饰的变量常量和字符串常量存储位置
  • 总结vxe-grid的一些用法
  • 精度分析方法-不确定度
  • [蓝桥杯]三体攻击
  • MySQL的并发事务问题及事务隔离级别
  • 12V降5V12A大功率WD5030A,充电器、便携式设备、网络及工业领域的理想选择
  • 大语言模型评测体系全解析(中篇):专项能力评测与行业垂直场景
  • Mysql莫名奇妙重启
  • 实现单例模式的常见方式
  • Redis Set集合命令、内部编码及应用场景(详细)
  • GC1809:高性能音频接收与转换芯片