系统 没有 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.py
import 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 = False
def 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 True
except subprocess.CalledProcessError:
display(f"{BG_RED}{COLOR_BLACK}", f"编译失败:{input_file}")
return False
def modify_python_file(output_file, input_file, md5, class_info, pyside_version, first_charer="Ui_"):
"""修改生成的Python文件(新增input_file参数)"""
class_name, widget_class = class_info
base_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 = True
return 'force'
elif choice == 'q':
global_skip = True
return '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}")
return
elif 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
# 计算MD5
md5 = 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_skip
first_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 = True
sys.argv.remove('--force')
elif '--skip' in sys.argv:
global_skip = True
sys.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选项需要指定输出路径")
return
input_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中添加自动生成