diff --git a/.gitignore b/.gitignore index aa6cef21..efd048cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,57 @@ __pycache__/ *.pyc .idea/ +【模板】模型及算法与功能对应清单.docx +【模板】GY知识-产品模块架构及功能清单和主要应用场景说明.docx +1.json +2.json +3.json +4.json +6.json +功能清单.md +模型及算法与功能对应清单(完整版).md +维修系统使用说明.md +audio_demo_config.json +create_test_audio.py +demo_universal_dialog.py +Dock按钮样式统一说明.md +Dock面板标题样式线完成说明.md +exam_result_20250926_004454.json +exam_result_20250926_010628.json +exam_result_20250926_011746.json +exam_result_20250926_090330.json +exam_result_20250926_111756.json +exam_test_config.json +Figma布局优化说明.md +Figma设计分析与菜单栏优化文档.md +Figma样式优化完成说明.md +manual_tools_config.json +pm.txt +simple_menubar_test.py +test_audio.py +test_basic_audio.py +test_exam_tool_error.py +test_full_audio_playback.py +test_menu_visibility.py +test_mode_selection.py +test_mp3_audio.py +test_pygame_audio.py +test_score_config.py +test_simple_audio.py +tool_test_config.json +UI设计文档.md +UniversalMessageDialog使用说明.md +.codex/settings/kiroCodex-settings.json +data/projects.json +RenderPipelineFile/config/daytime.yaml +Resources/audio/README.md +Resources/model/a.glb +Resources/model/b.glb +Resources/model/c.glb +Resources/model/JQB_auto_converted.glb +Resources/model/Untitled.glb +Resources/models/Women_1.glb +Subjects/test_maintenance_config.json +Resources/models/Women_1.glb +Resources/models/Women_1.glb +Resources/models/Women_1.glb diff --git a/RenderPipelineFile/config/daytime.yaml b/RenderPipelineFile/config/daytime.yaml index 6bb60b02..030a6111 100644 --- a/RenderPipelineFile/config/daytime.yaml +++ b/RenderPipelineFile/config/daytime.yaml @@ -17,8 +17,8 @@ control_points: scattering: sun_intensity: [[[0.0000000000,0.0000000000],[0.0041666667,0.0000000000],[0.0083333333,0.0000000000],[0.0125000000,0.0000000000],[0.0166666667,0.0000000000],[0.0208333333,0.0000000000],[0.0250000000,0.0000000000],[0.0291666667,0.0000000000],[0.0333333333,0.0000000000],[0.0375000000,0.0000000000],[0.0416666667,0.0000000000],[0.0458333333,0.0000000000],[0.0500000000,0.0000000000],[0.0541666667,0.0000000000],[0.0583333333,0.0000000000],[0.0625000000,0.0000000000],[0.0666666667,0.0000000000],[0.0708333333,0.0000000000],[0.0750000000,0.0000000000],[0.0791666667,0.0000000000],[0.0833333333,0.0000000000],[0.0875000000,0.0000000000],[0.0916666667,0.0000000000],[0.0958333333,0.0000000000],[0.1000000000,0.0000000000],[0.1041666667,0.0000000000],[0.1083333333,0.0000000000],[0.1125000000,0.0000000000],[0.1166666667,0.0000000000],[0.1208333333,0.0000000000],[0.1250000000,0.0000000000],[0.1291666667,0.0000000000],[0.1333333333,0.0000000000],[0.1375000000,0.0000000000],[0.1416666667,0.0000000000],[0.1458333333,0.0000000000],[0.1500000000,0.0000000000],[0.1541666667,0.0000000000],[0.1583333333,0.0000028805],[0.1625000000,0.0003577724],[0.1666666667,0.0013331400],[0.1708333333,0.0029671803],[0.1750000000,0.0052963381],[0.1791666667,0.0083550556],[0.1833333333,0.0121755589],[0.1875000000,0.0167876159],[0.1916666667,0.0222183530],[0.1958333333,0.0284919947],[0.2000000000,0.0356297193],[0.2041666667,0.0436494349],[0.2083333333,0.0525656099],[0.2125000000,0.0623891610],[0.2166666667,0.0731272461],[0.2208333333,0.0847831708],[0.2250000000,0.0973563167],[0.2291666667,0.1108419698],[0.2333333333,0.1252313631],[0.2375000000,0.1405115250],[0.2416666667,0.1566653434],[0.2458333333,0.1736715009],[0.2500000000,0.1915046014],[0.2541666667,0.2101350464],[0.2583333333,0.2295292930],[0.2625000000,0.2496498145],[0.2666666667,0.2704552670],[0.2708333333,0.2919006662],[0.2750000000,0.3139375192],[0.2791666667,0.3365139497],[0.2833333333,0.3595750662],[0.2875000000,0.3830630359],[0.2916666667,0.4069173972],[0.2958333333,0.4310753462],[0.3000000000,0.4554720417],[0.3041666667,0.4800408236],[0.3083333333,0.5047136020],[0.3125000000,0.5294212108],[0.3166666667,0.5540936424],[0.3208333333,0.5786605298],[0.3250000000,0.6030514553],[0.3291666667,0.6271963182],[0.3333333333,0.6510256858],[0.3375000000,0.6744711982],[0.3416666667,0.6974659988],[0.3458333333,0.7199450163],[0.3500000000,0.7418453485],[0.3541666667,0.7631067095],[0.3583333333,0.7836717291],[0.3625000000,0.8034862953],[0.3666666667,0.8224999302],[0.3708333333,0.8406661079],[0.3750000000,0.8579425235],[0.3791666667,0.8742914270],[0.3833333333,0.8896799131],[0.3875000000,0.9040801386],[0.3916666667,0.9174695289],[0.3958333333,0.9298310650],[0.4000000000,0.9411533765],[0.4041666667,0.9514309312],[0.4083333333,0.9606641691],[0.4125000000,0.9688595571],[0.4166666667,0.9760296330],[0.4208333333,0.9821930708],[0.4250000000,0.9873746114],[0.4291666667,0.9916050060],[0.4333333333,0.9949209310],[0.4375000000,0.9973647924],[0.4416666667,0.9989845508],[0.4458333333,0.9998334497],[0.4500000000,0.9999696949],[0.4541666667,0.9994560801],[0.4583333333,0.9983595429],[0.4625000000,0.9967506613],[0.4666666667,0.9947030614],[0.4708333333,0.9922927758],[0.4750000000,0.9895975125],[0.4791666667,0.9866958610],[0.4833333333,0.9836664262],[0.4875000000,0.9805868867],[0.4916666667,0.9775330316],[0.4958333333,0.9745777179],[0.5000000000,0.9717898417],[0.5041666667,0.9692332877],[0.5083333333,0.9669658924],[0.5125000000,0.9650384806],[0.5089595376,0.9690650222],[0.5208333333,0.9623666659],[0.5250000000,0.9616814371],[0.5291666667,0.9614534423],[0.5333333333,0.9616877089],[0.5375000000,0.9623790807],[0.5416666667,0.9635123329],[0.5458333333,0.9650624244],[0.5500000000,0.9669949804],[0.5541666667,0.9692669864],[0.5583333333,0.9718275065],[0.5625000000,0.9746185969],[0.5666666667,0.9775762863],[0.5708333333,0.9806315864],[0.5750000000,0.9837115661],[0.5791666667,0.9867403433],[0.5833333333,0.9896401655],[0.5875000000,0.9923323562],[0.5916666667,0.9947382579],[0.5958333333,0.9967800977],[0.6000000000,0.9983817820],[0.6041666667,0.9994696263],[0.6083333333,0.9999730028],[0.6125000000,0.9998249266],[0.6166666667,0.9989625601],[0.6208333333,0.9973276624],[0.6250000000,0.9948669567],[0.6291666667,0.9915324664],[0.6333333333,0.9872817545],[0.6375000000,0.9820781426],[0.6416666667,0.9758908775],[0.6458333333,0.9686952146],[0.6500000000,0.9604725211],[0.6541666667,0.9512102537],[0.6583333333,0.9409019858],[0.6625000000,0.9295473441],[0.6666666667,0.9171518878],[0.6708333333,0.9037270619],[0.6750000000,0.8892899902],[0.6791666667,0.8738633008],[0.6833333333,0.8574749656],[0.6875000000,0.8401579787],[0.6916666667,0.8219502453],[0.6958333333,0.8028941798],[0.7000000000,0.7830364456],[0.7041666667,0.7624277344],[0.7083333333,0.7411222520],[0.7125000000,0.7191776044],[0.7166666667,0.6966542563],[0.7208333333,0.6736152714],[0.7250000000,0.6501259629],[0.7291666667,0.6262533880],[0.7333333333,0.6020661121],[0.7375000000,0.5776338043],[0.7416666667,0.5530267796],[0.7458333333,0.5283156992],[0.7500000000,0.5035711751],[0.7541666667,0.4788634341],[0.7583333333,0.4542618347],[0.7625000000,0.4298347613],[0.7666666667,0.4056490351],[0.7708333333,0.3817697830],[0.7750000000,0.3582600107],[0.7791666667,0.3351803495],[0.7833333333,0.3125888445],[0.7875000000,0.2905406366],[0.7916666667,0.2690876955],[0.7958333333,0.2482787388],[0.8000000000,0.2281588906],[0.8041666667,0.2087696425],[0.8083333333,0.1901486315],[0.8125000000,0.1723295359],[0.8166666667,0.1553419918],[0.8208333333,0.1392115328],[0.8250000000,0.1239595144],[0.8291666667,0.1096030703],[0.8333333333,0.0961551918],[0.8375000000,0.0836246599],[0.8416666667,0.0720161369],[0.8458333333,0.0613302273],[0.8500000000,0.0515635598],[0.8541666667,0.0427088803],[0.8583333333,0.0347551990],[0.8625000000,0.0276878920],[0.8666666667,0.0214889271],[0.8708333333,0.0161369711],[0.8750000000,0.0116076130],[0.8791666667,0.0078735477],[0.8833333333,0.0049047927],[0.8875000000,0.0026688977],[0.8916666667,0.0011311782],[0.8958333333,0.0002549473],[0.9000000000,0.0000000000],[0.9041666667,0.0000000000],[0.9083333333,0.0000000000],[0.9125000000,0.0000000000],[0.9166666667,0.0000000000],[0.9208333333,0.0000000000],[0.9250000000,0.0000000000],[0.9291666667,0.0000000000],[0.9333333333,0.0000000000],[0.9375000000,0.0000000000],[0.9416666667,0.0000000000],[0.9458333333,0.0000000000],[0.9500000000,0.0000000000],[0.9541666667,0.0000000000],[0.9583333333,0.0000000000],[0.9625000000,0.0000000000],[0.9666666667,0.0000000000],[0.9708333333,0.0000000000],[0.9750000000,0.0000000000],[0.9791666667,0.0000000000],[0.9833333333,0.0000000000],[0.9875000000,0.0000000000],[0.9916666667,0.0000000000],[0.9958333333,0.0000000000]]] sun_color: [[[0.5010435645,0.5818710306],[0.0433100000,0.8999700000],[0.8635787716,0.9130000000],[0.1785000000,0.8973600000],[0.8099800000,0.8651100000],[0.2360800000,0.7712700000],[0.6583432177,0.8485126184],[0.1266806142,0.9648102053],[0.9558541267,0.9090909091],[0.5568400771,0.7353760446]],[[0.5001318426,0.5160300000],[0.0572700000,0.6541600000],[0.2395000000,0.5976800000],[0.8104600000,0.6009000000],[0.6967400000,0.5483900000]],[[0.0862400000,0.4257800000],[0.4955600000,0.4033000000],[0.8234200000,0.4340200000]]] - sun_azimuth: [[[0.5000000000,0.4944444444]]] - sun_altitude: [[[0.5000000000,0.9666666667]]] + sun_azimuth: [[[0.5000000000,0.4972222222]]] + sun_altitude: [[[0.5000000000,0.9555555556]]] extinction: [[[0.4913294798,0.6378830084]]] volumetrics: fog_ramp_size: [[[0.5510597303,0.7409470752]]] diff --git a/Resources/a b/Resources/a new file mode 100644 index 00000000..e69de29b diff --git a/Resources/icons/heightmap.png b/Resources/icons/heightmap.png new file mode 100644 index 00000000..33b6c701 Binary files /dev/null and b/Resources/icons/heightmap.png differ diff --git a/icons/property_select_image.png b/icons/property_select_image.png new file mode 100644 index 00000000..04abe10a Binary files /dev/null and b/icons/property_select_image.png differ diff --git a/icons/success_icon.png b/icons/success_icon.png new file mode 100644 index 00000000..4dda99f9 Binary files /dev/null and b/icons/success_icon.png differ diff --git a/icons/warning_icon.png b/icons/warning_icon.png new file mode 100644 index 00000000..540f03b4 Binary files /dev/null and b/icons/warning_icon.png differ diff --git a/new/project.json b/new/project.json new file mode 100644 index 00000000..5d10691e --- /dev/null +++ b/new/project.json @@ -0,0 +1,8 @@ +{ + "name": "new", + "path": "D:\\PythonProject\\CH_EG\\EG\\new", + "created_at": "2025-09-09 16:47:27", + "last_modified": "2025-09-11 09:28:24", + "version": "1.0.0", + "engine_version": "1.0.0" +} \ No newline at end of file diff --git a/quick_menu_fix.py b/quick_menu_fix.py new file mode 100644 index 00000000..a0f822d6 --- /dev/null +++ b/quick_menu_fix.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +快速菜单栏修复脚本 +用于诊断和修复菜单栏显示问题 +""" + +import sys +import os +from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QPushButton +from PyQt5.QtCore import Qt, QTimer + +# 设置环境变量避免DPI问题 +os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" +os.environ["QT_SCALE_FACTOR"] = "1" + +class QuickMenuFix(QMainWindow): + def __init__(self): + super().__init__() + self.setupUI() + + def setupUI(self): + """设置UI""" + self.setWindowTitle("菜单栏快速修复工具") + self.setGeometry(300, 300, 900, 700) + + # 强制窗口标志 + self.setWindowFlags(Qt.Window) + + # 设置主窗口样式 + self.setStyleSheet(""" + QMainWindow { + background-color: #1E1E1E; + color: #FFFFFF; + } + QPushButton { + background-color: #0078D4; + color: white; + border: none; + padding: 10px 20px; + margin: 5px; + border-radius: 5px; + font-size: 12px; + } + QPushButton:hover { + background-color: #106EBE; + } + QPushButton:pressed { + background-color: #005A9E; + } + """) + + # 创建中央部件 + central_widget = QWidget() + self.setCentralWidget(central_widget) + layout = QVBoxLayout(central_widget) + + # 标题 + title = QLabel("🔧 菜单栏修复工具") + title.setStyleSheet("font-size: 24px; font-weight: bold; color: #0078D4; margin: 20px;") + title.setAlignment(Qt.AlignCenter) + layout.addWidget(title) + + # 状态显示 + self.status_label = QLabel("准备就绪...") + self.status_label.setStyleSheet("font-size: 14px; color: white; margin: 10px; padding: 10px; background-color: #2D2D30; border-radius: 5px;") + self.status_label.setAlignment(Qt.AlignCenter) + layout.addWidget(self.status_label) + + # 按钮区域 + button_layout = QVBoxLayout() + + # 测试按钮 + test_btn = QPushButton("🔍 测试菜单栏显示") + test_btn.clicked.connect(self.testMenuBar) + button_layout.addWidget(test_btn) + + # 修复按钮 + fix_btn = QPushButton("🛠️ 强制修复菜单栏") + fix_btn.clicked.connect(self.forceFixMenuBar) + button_layout.addWidget(fix_btn) + + # 重置按钮 + reset_btn = QPushButton("🔄 重置菜单栏样式") + reset_btn.clicked.connect(self.resetMenuBarStyle) + button_layout.addWidget(reset_btn) + + # 信息按钮 + info_btn = QPushButton("📊 显示系统信息") + info_btn.clicked.connect(self.showSystemInfo) + button_layout.addWidget(info_btn) + + layout.addLayout(button_layout) + + # 创建初始菜单栏 + self.createInitialMenuBar() + + def createInitialMenuBar(self): + """创建初始菜单栏""" + menubar = self.menuBar() + menubar.setNativeMenuBar(False) + + # 添加基本菜单 + file_menu = menubar.addMenu('文件') + file_menu.addAction('新建') + file_menu.addAction('打开') + + edit_menu = menubar.addMenu('编辑') + edit_menu.addAction('复制') + edit_menu.addAction('粘贴') + + help_menu = menubar.addMenu('帮助') + help_menu.addAction('关于') + + print("✅ 初始菜单栏已创建") + + def testMenuBar(self): + """测试菜单栏显示""" + menubar = self.menuBar() + + # 收集信息 + info = [] + info.append(f"可见性: {menubar.isVisible()}") + info.append(f"启用状态: {menubar.isEnabled()}") + info.append(f"高度: {menubar.height()}px") + info.append(f"宽度: {menubar.width()}px") + info.append(f"位置: {menubar.pos()}") + info.append(f"菜单数量: {len(menubar.actions())}") + info.append(f"原生菜单栏: {menubar.isNativeMenuBar()}") + + # 显示结果 + status_text = "📊 菜单栏状态:\n" + "\n".join(info) + self.status_label.setText(status_text) + + # 控制台输出 + print("\n🔍 菜单栏测试结果:") + for item in info: + print(f" - {item}") + + # 判断是否正常 + if menubar.isVisible() and menubar.height() > 0: + self.status_label.setStyleSheet("font-size: 14px; color: white; margin: 10px; padding: 10px; background-color: #0F7B0F; border-radius: 5px;") + print("✅ 菜单栏显示正常") + else: + self.status_label.setStyleSheet("font-size: 14px; color: white; margin: 10px; padding: 10px; background-color: #A80000; border-radius: 5px;") + print("❌ 菜单栏显示异常") + + def forceFixMenuBar(self): + """强制修复菜单栏""" + menubar = self.menuBar() + + print("🛠️ 开始强制修复菜单栏...") + + # 重置基本属性 + menubar.setNativeMenuBar(False) + menubar.setVisible(True) + menubar.setEnabled(True) + + # 设置尺寸 + menubar.setMinimumHeight(50) + menubar.setMaximumHeight(50) + + # 应用强制样式 + menubar.setStyleSheet(""" + QMenuBar { + background-color: #FF4444 !important; + color: #FFFFFF !important; + border: 4px solid #00FF00 !important; + min-height: 50px !important; + max-height: 50px !important; + font-size: 18px !important; + font-weight: bold !important; + padding: 8px !important; + } + QMenuBar::item { + background-color: #FFFF00 !important; + color: #000000 !important; + padding: 12px 20px !important; + margin: 4px !important; + font-size: 18px !important; + font-weight: bold !important; + border: 2px solid #FF0000 !important; + border-radius: 3px !important; + } + QMenuBar::item:selected { + background-color: #0066FF !important; + color: #FFFFFF !important; + } + """) + + # 强制显示和刷新 + menubar.show() + menubar.raise_() + menubar.update() + menubar.repaint() + self.update() + self.repaint() + + self.status_label.setText("🛠️ 强制修复已应用!\n如果看到红色菜单栏说明修复成功") + self.status_label.setStyleSheet("font-size: 14px; color: white; margin: 10px; padding: 10px; background-color: #FF6600; border-radius: 5px;") + + print("🛠️ 强制修复完成") + + # 延迟测试结果 + QTimer.singleShot(1000, self.testMenuBar) + + def resetMenuBarStyle(self): + """重置菜单栏样式""" + menubar = self.menuBar() + + # 清除样式 + menubar.setStyleSheet("") + + # 重新设置基本样式 + menubar.setStyleSheet(""" + QMenuBar { + background-color: #2D2D30; + color: #FFFFFF; + border-bottom: 1px solid #3E3E42; + min-height: 30px; + font-size: 14px; + } + QMenuBar::item { + background-color: transparent; + color: #FFFFFF; + padding: 8px 12px; + margin: 0px 2px; + } + QMenuBar::item:selected { + background-color: #0078D4; + } + """) + + menubar.update() + self.status_label.setText("🔄 菜单栏样式已重置为正常样式") + self.status_label.setStyleSheet("font-size: 14px; color: white; margin: 10px; padding: 10px; background-color: #2D2D30; border-radius: 5px;") + + print("🔄 菜单栏样式已重置") + + def showSystemInfo(self): + """显示系统信息""" + import platform + from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR + + info = [] + info.append(f"操作系统: {platform.system()} {platform.release()}") + info.append(f"Python版本: {sys.version.split()[0]}") + info.append(f"PyQt5版本: {PYQT_VERSION_STR}") + info.append(f"Qt版本: {QT_VERSION_STR}") + info.append(f"屏幕分辨率: {QApplication.desktop().screenGeometry().width()}x{QApplication.desktop().screenGeometry().height()}") + + status_text = "📊 系统信息:\n" + "\n".join(info) + self.status_label.setText(status_text) + self.status_label.setStyleSheet("font-size: 12px; color: white; margin: 10px; padding: 10px; background-color: #0078D4; border-radius: 5px;") + + print("\n📊 系统信息:") + for item in info: + print(f" - {item}") + +def main(): + """主函数""" + app = QApplication(sys.argv) + + # 设置应用样式 + app.setStyle('Fusion') + + print("🚀 启动菜单栏修复工具") + print("📋 这个工具可以帮助诊断和修复菜单栏显示问题") + print("🎯 使用明显的颜色来测试菜单栏是否正常显示") + + window = QuickMenuFix() + window.show() + + return app.exec_() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/scripts/a.py b/scripts/a.py new file mode 100644 index 00000000..34bd5408 --- /dev/null +++ b/scripts/a.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +a - 自定义脚本 +""" + +from core.script_system import ScriptBase + +class A(ScriptBase): + """自定义脚本类""" + + def __init__(self): + super().__init__() + # 在这里初始化您的变量 + + def start(self): + """脚本开始时调用""" + self.log("脚本开始运行!") + + def update(self, dt): + """每帧更新""" + # 在这里编写更新逻辑 + pass + + def on_destroy(self): + """脚本销毁时调用""" + self.log("脚本被销毁") diff --git a/ui/icon_manager.py b/ui/icon_manager.py index 8bfebceb..5d07611d 100644 --- a/ui/icon_manager.py +++ b/ui/icon_manager.py @@ -69,6 +69,9 @@ class IconManager: 'success_icon': 'success_icon.png', 'warning_icon': 'warning_icon.png', 'fail_icon': 'delete_fail_icon.png', + + # 属性面板图标 + 'property_select_image': 'property_select_image.png', } # 初始化默认图标 diff --git a/ui/main_window.py b/ui/main_window.py index 73e8c3bc..4c1b7fdc 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -1504,8 +1504,8 @@ class MainWindow(QMainWindow): padding: 8px 10px; border-bottom: none; text-align: left; - font-family: 'Microsoft YaHei', 'Inter', sans-serif; - font-size: 14px; + font-family: 'Inter', 'Microsoft YaHei', sans-serif; + font-size: 12px; font-weight: 500; } QDockWidget::close-button { @@ -1540,6 +1540,7 @@ class MainWindow(QMainWindow): background-color: #19191b; color: #ebebeb; border: none; + padding: 0px 0px 0px 8px; alternate-background-color: #19191b; font-family: 'Microsoft YaHei', 'Inter', sans-serif; font-size: 10px; @@ -1844,16 +1845,16 @@ class MainWindow(QMainWindow): /* 设置内边距,为标题和内容留出空间,左右内边距与主标题对齐 */ padding-top: 20px; /* 为标题留出足够空间 */ - padding-left: 8px; /* 左内边距与主标题分隔线对齐 */ - padding-right: 8px; /* 右内边距与主标题分隔线对齐 */ - + padding-left: 0px; /* Align subtitle with dock title baseline */ + padding-right: 0px; /* 右内边距与主标题分隔线对齐 */ + /* 减少下边距,使相邻组更紧凑 */ background-color: transparent; margin-bottom: 6px; /* 使用边框创建样式线,与主标题分隔线保持相同的8px左右间距 */ border-top: 1px solid rgba(77, 116, 189, 0.4); - margin-left: 8px; + margin-left: 0px; margin-right: 8px; } @@ -1863,7 +1864,7 @@ class MainWindow(QMainWindow): subcontrol-position: top left; /* 标题样式,左边距为0因为已经通过padding-left处理了对齐 */ - padding: 5px 3px 8px 0px; /* 左边距设为0,与内容对齐 */ + padding: 8px 3px 8px 0px; /* 左边距设为0,与内容对齐 */ margin-top: 0px; /* 标题紧贴内边距顶部 */ /* 移除标题上的边框 */ @@ -1875,6 +1876,13 @@ class MainWindow(QMainWindow): font-size: 12px; font-weight: 500; } + QGroupBox[groupRole="first"] { + padding-top: 16px; + margin-top: 0px; + } + QGroupBox[groupRole="first"]::title { + padding: 6px 3px 8px 0px; + } """) # 创建属性面板的主容器和布局 @@ -1883,7 +1891,9 @@ class MainWindow(QMainWindow): try: self.propertyLayout = QVBoxLayout(self.propertyContainer) - self.propertyLayout.setContentsMargins(0, 0, 0, 0) + # Keep subtitles aligned with the dock title left padding + self.propertyLayout.setContentsMargins(10, 0, 10, 12) + self.propertyLayout.setSpacing(12) print(f"✓ 属性布局创建完成: {self.propertyLayout}") # 添加初始提示信息 @@ -2032,7 +2042,7 @@ class MainWindow(QMainWindow): subcontrol-position: top left; /* 标题样式,左边距为0因为已经通过padding-left处理了对齐 */ - padding: 5px 3px 8px 0px; /* 左边距设为0,与内容对齐 */ + padding: 8px 3px 8px 0px; /* 左边距设为0,与内容对齐 */ margin-top: 0px; /* 标题紧贴内边距顶部 */ /* 移除标题上的边框 */ @@ -2044,6 +2054,13 @@ class MainWindow(QMainWindow): font-size: 12px; font-weight: 500; } + QGroupBox[groupRole="first"] { + padding-top: 16px; + margin-top: 0px; + } + QGroupBox[groupRole="first"]::title { + padding: 6px 3px 8px 0px; + } """) # 创建脚本面板的主容器和布局(与属性面板相同结构) @@ -2056,6 +2073,9 @@ class MainWindow(QMainWindow): """) self.scriptContainer.setObjectName("ScriptContainer") self.scriptLayout = QVBoxLayout(self.scriptContainer) + # Match the dock title horizontal padding for consistent alignment + self.scriptLayout.setContentsMargins(10, 0, 10, 12) + self.scriptLayout.setSpacing(12) # 创建滚动区域并设置属性 self.scriptScrollArea = QScrollArea() @@ -2215,6 +2235,14 @@ class MainWindow(QMainWindow): font-size: 12px; font-weight: 500; } + QGroupBox[groupRole="first"] { + border-top: none; + margin-top: 0px; + padding-top: 8px; + } + QGroupBox[groupRole="first"]::title { + padding: 8px 5px 8px 0px; + } """) # 创建包装容器,添加标题下方的分隔线 @@ -2238,7 +2266,14 @@ class MainWindow(QMainWindow): } """) bottomDockLayout.addWidget(bottomSeparator) - bottomDockLayout.addWidget(self.fileView) + + # Wrap the resource view to add consistent left padding + resourceContentWrapper = QWidget() + resourceContentLayout = QVBoxLayout(resourceContentWrapper) + resourceContentLayout.setContentsMargins(8, 0, 0, 0) + resourceContentLayout.setSpacing(0) + resourceContentLayout.addWidget(self.fileView) + bottomDockLayout.addWidget(resourceContentWrapper) self.bottomDock.setWidget(bottomDockContainer) self.addDockWidget(Qt.BottomDockWidgetArea, self.bottomDock) @@ -2514,6 +2549,8 @@ class MainWindow(QMainWindow): """创建脚本管理面板""" # 脚本状态组 statusGroup = QGroupBox("脚本系统状态") + + statusGroup.setProperty("groupRole", "first") statusLayout = QVBoxLayout() # 脚本系统状态行 diff --git a/ui/property_panel.py b/ui/property_panel.py index b815ee94..0ed44450 100644 --- a/ui/property_panel.py +++ b/ui/property_panel.py @@ -7,7 +7,7 @@ from typing import Hashable from PyQt5.QtGui import QColor from PyQt5.QtWidgets import (QLabel, QLineEdit, QDoubleSpinBox, QPushButton, QTreeWidget, QTreeWidgetItem, QMenu, QCheckBox, QComboBox, QHBoxLayout, QWidget, - QVBoxLayout, QGroupBox, QGridLayout, QSpinBox, QFileDialog, QMessageBox) + QVBoxLayout, QGroupBox, QGridLayout, QSpinBox, QFileDialog, QMessageBox, QSizePolicy) from PyQt5.QtCore import Qt from deploy_libs.unicodedata import normalize from direct.actor.Actor import Actor @@ -19,7 +19,7 @@ from panda3d.core import Vec3, Vec4, transpose, TransparencyAttrib, PartGroup, C from scene import util from direct.gui.DirectGui import DirectLabel, DirectFrame from panda3d.core import TextNode - +from ui.icon_manager import get_icon_manager, has_icon, get_icon class PropertyPanelManager: """属性面板管理器""" @@ -31,6 +31,8 @@ class PropertyPanelManager: self._actor_cache = {} self._spherical_video_controls = {} + self.column_minimum_width = 85 + # 初始化地形编辑参数 if not hasattr(self.world, 'terrain_edit_radius'): self.world.terrain_edit_radius = 3.0 @@ -174,12 +176,12 @@ class PropertyPanelManager: """ self.compact_style += """ QGroupBox { - margin-left: 8px; + margin-left: 0px; padding-left: 0px; } QGroupBox::title { left: 0px; - padding: 0 5px 5px 0; + padding: 8px 5px 5px 0; margin-top: 0px; } """ @@ -423,11 +425,41 @@ class PropertyPanelManager: } """) else: # default - # 使用全局样式,不需要额外设置 + # 使用全局默认样式 pass - return button - + + def _collision_button_style(self): + """返回碰撞检测面板按钮统一样式""" + return """ + QPushButton { + background-color: rgba(89, 98, 118, 0.4); + color: rgba(235, 235, 235, 0.85); + border: 1px solid rgba(89, 98, 118, 0.65); + border-radius: 4px; + padding: 8px 12px; + font-family: 'Microsoft YaHei', 'Inter', sans-serif; + font-size: 10px; + font-weight: 400; + min-height: 16px; + } + QPushButton:hover { + background-color: rgba(89, 98, 118, 0.55); + border: 1px solid rgba(89, 98, 118, 0.9); + color: #ffffff; + } + QPushButton:pressed { + background-color: rgba(89, 98, 118, 0.7); + border: 1px solid rgba(89, 98, 118, 0.95); + color: #ffffff; + } + QPushButton:disabled { + background-color: rgba(89, 98, 118, 0.18); + border: 1px solid rgba(89, 98, 118, 0.25); + color: rgba(235, 235, 235, 0.35); + } + """ + def createModernGroupBox(self, title, parent_layout): """创建现代化的分组框 @@ -458,7 +490,7 @@ class PropertyPanelManager: subcontrol-origin: padding; subcontrol-position: top left; left: 0px; - padding: 0 5px 5px 0; + padding: 8px 5px 5px 0; margin-top: 0px; } """) @@ -635,8 +667,7 @@ class PropertyPanelManager: model.setPythonTag("user_visible", True) self.name_group = QGroupBox("物体名称") - name_layout = QHBoxLayout() - name_layout.setContentsMargins(0, 8, 0, 8) + name_layout = QGridLayout() self.active_check = QCheckBox() # 根据模型的实际可见性状态设置复选框 self.active_check.setChecked(user_visible) @@ -646,8 +677,9 @@ class PropertyPanelManager: lambda: self.world.treeWidget.update_item_name(self.name_input.text(), item) ) - name_layout.addWidget(self.active_check) - name_layout.addWidget(self.name_input) + name_layout.addWidget(self.active_check, 0, 0) + name_layout.setColumnMinimumWidth(0, self.column_minimum_width) + name_layout.addWidget(self.name_input, 0, 1, 1, 3) self.name_group.setLayout(name_layout) self._propertyLayout.addWidget(self.name_group) @@ -718,19 +750,20 @@ class PropertyPanelManager: info_group = QGroupBox("地形信息") info_layout = QGridLayout() + info_layout.setColumnMinimumWidth(0, self.column_minimum_width) info_layout.addWidget(QLabel("名称:"), 0, 0) name_label = QLabel(terrain_info.get('name', '未知')) - info_layout.addWidget(name_label, 0, 1) + info_layout.addWidget(name_label, 0, 1, 1, 3) info_layout.addWidget(QLabel("类型:"), 1, 0) type_label = QLabel("高度图地形" if terrain_info.get('heightmap') else "平面地形") - info_layout.addWidget(type_label, 1, 1) + info_layout.addWidget(type_label, 1, 1, 1, 3) if terrain_info.get('heightmap'): info_layout.addWidget(QLabel("高度图:"), 2, 0) heightmap_label = QLabel(os.path.basename(terrain_info['heightmap'])) heightmap_label.setWordWrap(True) - info_layout.addWidget(heightmap_label, 2, 1) + info_layout.addWidget(heightmap_label, 2, 1, 1, 3) info_group.setLayout(info_layout) self._propertyLayout.addWidget(info_group) @@ -768,23 +801,17 @@ class PropertyPanelManager: try: transform_group = QGroupBox("变换 Transform") transform_layout = QGridLayout() + transform_layout.setColumnMinimumWidth(0, self.column_minimum_width) pos = terrain_node.getPos() scale = terrain_node.getScale() transform_layout.addWidget(QLabel("位置"), 0, 0) - x_label = QLabel("X") - y_label = QLabel("Y") - z_label = QLabel("Z") - x_label.setAlignment(Qt.AlignCenter) - y_label.setAlignment(Qt.AlignCenter) - z_label.setAlignment(Qt.AlignCenter) - - transform_layout.addWidget(x_label, 0, 1) - transform_layout.addWidget(y_label, 0, 2) - transform_layout.addWidget(z_label, 0, 3) - + position_row = QHBoxLayout() + position_row.setContentsMargins(0, 0, 0, 0) + position_row.setSpacing(4) + self.pos_x = QDoubleSpinBox() self.pos_y = QDoubleSpinBox() self.pos_z = QDoubleSpinBox() @@ -813,11 +840,20 @@ class PropertyPanelManager: self.pos_z.valueChanged.connect(updateZPosition) - transform_layout.addWidget(self.pos_x, 1, 1) - transform_layout.addWidget(self.pos_y, 1, 2) - transform_layout.addWidget(self.pos_z, 1, 3) + for axis_label, widget in (("X:", self.pos_x), ("Y:", self.pos_y), ("Z:", self.pos_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + position_row.addWidget(axis) + position_row.addWidget(widget) - transform_layout.addWidget(QLabel("旋转"), 2, 0) + transform_layout.addLayout(position_row, 0, 1, 1, 3) + + transform_layout.addWidget(QLabel("旋转"), 1, 0) + rotation_row = QHBoxLayout() + rotation_row.setContentsMargins(0, 0, 0, 0) + rotation_row.setSpacing(4) + self.rot_x = QDoubleSpinBox() self.rot_y = QDoubleSpinBox() self.rot_z = QDoubleSpinBox() @@ -833,21 +869,21 @@ class PropertyPanelManager: self.rot_y.valueChanged.connect(lambda v: terrain_node.setP(v)) self.rot_z.valueChanged.connect(lambda v: terrain_node.setR(v)) - h_label = QLabel("H") - p_label = QLabel("P") - r_label = QLabel("R") - h_label.setAlignment(Qt.AlignCenter) - p_label.setAlignment(Qt.AlignCenter) - r_label.setAlignment(Qt.AlignCenter) + for axis_label, widget in (("H:", self.rot_x), ("P:", self.rot_y), ("R:", self.rot_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + rotation_row.addWidget(axis) + rotation_row.addWidget(widget) - transform_layout.addWidget(h_label, 2, 1) - transform_layout.addWidget(p_label, 2, 2) - transform_layout.addWidget(r_label, 2, 3) - transform_layout.addWidget(self.rot_x, 3, 1) - transform_layout.addWidget(self.rot_y, 3, 2) - transform_layout.addWidget(self.rot_z, 3, 3) + transform_layout.addLayout(rotation_row, 1, 1, 1, 3) - transform_layout.addWidget(QLabel("缩放"), 4, 0) + transform_layout.addWidget(QLabel("缩放"), 2, 0) + + scale_row = QHBoxLayout() + scale_row.setContentsMargins(0, 0, 0, 0) + scale_row.setSpacing(4) + self.scale_x = QDoubleSpinBox() self.scale_y = QDoubleSpinBox() self.scale_z = QDoubleSpinBox() @@ -863,19 +899,14 @@ class PropertyPanelManager: self.scale_y.valueChanged.connect(lambda v: self._updateYScale(terrain_node, v)) self.scale_z.valueChanged.connect(lambda v: self._updateZScale(terrain_node, v)) - x_label2 = QLabel("X") - y_label2 = QLabel("Y") - z_label2 = QLabel("Z") - x_label2.setAlignment(Qt.AlignCenter) - y_label2.setAlignment(Qt.AlignCenter) - z_label2.setAlignment(Qt.AlignCenter) + for axis_label, widget in (("X:", self.scale_x), ("Y:", self.scale_y), ("Z:", self.scale_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + scale_row.addWidget(axis) + scale_row.addWidget(widget) - transform_layout.addWidget(x_label2, 4, 1) - transform_layout.addWidget(y_label2, 4, 2) - transform_layout.addWidget(z_label2, 4, 3) - transform_layout.addWidget(self.scale_x, 5, 1) - transform_layout.addWidget(self.scale_y, 5, 2) - transform_layout.addWidget(self.scale_z, 5, 3) + transform_layout.addLayout(scale_row, 2, 1, 1, 3) transform_group.setLayout(transform_layout) self._propertyLayout.addWidget(transform_group) @@ -886,6 +917,7 @@ class PropertyPanelManager: try: edit_group = QGroupBox("地形编辑") edit_layout = QGridLayout() + edit_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 编辑半径 edit_layout.addWidget(QLabel("编辑半径:"), 0, 0) @@ -894,7 +926,7 @@ class PropertyPanelManager: self.terrain_radius_spin.setSingleStep(0.5) self.terrain_radius_spin.setValue(getattr(self.world, 'terrain_edit_radius', 3.0)) self.terrain_radius_spin.valueChanged.connect(self._onTerrainRadiusChanged) - edit_layout.addWidget(self.terrain_radius_spin, 0, 1) + edit_layout.addWidget(self.terrain_radius_spin, 0, 1, 1, 3) # 编辑强度 edit_layout.addWidget(QLabel("编辑强度:"), 1, 0) @@ -903,7 +935,7 @@ class PropertyPanelManager: self.terrain_strength_spin.setSingleStep(0.1) self.terrain_strength_spin.setValue(getattr(self.world, 'terrain_edit_strength', 0.3)) self.terrain_strength_spin.valueChanged.connect(self._onTerrainStrengthChanged) - edit_layout.addWidget(self.terrain_strength_spin, 1, 1) + edit_layout.addWidget(self.terrain_strength_spin, 1, 1, 1, 3) edit_layout.addWidget(QLabel("操作类型:"), 2, 0) self.terrain_operation_combo = QComboBox() @@ -916,12 +948,12 @@ class PropertyPanelManager: else: self.terrain_operation_combo.setCurrentText("平坦化") self.terrain_operation_combo.currentTextChanged.connect(self._onTerrainOperationChanged) - edit_layout.addWidget(self.terrain_operation_combo, 2, 1) + edit_layout.addWidget(self.terrain_operation_combo, 2, 1, 1, 3) help_label = QLabel("在地形编辑模式下,使用鼠标左键升高地形,右键降低地形") help_label.setWordWrap(True) help_label.setStyleSheet("font-size:10px;color:gray;") - edit_layout.addWidget(help_label, 3, 0, 1, 2) + edit_layout.addWidget(help_label, 3, 0, 1, 4) edit_group.setLayout(edit_layout) self._propertyLayout.addWidget(edit_group) @@ -960,15 +992,17 @@ class PropertyPanelManager: try: material_group = QGroupBox("材质属性") material_layout = QGridLayout() + material_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 颜色设置 material_layout.addWidget(QLabel("颜色:"), 0, 0) color_button = QPushButton("选择颜色") color_button.clicked.connect(lambda: self._selectTerrainColor(terrain_info)) + material_layout.addWidget(color_button, 0, 1, 1, 3) # 纹理设置 material_layout.addWidget(QLabel("纹理:"), 1, 0) texture_button = QPushButton("选择纹理") texture_button.clicked.connect(lambda: self._selectTerrainTexture(terrain_info)) - material_layout.addWidget(texture_button, 1, 1) + material_layout.addWidget(texture_button, 1, 1, 1, 3) material_group.setLayout(material_layout) self._propertyLayout.addWidget(material_group) @@ -1408,22 +1442,33 @@ class PropertyPanelManager: print(f"显示 Cesium tileset 属性: {nodePath.getName()}") # 标题 - title = QLabel("Cesium 3D Tiles") - title.setStyleSheet("font-size: 14px; font-weight: bold; margin: 10px 0;") - self._propertyLayout.addWidget(title) + tileset_group = QGroupBox("Cesium 3D Tiles") + tileset_layout = QGridLayout() + tileset_layout.setColumnMinimumWidth(0, self.column_minimum_width) + # self._propertyLayout.addWidget(title) # URL 信息 if nodePath.hasTag("tileset_url"): url_label = QLabel("URL:") + tileset_layout.addWidget(url_label, 0, 0) url_value = QLabel(nodePath.getTag("tileset_url")) url_value.setWordWrap(True) url_value.setStyleSheet("font-size: 9px; color: #666;") - self._propertyLayout.addWidget(url_label) - self._propertyLayout.addWidget(url_value) + tileset_layout.addWidget(url_value, 0, 1, 1, 3) + # self._propertyLayout.addWidget(url_label) + # self._propertyLayout.addWidget(url_value) + tileset_group.setLayout(tileset_layout) + self._propertyLayout.addWidget(tileset_group) # 位置控制 pos_group = QGroupBox("位置") - pos_layout = QFormLayout() + pos_layout = QGridLayout() + pos_layout.setColumnMinimumWidth(0, self.column_minimum_width) + + pos_row = QHBoxLayout() + pos_row.setContentsMargins(0, 0, 0, 0) + pos_row.setSpacing(4) + pos_layout.addWidget(QLabel("位置"), 0, 0) # X 坐标 x_spin = QDoubleSpinBox() @@ -1433,7 +1478,11 @@ class PropertyPanelManager: pos = nodePath.getPos() x_spin.setValue(pos.getX()) x_spin.valueChanged.connect(lambda v: self._updateTilesetPosition(nodePath, 'x', v)) - pos_layout.addRow("X:", x_spin) + x_label = QLabel("X:") + x_label.setAlignment(Qt.AlignVCenter) + x_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + pos_row.addWidget(x_label) + pos_row.addWidget(x_spin) # Y 坐标 y_spin = QDoubleSpinBox() @@ -1442,7 +1491,11 @@ class PropertyPanelManager: y_spin.setDecimals(2) y_spin.setValue(pos.getY()) y_spin.valueChanged.connect(lambda v: self._updateTilesetPosition(nodePath, 'y', v)) - pos_layout.addRow("Y:", y_spin) + y_label = QLabel("Y:") + y_label.setAlignment(Qt.AlignVCenter) + y_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + pos_row.addWidget(y_label) + pos_row.addWidget(y_spin) # Z 坐标 z_spin = QDoubleSpinBox() @@ -1451,25 +1504,28 @@ class PropertyPanelManager: z_spin.setDecimals(2) z_spin.setValue(pos.getZ()) z_spin.valueChanged.connect(lambda v: self._updateTilesetPosition(nodePath, 'z', v)) - pos_layout.addRow("Z:", z_spin) + z_label = QLabel("Z:") + z_label.setAlignment(Qt.AlignVCenter) + z_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + pos_row.addWidget(z_label) + pos_row.addWidget(z_spin) + + pos_layout.addLayout(pos_row, 0, 1, 1, 3) pos_group.setLayout(pos_layout) - self._propertyLayout.addWidget(pos_group) # 缩放控制 - scale_group = QGroupBox("缩放") - scale_layout = QFormLayout() - + scale_label = QLabel("缩放") + pos_layout.addWidget(scale_label, 1, 0) scale_spin = QDoubleSpinBox() scale_spin.setRange(0.01, 1000) scale_spin.setSingleStep(0.1) scale_spin.setDecimals(2) scale_spin.setValue(nodePath.getScale().getX()) scale_spin.valueChanged.connect(lambda v: self._updateTilesetScale(nodePath, v)) - scale_layout.addRow("缩放:", scale_spin) + pos_layout.addWidget(scale_spin, 1, 1, 1, 3) - scale_group.setLayout(scale_layout) - self._propertyLayout.addWidget(scale_group) + self._propertyLayout.addWidget(pos_group) # # 删除按钮 # delete_btn = QPushButton("删除 Tileset") @@ -1622,234 +1678,168 @@ class PropertyPanelManager: # 获取父节点 parent = model.getParent() - # 变换属性部分 (Transform) - 根据Figma设计优化 + # 变换属性部分 (Transform) self.transform_group = QGroupBox("变换 Transform") - # 应用Figma设计的GroupBox样式 - self.transform_group.setStyleSheet(""" - QGroupBox { - margin-top: 0px; - border: none; - padding-top: 20px; - padding-left: 8px; - padding-right: 8px; - background-color: transparent; - margin-bottom: 6px; - border-top: 1px solid rgba(77, 116, 189, 0.4); - margin-left: 8px; - margin-right: 8px; - } - QGroupBox::title { - subcontrol-origin: padding; - subcontrol-position: top left; - padding: 5px 3px 8px 0px; - margin-top: 0px; - border: none; - color: rgba(255, 255, 255, 0.8); - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 12px; - font-weight: 500; - letter-spacing: 0.6px; - } - """) - self._propertyLayout.addWidget(self.transform_group) - transform_layout = QVBoxLayout() - transform_layout.setSpacing(8) # 设置行间距 - transform_layout.setContentsMargins(0, 8, 0, 8) + transform_layout = QGridLayout() + transform_layout.setColumnMinimumWidth(0, self.column_minimum_width) + # transform_layout.setColumnStretch(1, 1) + # transform_layout.setColumnStretch(2, 1) + # transform_layout.setColumnStretch(3, 1) + # 获取当前值 relativePos = model.getPos(parent) if parent else model.getPos() - current_scale = model.getScale() + worldPos = model.getPos(self.world.render) - # 位置行 (Position) - position_container = QWidget() - position_layout = QHBoxLayout(position_container) - position_layout.setContentsMargins(0, 0, 0, 0) - position_layout.setSpacing(6) + position_label = QLabel("位置") + transform_layout.addWidget(position_label, 0, 0) - # 位置标签 - pos_label = QLabel("位置:") - pos_label.setStyleSheet(""" - QLabel { - color: #ebebeb; - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 10px; - font-weight: 300; - letter-spacing: 0.5px; - min-width: 31px; - } - """) - position_layout.addWidget(pos_label) - - # 添加弹性空间 - position_layout.addStretch() - - # X位置 - x_pos_label = QLabel("X:") - x_pos_label.setStyleSheet(""" - QLabel { - color: #ebebeb; - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 10px; - font-weight: 300; - letter-spacing: 0.5px; - } - """) - position_layout.addWidget(x_pos_label) + # 位置 (Position) + position_row = QHBoxLayout() + position_row.setContentsMargins(0, 0, 0, 0) + position_row.setSpacing(4) + # position_row.addWidget(QLabel("位置")) self.pos_x = QDoubleSpinBox() - self.pos_x.setRange(-1000000.0, 1000000.0) - self.pos_x.setValue(relativePos.getX()) - self.pos_x.setStyleSheet(self._getFigmaSpinBoxStyle()) - position_layout.addWidget(self.pos_x) - - # Y位置 - y_pos_label = QLabel("Y:") - y_pos_label.setStyleSheet(""" - QLabel { - color: #ebebeb; - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 10px; - font-weight: 300; - letter-spacing: 0.5px; - } - """) - position_layout.addWidget(y_pos_label) - self.pos_y = QDoubleSpinBox() - self.pos_y.setRange(-1000000.0, 1000000.0) - self.pos_y.setValue(relativePos.getY()) - self.pos_y.setStyleSheet(self._getFigmaSpinBoxStyle()) - position_layout.addWidget(self.pos_y) - - # Z位置 - z_pos_label = QLabel("Z:") - z_pos_label.setStyleSheet(""" - QLabel { - color: #ebebeb; - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 10px; - font-weight: 300; - letter-spacing: 0.5px; - } - """) - position_layout.addWidget(z_pos_label) - self.pos_z = QDoubleSpinBox() - self.pos_z.setRange(-1000000.0, 1000000.0) + + # 设置位置控件属性 + for pos_widget in [self.pos_x, self.pos_y, self.pos_z]: + pos_widget.setRange(-1000000.0, 1000000.0) + + self.pos_x.setValue(relativePos.getX()) + self.pos_y.setValue(relativePos.getY()) self.pos_z.setValue(relativePos.getZ()) - self.pos_z.setStyleSheet(self._getFigmaSpinBoxStyle()) - position_layout.addWidget(self.pos_z) - transform_layout.addWidget(position_container) - - # 缩放行 (Scale) - scale_container = QWidget() - scale_layout = QHBoxLayout(scale_container) - scale_layout.setContentsMargins(0, 0, 0, 0) - scale_layout.setSpacing(6) - - # 缩放标签 - scale_label = QLabel("缩放:") - scale_label.setStyleSheet(""" - QLabel { - color: #ebebeb; - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 10px; - font-weight: 300; - letter-spacing: 0.5px; - min-width: 31px; - } - """) - scale_layout.addWidget(scale_label) - - # 添加弹性空间 - scale_layout.addStretch() - - # X缩放 - x_scale_label = QLabel("X:") - x_scale_label.setStyleSheet(""" - QLabel { - color: #ebebeb; - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 10px; - font-weight: 300; - letter-spacing: 0.5px; - } - """) - scale_layout.addWidget(x_scale_label) - - self.scale_x = QDoubleSpinBox() - self.scale_x.setRange(-1000, 1000) - self.scale_x.setSingleStep(0.1) - self.scale_x.setValue(current_scale.getX()) - self.scale_x.setStyleSheet(self._getFigmaSpinBoxStyle()) - scale_layout.addWidget(self.scale_x) - - # Y缩放 - y_scale_label = QLabel("Y:") - y_scale_label.setStyleSheet(""" - QLabel { - color: #ebebeb; - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 10px; - font-weight: 300; - letter-spacing: 0.5px; - } - """) - scale_layout.addWidget(y_scale_label) - - self.scale_y = QDoubleSpinBox() - self.scale_y.setRange(-1000, 1000) - self.scale_y.setSingleStep(0.1) - self.scale_y.setValue(current_scale.getY()) - self.scale_y.setStyleSheet(self._getFigmaSpinBoxStyle()) - scale_layout.addWidget(self.scale_y) - - # Z缩放 - z_scale_label = QLabel("Z:") - z_scale_label.setStyleSheet(""" - QLabel { - color: #ebebeb; - font-family: 'Inter', 'Microsoft YaHei', sans-serif; - font-size: 10px; - font-weight: 300; - letter-spacing: 0.5px; - } - """) - scale_layout.addWidget(z_scale_label) - - self.scale_z = QDoubleSpinBox() - self.scale_z.setRange(-1000, 1000) - self.scale_z.setSingleStep(0.1) - self.scale_z.setValue(current_scale.getZ()) - self.scale_z.setStyleSheet(self._getFigmaSpinBoxStyle()) - scale_layout.addWidget(self.scale_z) - - transform_layout.addWidget(scale_container) - - # 连接事件 + # 连接位置变化事件 def updateXPosition(value): model.setX(value) self.refreshModelValues(model) + self.pos_x.valueChanged.connect(updateXPosition) + def updateYPosition(value): model.setY(value) self.refreshModelValues(model) + self.pos_y.valueChanged.connect(updateYPosition) + def updateZPosition(value): model.setZ(value) self.refreshModelValues(model) - self.pos_x.valueChanged.connect(updateXPosition) - self.pos_y.valueChanged.connect(updateYPosition) self.pos_z.valueChanged.connect(updateZPosition) - # 连接缩放变化事件 + # 水平排布 X, Y, Z + for axis_label, widget in (("X:", self.pos_x), ("Y:", self.pos_y), ("Z:", self.pos_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + position_row.addWidget(axis) + position_row.addWidget(widget) + + transform_layout.addLayout(position_row, 0, 1, 1, 3) + + # 世界位置 (只读) + world_position_row = QHBoxLayout() + world_position_row.setContentsMargins(0, 0, 0, 0) + world_position_row.setSpacing(4) + # world_position_row.addWidget(QLabel("世界位置")) + world_position_label = QLabel("世界位置") + transform_layout.addWidget(world_position_label, 1, 0) + + self.world_pos_x = QDoubleSpinBox() + self.world_pos_y = QDoubleSpinBox() + self.world_pos_z = QDoubleSpinBox() + + for world_pos_widget in [self.world_pos_x, self.world_pos_y, self.world_pos_z]: + world_pos_widget.setRange(-1000000.0, 1000000.0) + world_pos_widget.setReadOnly(True) + + self.world_pos_x.setValue(worldPos.getX()) + self.world_pos_y.setValue(worldPos.getY()) + self.world_pos_z.setValue(worldPos.getZ()) + + for axis_label, widget in (("X:", self.world_pos_x), ("Y:", self.world_pos_y), ("Z:", self.world_pos_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + world_position_row.addWidget(axis) + world_position_row.addWidget(widget) + + transform_layout.addLayout(world_position_row, 1, 1, 1, 3) + + # 旋转 (Rotation) + rotation_row = QHBoxLayout() + rotation_row.setContentsMargins(0, 0, 0, 0) + rotation_row.setSpacing(4) + rotation_label = QLabel("旋转") + transform_layout.addWidget(rotation_label, 2, 0) + + self.rot_x = QDoubleSpinBox() + self.rot_y = QDoubleSpinBox() + self.rot_z = QDoubleSpinBox() + + for rot_widget in [self.rot_x, self.rot_y, self.rot_z]: + rot_widget.setRange(-360, 360) + + self.rot_x.setValue(model.getH()) + self.rot_y.setValue(model.getP()) + self.rot_z.setValue(model.getR()) + + self.rot_x.valueChanged.connect(lambda v: model.setH(v)) + self.rot_y.valueChanged.connect(lambda v: model.setP(v)) + self.rot_z.valueChanged.connect(lambda v: model.setR(v)) + + for axis_label, widget in (("H:", self.rot_x), ("P:", self.rot_y), ("R:", self.rot_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + rotation_row.addWidget(axis) + rotation_row.addWidget(widget) + + transform_layout.addLayout(rotation_row, 2, 1, 1, 3) + + # 缩放 (Scale) + scale_row = QHBoxLayout() + scale_row.setContentsMargins(0, 0, 0, 0) + scale_row.setSpacing(4) + scale_label = QLabel("缩放") + transform_layout.addWidget(scale_label, 3, 0) + + self.scale_x = QDoubleSpinBox() + self.scale_y = QDoubleSpinBox() + self.scale_z = QDoubleSpinBox() + + current_scale = model.getScale() + + for scale_widget, scale_value in zip([self.scale_x, self.scale_y, self.scale_z], + [current_scale.getX(), current_scale.getY(), + current_scale.getZ()]): + scale_widget.setRange(-1000, 1000) + scale_widget.setSingleStep(0.1) + scale_widget.setValue(scale_value) + + self.scale_x.valueChanged.connect(lambda value: self._onScaleValueChanged(self.scale_x, value)) + self.scale_y.valueChanged.connect(lambda value: self._onScaleValueChanged(self.scale_y, value)) + self.scale_z.valueChanged.connect(lambda value: self._onScaleValueChanged(self.scale_z, value)) + self.scale_x.valueChanged.connect(lambda value: self._updateXScale(model, value)) self.scale_y.valueChanged.connect(lambda value: self._updateYScale(model, value)) self.scale_z.valueChanged.connect(lambda value: self._updateZScale(model, value)) + for axis_label, widget in (("X:", self.scale_x), ("Y:", self.scale_y), ("Z:", self.scale_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + scale_row.addWidget(axis) + scale_row.addWidget(widget) + + transform_layout.addLayout(scale_row, 3, 1, 1, 3) + self.transform_group.setLayout(transform_layout) + self._propertyLayout.addWidget(self.transform_group) # 碰撞检测面板 self._addCollisionPanel(model) @@ -1934,7 +1924,7 @@ class PropertyPanelManager: gui_element.setPythonTag("user_visible", True) self.name_group = QGroupBox("物体名称") - name_layout = QHBoxLayout() + name_layout = QGridLayout() self.active_check = QCheckBox() # 根据元素的实际可见性状态设置复选框 self.active_check.setChecked(user_visible) @@ -1955,8 +1945,9 @@ class PropertyPanelManager: # 如果失去焦点也更新名称 self.name_input.editingFinished.connect(lambda: updateGUIName(self.name_input.text())) - name_layout.addWidget(self.active_check) - name_layout.addWidget(self.name_input) + name_layout.addWidget(self.active_check, 0, 0) + name_layout.setColumnMinimumWidth(0, self.column_minimum_width) + name_layout.addWidget(self.name_input, 0, 1, 1, 3) self.name_group.setLayout(name_layout) self._propertyLayout.addWidget(self.name_group) @@ -1975,12 +1966,13 @@ class PropertyPanelManager: # GUI基本信息组 gui_info_group = QGroupBox("GUI信息") gui_info_layout = QGridLayout() + gui_info_layout.setColumnMinimumWidth(0, self.column_minimum_width) # GUI类型显示 gui_info_layout.addWidget(QLabel("GUI类型:"), 0, 0) typeValue = QLabel(gui_type) # typeValue.setStyleSheet("color: #00AAFF; font-weight: bold;") - gui_info_layout.addWidget(typeValue, 0, 1) + gui_info_layout.addWidget(typeValue, 0, 1, 1, 3) # 修改 updateGUIPropertyPanel 中的文本属性部分 # 文本属性(如果适用) @@ -2001,7 +1993,7 @@ class PropertyPanelManager: textEdit.returnPressed.connect(updateText) textEdit.editingFinished.connect(updateText) - gui_info_layout.addWidget(textEdit, 1, 1) + gui_info_layout.addWidget(textEdit, 1, 1, 1, 3) gui_info_group.setLayout(gui_info_layout) self._propertyLayout.addWidget(gui_info_group) @@ -2015,6 +2007,7 @@ class PropertyPanelManager: transform_group = QGroupBox("变换 Transform") transform_layout = QGridLayout() + transform_layout.setColumnMinimumWidth(0, self.column_minimum_width) pos = gui_element.getPos() @@ -2024,40 +2017,90 @@ class PropertyPanelManager: logical_x = pos.getX() / 0.1 # 反向转换为逻辑坐标 logical_z = pos.getZ() / 0.1 - # 屏幕位置控件 - transform_layout.addWidget(QLabel("屏幕位置"), 0, 0) + position_row = QHBoxLayout() + position_row.setContentsMargins(0, 0, 0, 0) + position_row.setSpacing(4) - # X, Z 标签居中 - x_label = QLabel("X") - z_label = QLabel("Z") - x_label.setAlignment(Qt.AlignCenter) - z_label.setAlignment(Qt.AlignCenter) - - transform_layout.addWidget(x_label, 0, 1) - transform_layout.addWidget(z_label, 0, 2) + screen_title = QLabel("屏幕位置") + transform_layout.addWidget(screen_title, 0, 0) self.pos_x = QDoubleSpinBox() self.pos_x.setRange(-1000, 1000) self.pos_x.setValue(logical_x) - self.pos_x.valueChanged.connect(lambda v: self.world.gui_manager.editGUI2DPosition(gui_element, "x", v)) - transform_layout.addWidget(self.pos_x, 1, 1) - self.pos_z = QDoubleSpinBox() self.pos_z.setRange(-1000, 1000) self.pos_z.setValue(logical_z) - self.pos_z.valueChanged.connect(lambda v: self.world.gui_manager.editGUI2DPosition(gui_element, "z", v)) - transform_layout.addWidget(self.pos_z, 1, 2) - # 显示实际屏幕坐标(只读) - transform_layout.addWidget(QLabel("实际坐标"), 2, 0) + axis_labels = [] + for col, (axis_label, widget) in enumerate((("X:", self.pos_x), ("Z:", self.pos_z)), start=1): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + axis_labels.append(axis) + position_row.addWidget(axis) + position_row.addWidget(widget) - actualXLabel = QLabel(f"{pos.getX():.3f}") - actualXLabel.setStyleSheet("color: gray; font-size: 10px;") - actualZLabel = QLabel(f"{pos.getZ():.3f}") - actualZLabel.setStyleSheet("color: gray; font-size: 10px;") + transform_layout.addLayout(position_row, 0, 1, 1, 3) - transform_layout.addWidget(actualXLabel, 3, 1) - transform_layout.addWidget(actualZLabel, 3, 2) + # axis_width = max(label.sizeHint().width() for label in axis_labels) + # for label in axis_labels: + # label.setMinimumWidth(axis_width) + + # screen_position_row.setColumnStretch(2, 1) + # screen_position_row.setColumnStretch(4, 1) + + for i in range(4): + transform_layout.setColumnStretch(i, 1) + actual_position_row = QGridLayout() + + actual_row = QHBoxLayout() + actual_row.setContentsMargins(0, 0, 0, 0) + actual_row.setSpacing(4) + + actual_title = QLabel("实际坐标") + # actual_row.addWidget(actual_title) + transform_layout.addWidget(actual_title, 1, 0) + + self.actual_pos_x_label = QLabel(f"{pos.getX():.3f}") + self.actual_pos_x_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + self.actual_pos_x_label.setStyleSheet("color: gray; font-size: 10px;") + self.actual_pos_z_label = QLabel(f"{pos.getZ():.3f}") + self.actual_pos_z_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + self.actual_pos_z_label.setStyleSheet("color: gray; font-size: 10px;") + + for col, (axis_label, widget) in enumerate((("X:", self.actual_pos_x_label), + ("Z:", self.actual_pos_z_label)), start=1): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + # axis.setMinimumWidth(axis_width) + # actual_position_row.addWidget(axis, 0, col * 2 - 1, Qt.AlignLeft | Qt.AlignVCenter) + # actual_position_row.addWidget(widget, 0, col * 2, Qt.AlignLeft | Qt.AlignVCenter) + actual_row.addWidget(axis) + actual_row.addWidget(widget) + + # actual_position_row.setColumnStretch(2, 1) + # actual_position_row.setColumnStretch(4, 1) + + transform_layout.addLayout(actual_row, 1, 1, 1, 3) + + def _update_actual_position_labels(): + current_pos = gui_element.getPos() + self.actual_pos_x_label.setText(f"{current_pos.getX():.3f}") + self.actual_pos_z_label.setText(f"{current_pos.getZ():.3f}") + + _update_actual_position_labels() + + def _on_gui_pos_x_changed(value): + self.world.gui_manager.editGUI2DPosition(gui_element, "x", value) + _update_actual_position_labels() + + def _on_gui_pos_z_changed(value): + self.world.gui_manager.editGUI2DPosition(gui_element, "z", value) + _update_actual_position_labels() + + self.pos_x.valueChanged.connect(_on_gui_pos_x_changed) + self.pos_z.valueChanged.connect(_on_gui_pos_z_changed) if gui_type in ["2d_image", "2d_video_screen"]: scale = gui_element.getScale() @@ -2067,93 +2110,101 @@ class PropertyPanelManager: (tuple, list)) and len( scale) > 1 else scale - transform_layout.addWidget(QLabel("宽度"), 4, 0) + scale_row = QHBoxLayout() + scale_row.setContentsMargins(0, 0, 0, 0) + scale_row.setSpacing(4) + transform_layout.addWidget(QLabel("缩放"), 2, 0) + self.scale_x = QDoubleSpinBox() self.scale_x.setRange(0.1, 100) self.scale_x.setSingleStep(0.01) self.scale_x.setValue(width) self.scale_x.valueChanged.connect(lambda v: self.editGUIScale(gui_element, "x", v)) - transform_layout.addWidget(self.scale_x, 4, 1) - transform_layout.addWidget(QLabel("高度"), 4, 2) self.scale_z = QDoubleSpinBox() self.scale_z.setRange(0.1, 10) self.scale_z.setSingleStep(0.01) self.scale_z.setValue(height) self.scale_z.valueChanged.connect(lambda v: self.editGUIScale(gui_element, "z", v)) - transform_layout.addWidget(self.scale_z, 4, 3) + + for axis_label, widget in (("X:", self.scale_x), ("Z:", self.scale_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + scale_row.addWidget(axis) + scale_row.addWidget(widget) + + transform_layout.addLayout(scale_row, 2, 1, 1, 3) else: - transform_layout.addWidget(QLabel("缩放"), 4, 0) + scale_row = QHBoxLayout() + scale_row.setContentsMargins(0, 0, 0, 0) + scale_row.setSpacing(4) + transform_layout.addWidget(QLabel("缩放"), 2, 0) + scaleSpinBox = QDoubleSpinBox() scaleSpinBox.setRange(0.01, 100) scaleSpinBox.setSingleStep(0.01) scaleSpinBox.setValue(gui_element.getScale().getX() * 2) scaleSpinBox.valueChanged.connect(lambda v: self._update2DImageScale(gui_element, v)) - transform_layout.addWidget(scaleSpinBox, 4, 1) + + axis = QLabel("X:") + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + scale_row.addWidget(axis) + scale_row.addWidget(scaleSpinBox) + transform_layout.addLayout(scale_row, 2, 1, 1, 3) else: # 3D GUI组件使用世界坐标 - transform_layout.addWidget(QLabel("位置"), 0, 0) + position_row = QHBoxLayout() + position_row.setContentsMargins(0, 0, 0, 0) + position_row.setSpacing(4) + # position_row.addWidget(QLabel("位置")) + position_label = QLabel("位置") + transform_layout.addWidget(position_label, 0, 0) - # X, Y, Z 标签居中 - x_label = QLabel("X") - y_label = QLabel("Y") - z_label = QLabel("Z") - x_label.setAlignment(Qt.AlignCenter) - y_label.setAlignment(Qt.AlignCenter) - z_label.setAlignment(Qt.AlignCenter) - - transform_layout.addWidget(x_label, 0, 1) - transform_layout.addWidget(y_label, 0, 2) - transform_layout.addWidget(z_label, 0, 3) - - # 位置数值输入框 self.pos_x = QDoubleSpinBox() self.pos_x.setRange(-100, 100) self.pos_x.setValue(pos.getX()) self.pos_x.setSingleStep(0.01) self.pos_x.valueChanged.connect(lambda v: self.world.gui_manager.editGUI3DPosition(gui_element, "x", v)) - transform_layout.addWidget(self.pos_x, 1, 1) self.pos_y = QDoubleSpinBox() self.pos_y.setRange(-100, 100) self.pos_y.setValue(pos.getY()) self.pos_y.setSingleStep(0.01) self.pos_y.valueChanged.connect(lambda v: self.world.gui_manager.editGUI3DPosition(gui_element, "y", v)) - transform_layout.addWidget(self.pos_y, 1, 2) self.pos_z = QDoubleSpinBox() self.pos_z.setRange(-100, 100) self.pos_z.setValue(pos.getZ()) self.pos_z.setSingleStep(0.01) self.pos_z.valueChanged.connect(lambda v: self.world.gui_manager.editGUI3DPosition(gui_element, "z", v)) - transform_layout.addWidget(self.pos_z, 1, 3) - # 缩放属性 + for axis_label, widget in (("X:", self.pos_x), ("Y:", self.pos_y), ("Z:", self.pos_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + position_row.addWidget(axis) + position_row.addWidget(widget) + + transform_layout.addLayout(position_row, 0, 1, 1, 3) + scale = gui_element.getScale() - transform_layout.addWidget(QLabel("缩放"), 2, 0) + scale_row = QHBoxLayout() + scale_row.setContentsMargins(0, 0, 0, 0) + scale_row.setSpacing(4) + # scale_row.addWidget(QLabel("缩放")) + scale_label = QLabel("缩放") + transform_layout.addWidget(scale_label, 1, 0) - # X, Y, Z 缩放标签居中 - sx_label = QLabel("X") - sy_label = QLabel("Y") - sz_label = QLabel("Z") - sx_label.setAlignment(Qt.AlignCenter) - sy_label.setAlignment(Qt.AlignCenter) - sz_label.setAlignment(Qt.AlignCenter) - - transform_layout.addWidget(sx_label, 2, 1) - transform_layout.addWidget(sy_label, 2, 2) - transform_layout.addWidget(sz_label, 2, 3) - - # 缩放数值输入框 self.scale_x = QDoubleSpinBox() self.scale_x.setRange(0.01, 100) self.scale_x.setSingleStep(0.01) self.scale_x.setValue( scale.getX() if hasattr(scale, 'getX') else scale[0] if isinstance(scale, (tuple, list)) else scale) self.scale_x.valueChanged.connect(lambda v: self.world.gui_manager.editGUIScale(gui_element, "x", v)) - transform_layout.addWidget(self.scale_x, 3, 1) self.scale_y = QDoubleSpinBox() self.scale_y.setRange(0.01, 100) @@ -2162,7 +2213,6 @@ class PropertyPanelManager: scale.getY() if hasattr(scale, 'getY') else scale[1] if isinstance(scale, (tuple, list)) and len( scale) > 1 else scale) self.scale_y.valueChanged.connect(lambda v: self.world.gui_manager.editGUIScale(gui_element, "y", v)) - transform_layout.addWidget(self.scale_y, 3, 2) self.scale_z = QDoubleSpinBox() self.scale_z.setRange(0.01, 100) @@ -2171,7 +2221,15 @@ class PropertyPanelManager: scale.getZ() if hasattr(scale, 'getZ') else scale[2] if isinstance(scale, (tuple, list)) and len( scale) > 2 else scale) self.scale_z.valueChanged.connect(lambda v: self.world.gui_manager.editGUIScale(gui_element, "z", v)) - transform_layout.addWidget(self.scale_z, 3, 3) + + for axis_label, widget in (("X:", self.scale_x), ("Y:", self.scale_y), ("Z:", self.scale_z)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + scale_row.addWidget(axis) + scale_row.addWidget(widget) + + transform_layout.addLayout(scale_row, 1, 1, 1, 3) transform_group.setLayout(transform_layout) self._propertyLayout.addWidget(transform_group) @@ -2180,6 +2238,7 @@ class PropertyPanelManager: if gui_type in ["2d_image", "2d_video_screen", "info_panel"]: sort_group = QGroupBox("显示顺序") sort_layout = QGridLayout() + sort_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 获取当前sort值,如果没有设置则默认为0 current_sort = int(gui_element.getTag("sort") or "0") @@ -2200,7 +2259,7 @@ class PropertyPanelManager: print(f"✗ 更新{gui_type}渲染顺序失败: {e}") sort_spin.valueChanged.connect(updateSort) - sort_layout.addWidget(sort_spin, 0, 1) + sort_layout.addWidget(sort_spin, 0, 1, 1, 3) sort_group.setLayout(sort_layout) self._propertyLayout.addWidget(sort_group) @@ -2209,12 +2268,13 @@ class PropertyPanelManager: if gui_type in ["button", "label", "3d_text"]: appearance_group = QGroupBox("外观属性") appearance_layout = QGridLayout() + appearance_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 字体颜色选择 appearance_layout.addWidget(QLabel("字体颜色:"), 0, 0) colorButton = QPushButton("选择颜色") colorButton.clicked.connect(lambda checked, elem=gui_element: self._selectGUIColor(elem)) - appearance_layout.addWidget(colorButton, 0, 1) + appearance_layout.addWidget(colorButton, 0, 1, 1, 3) appearance_group.setLayout(appearance_layout) self._propertyLayout.addWidget(appearance_group) @@ -2223,12 +2283,13 @@ class PropertyPanelManager: if gui_type in ["button", "label"]: appearance_group = QGroupBox("外观属性") appearance_layout = QGridLayout() + appearance_layout.setColumnMinimumWidth(0, self.column_minimum_width) appearance_layout.addWidget(QLabel("背景颜色:"), 0, 0) colorButton = QPushButton("选择颜色") colorButton.clicked.connect(lambda: self.world.gui_manager.selectGUIColor(gui_element)) - appearance_layout.addWidget(colorButton, 0, 1) + appearance_layout.addWidget(colorButton, 0, 1, 1, 3) appearance_group.setLayout(appearance_layout) self._propertyLayout.addWidget(appearance_group) @@ -2236,6 +2297,7 @@ class PropertyPanelManager: if gui_type == "3d_image": image_group = QGroupBox("图片设置") image_layout = QGridLayout() + image_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 当前图片路径标签和 current_image_label = QLabel("当前图片:") @@ -2251,11 +2313,14 @@ class PropertyPanelManager: texture_label = QLabel(display_path) texture_label.setWordWrap(True) texture_label.setToolTip(current_texture_path if current_texture_path != "未设置" else "") - image_layout.addWidget(texture_label, 0, 1) + image_layout.addWidget(texture_label, 0, 1, 1, 3) # 选择图片按钮 - select_texture_button = QPushButton("选择图片...") - image_layout.addWidget(select_texture_button, 1, 0, 1, 2) + if has_icon("property_select_image"): + select_texture_button = QPushButton(get_icon("property_select_image"), "选择图片...") + else: + select_texture_button = QPushButton("选择图片...") + image_layout.addWidget(select_texture_button, 1, 0, 1, 4) def onSelectTexture(): from PyQt5.QtWidgets import QFileDialog @@ -2286,6 +2351,7 @@ class PropertyPanelManager: if gui_type in ["2d_image"]: image_group = QGroupBox("2D图片设置") image_layout = QGridLayout() + image_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 当前图片路径标签 current_image_label = QLabel("当前图片:") @@ -2300,11 +2366,15 @@ class PropertyPanelManager: display_path = current_texture_path texture_label = QLabel(display_path) texture_label.setWordWrap(True) - image_layout.addWidget(texture_label, 0, 1) + image_layout.addWidget(texture_label, 0, 1, 1, 3) # 选择图片按钮 - select_texture_button = QPushButton("选择图片...") - image_layout.addWidget(select_texture_button, 1, 0, 1, 2) + if has_icon("property_select_image"): + select_texture_button = QPushButton(get_icon("property_select_image"), "选择图片...") + else: + select_texture_button = QPushButton("选择图片...") + # select_texture_button = QPushButton("选择图片...") + image_layout.addWidget(select_texture_button, 1, 0, 1, 4) def onSelect2DTexture(): from PyQt5.QtWidgets import QFileDialog @@ -2463,6 +2533,7 @@ class PropertyPanelManager: # 背景图片组 image_group = QGroupBox("背景图片设置") image_layout = QGridLayout() + image_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 当前背景图片路径标签 current_image_label = QLabel("当前背景图片:") @@ -2476,16 +2547,16 @@ class PropertyPanelManager: texture_label = QLabel(display_path) texture_label.setWordWrap(True) texture_label.setToolTip(current_bg_image if current_bg_image else "") - image_layout.addWidget(texture_label, 0, 1) + image_layout.addWidget(texture_label, 0, 1, 1, 3) # 选择背景图片按钮 select_texture_button = QPushButton("选择背景图片...") - image_layout.addWidget(select_texture_button, 1, 0, 1, 2) + image_layout.addWidget(select_texture_button, 1, 0, 1, 4) # 清除背景图片按钮 clear_texture_button = QPushButton("清除背景图片") clear_texture_button.setStyleSheet("color: red;") - image_layout.addWidget(clear_texture_button, 2, 0, 1, 2) + image_layout.addWidget(clear_texture_button, 2, 0, 1, 4) def onSelectBackgroundImage(): """选择背景图片""" @@ -2526,6 +2597,7 @@ class PropertyPanelManager: # 显示顺序组 sort_group = QGroupBox("显示顺序") sort_layout = QGridLayout() + sort_layout.setColumnMinimumWidth(0, self.column_minimum_width) sort_layout.addWidget(QLabel("显示优先度:"), 0, 0) sort_spin = QSpinBox() @@ -2543,28 +2615,42 @@ class PropertyPanelManager: print(f"✗ 更新信息面板渲染顺序失败: {e}") sort_spin.valueChanged.connect(updateSort) - sort_layout.addWidget(sort_spin, 0, 1) + sort_layout.addWidget(sort_spin, 0, 1, 1, 3) sort_group.setLayout(sort_layout) self._propertyLayout.addWidget(sort_group) # 面板大小设置组 - size_group = QGroupBox("面板大小设置") + size_group = QGroupBox() + size_group.setTitle("面板大小设置") size_layout = QGridLayout() + size_layout.setColumnMinimumWidth(0, self.column_minimum_width) + + size_row = QHBoxLayout() + size_row.setContentsMargins(0, 0, 0, 0) + size_row.setSpacing(4) + + size_title = QLabel("面板大小设置:") + size_layout.addWidget(size_title, 0, 0) - size_layout.addWidget(QLabel("宽度:"), 0, 0) width_spin = QDoubleSpinBox() width_spin.setRange(0.1, 5.0) width_spin.setSingleStep(0.1) width_spin.setValue(current_size[0]) - size_layout.addWidget(width_spin, 0, 1) - size_layout.addWidget(QLabel("高度:"), 0, 2) height_spin = QDoubleSpinBox() height_spin.setRange(0.1, 5.0) height_spin.setSingleStep(0.1) height_spin.setValue(current_size[1]) - size_layout.addWidget(height_spin, 0, 3) + + for label_text, spinbox in (("宽度:", width_spin), ("高度:", height_spin)): + label = QLabel(label_text) + label.setAlignment(Qt.AlignVCenter) + label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + size_row.addWidget(label) + size_row.addWidget(spinbox) + + size_layout.addLayout(size_row, 0, 1, 1, 3) # 连接大小变化信号 def onSizeChanged(): @@ -2589,36 +2675,40 @@ class PropertyPanelManager: size_group.setLayout(size_layout) self._propertyLayout.addWidget(size_group) - # 位置设置组 (根据面板类型显示不同的位置控件) - position_group = QGroupBox("位置设置") + # 位置设置:X:[box] Y:[box]/Z:[box],右对齐可伸缩 + position_group = QGroupBox() + position_group.setTitle("位置设置") position_layout = QGridLayout() + position_layout.setColumnMinimumWidth(0, self.column_minimum_width) + + position_row = QHBoxLayout() + position_row.setContentsMargins(0, 0, 0, 0) + position_row.setSpacing(4) + + position_title = QLabel("位置设置:") + position_layout.addWidget(position_title, 0, 0) if is_3d_panel: - # 3D面板位置设置 (X, Y, Z) - position_layout.addWidget(QLabel("X:"), 0, 0) + # 3D 面板位置:X、Y、Z pos_x_spin = QDoubleSpinBox() pos_x_spin.setRange(-1000, 1000) pos_x_spin.setSingleStep(0.1) pos_x_spin.setValue(current_position[0] if len(current_position) > 0 else 0) - position_layout.addWidget(pos_x_spin, 0, 1) - position_layout.addWidget(QLabel("Y:"), 0, 2) pos_y_spin = QDoubleSpinBox() pos_y_spin.setRange(-1000, 1000) pos_y_spin.setSingleStep(0.1) pos_y_spin.setValue(current_position[1] if len(current_position) > 1 else 0) - position_layout.addWidget(pos_y_spin, 0, 3) - position_layout.addWidget(QLabel("Z:"), 1, 0) pos_z_spin = QDoubleSpinBox() pos_z_spin.setRange(-1000, 1000) pos_z_spin.setSingleStep(0.1) pos_z_spin.setValue(current_position[2] if len(current_position) > 2 else 0) - position_layout.addWidget(pos_z_spin, 1, 1) - # 连接位置变化信号 + axis_widgets = (("X:", pos_x_spin), ("Y:", pos_y_spin), ("Z:", pos_z_spin)) + def on3DPositionChanged(): - if hasattr(self.world, 'info_panel_manager'): + if hasattr(self.world, "info_panel_manager"): self.world.info_panel_manager.update3DPanelProperties( panel_id, position=(pos_x_spin.value(), pos_y_spin.value(), pos_z_spin.value())) @@ -2626,76 +2716,107 @@ class PropertyPanelManager: pos_y_spin.valueChanged.connect(on3DPositionChanged) pos_z_spin.valueChanged.connect(on3DPositionChanged) else: - # 2D面板位置设置 (X, Z) - position_layout.addWidget(QLabel("X:"), 0, 0) + # 2D 面板位置:X、Z pos_x_spin = QDoubleSpinBox() pos_x_spin.setRange(-1000, 1000) pos_x_spin.setSingleStep(0.1) pos_x_spin.setValue(current_position[0] if len(current_position) > 0 else 0) - position_layout.addWidget(pos_x_spin, 0, 1) - position_layout.addWidget(QLabel("Z:"), 0, 2) pos_z_spin = QDoubleSpinBox() pos_z_spin.setRange(-1000, 1000) pos_z_spin.setSingleStep(0.1) pos_z_spin.setValue(current_position[1] if len(current_position) > 1 else 0) - position_layout.addWidget(pos_z_spin, 0, 3) - # 连接位置变化信号 + axis_widgets = (("X:", pos_x_spin), ("Z:", pos_z_spin)) + def on2DPositionChanged(): - if hasattr(self.world, 'info_panel_manager'): + if hasattr(self.world, "info_panel_manager"): self.world.info_panel_manager.updatePanelProperties( panel_id, position=(pos_x_spin.value(), pos_z_spin.value())) pos_x_spin.valueChanged.connect(on2DPositionChanged) pos_z_spin.valueChanged.connect(on2DPositionChanged) + for axis_label, spinbox in axis_widgets: + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + position_row.addWidget(axis) + position_row.addWidget(spinbox) + + position_layout.addLayout(position_row, 0, 1, 1, 3) position_group.setLayout(position_layout) self._propertyLayout.addWidget(position_group) # 边框属性组 border_group = QGroupBox("边框属性") border_layout = QGridLayout() + border_layout.setColumnMinimumWidth(0, self.column_minimum_width) border_layout.addWidget(QLabel("边框颜色:"), 0, 0) + # border_layout.addWidget(border_color_label, 0, 0) + + border_row_top = QHBoxLayout() + border_row_top.setContentsMargins(0, 0, 0, 0) + border_row_top.setSpacing(4) - # 边框R分量 - border_layout.addWidget(QLabel("R:"), 1, 0) border_r_spin = QDoubleSpinBox() border_r_spin.setRange(0.0, 1.0) border_r_spin.setSingleStep(0.01) border_r_spin.setValue(current_border_color[0]) - border_layout.addWidget(border_r_spin, 1, 1) + color_r_bg = QLabel("R:") + color_r_bg.setAlignment(Qt.AlignVCenter) + color_r_bg.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + border_row_top.addWidget(color_r_bg) + border_row_top.addWidget(border_r_spin) - # 边框G分量 - border_layout.addWidget(QLabel("G:"), 1, 2) border_g_spin = QDoubleSpinBox() border_g_spin.setRange(0.0, 1.0) border_g_spin.setSingleStep(0.01) border_g_spin.setValue(current_border_color[1]) - border_layout.addWidget(border_g_spin, 1, 3) + color_g = QLabel("G:") + color_g.setAlignment(Qt.AlignVCenter) + color_g.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + border_row_top.addWidget(color_g) + border_row_top.addWidget(border_g_spin) + + border_layout.addLayout(border_row_top, 0, 1, 1, 3) + + indent_placeholder = QLabel("") + border_layout.addWidget(indent_placeholder, 1, 0) + # indent_placeholder.setFixedWidth(border_color_label.sizeHint().width()) + # border_layout.addWidget(indent_placeholder, 1, 0) + + border_row_bottom = QHBoxLayout() + border_row_bottom.setContentsMargins(0, 0, 0, 0) + border_row_bottom.setSpacing(4) - # 边框B分量 - border_layout.addWidget(QLabel("B:"), 2, 0) border_b_spin = QDoubleSpinBox() border_b_spin.setRange(0.0, 1.0) border_b_spin.setSingleStep(0.01) border_b_spin.setValue(current_border_color[2]) - border_layout.addWidget(border_b_spin, 2, 1) + color_b = QLabel("B:") + color_b.setAlignment(Qt.AlignVCenter) + color_b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + border_row_bottom.addWidget(color_b) + border_row_bottom.addWidget(border_b_spin) - # 边框A分量 - border_layout.addWidget(QLabel("A:"), 2, 2) border_a_spin = QDoubleSpinBox() border_a_spin.setRange(0.0, 1.0) border_a_spin.setSingleStep(0.01) border_a_spin.setValue(current_border_color[3]) - border_layout.addWidget(border_a_spin, 2, 3) + color_a = QLabel("A:") + color_a.setAlignment(Qt.AlignVCenter) + color_a.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + border_row_bottom.addWidget(color_a) + border_row_bottom.addWidget(border_a_spin) + + border_layout.addLayout(border_row_bottom, 1, 1, 1, 3) - # 边框颜色选择按钮 border_color_button = QPushButton("选择边框颜色") border_color_button.clicked.connect(lambda: self._selectInfoPanelBorderColor( info_panel, border_r_spin, border_g_spin, border_b_spin, border_a_spin)) - border_layout.addWidget(border_color_button, 3, 0, 1, 4) + border_layout.addWidget(border_color_button, 2, 0, 1, 4) # 连接边框颜色变化信号 def onBorderColorChanged(): @@ -2724,45 +2845,74 @@ class PropertyPanelManager: bg_color_group = QGroupBox("背景颜色设置") bg_color_layout = QGridLayout() + bg_color_layout.setColumnMinimumWidth(0, self.column_minimum_width) - bg_color_layout.addWidget(QLabel("背景颜色:"), 0, 0) + bg_color_label = QLabel("背景颜色:") + bg_color_layout.addWidget(bg_color_label, 0, 0) + bg_color_row_top = QHBoxLayout() + bg_color_row_top.setContentsMargins(0, 0, 0, 0) + bg_color_row_top.setSpacing(4) - bg_color_layout.addWidget(QLabel("R:"), 1, 0) + # R分量 + color_r_bg = QLabel("R:") + color_r_bg.setAlignment(Qt.AlignVCenter) + color_r_bg.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) r_spin = QDoubleSpinBox() r_spin.setRange(0.0, 1.0) r_spin.setSingleStep(0.01) r_spin.setValue(current_bg_color[0]) - bg_color_layout.addWidget(r_spin, 1, 1) + bg_color_row_top.addWidget(color_r_bg) + bg_color_row_top.addWidget(r_spin) # G分量 - bg_color_layout.addWidget(QLabel("G:"), 1, 2) + color_g_bg = QLabel("G:") + color_g_bg.setAlignment(Qt.AlignVCenter) + color_g_bg.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) g_spin = QDoubleSpinBox() g_spin.setRange(0.0, 1.0) g_spin.setSingleStep(0.01) g_spin.setValue(current_bg_color[1]) - bg_color_layout.addWidget(g_spin, 1, 3) + bg_color_row_top.addWidget(color_g_bg) + bg_color_row_top.addWidget(g_spin) + + bg_color_layout.addLayout(bg_color_row_top, 0, 1, 1, 3) + + indent_placeholder = QLabel("") + bg_color_layout.addWidget(indent_placeholder, 1, 0) + + bg_color_row_bottom = QHBoxLayout() + bg_color_row_bottom.setContentsMargins(0, 0, 0, 0) + bg_color_row_bottom.setSpacing(4) # B分量 - bg_color_layout.addWidget(QLabel("B:"), 2, 0) + color_b_bg = QLabel("B:") + color_b_bg.setAlignment(Qt.AlignVCenter) + color_b_bg.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) b_spin = QDoubleSpinBox() b_spin.setRange(0.0, 1.0) b_spin.setSingleStep(0.01) b_spin.setValue(current_bg_color[2]) - bg_color_layout.addWidget(b_spin, 2, 1) + bg_color_row_bottom.addWidget(color_b_bg) + bg_color_row_bottom.addWidget(b_spin) # A分量 - bg_color_layout.addWidget(QLabel("A:"), 2, 2) + color_a_bg = QLabel("A:") + color_a_bg.setAlignment(Qt.AlignVCenter) + color_a_bg.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) a_spin = QDoubleSpinBox() a_spin.setRange(0.0, 1.0) a_spin.setSingleStep(0.01) a_spin.setValue(current_bg_color[3]) - bg_color_layout.addWidget(a_spin, 2, 3) + bg_color_row_bottom.addWidget(color_a_bg) + bg_color_row_bottom.addWidget(a_spin) + + bg_color_layout.addLayout(bg_color_row_bottom, 1, 1, 1, 3) # 颜色选择按钮 color_button = QPushButton("选择颜色") color_button.clicked.connect(lambda: self._selectInfoPanelBackgroundColor( info_panel, r_spin, g_spin, b_spin, a_spin)) - bg_color_layout.addWidget(color_button, 3, 0, 1, 4) + bg_color_layout.addWidget(color_button, 2, 0, 1, 4) # 连接颜色数值变化信号,自动更新背景颜色 def onBackgroundColorChanged(): @@ -2780,6 +2930,7 @@ class PropertyPanelManager: # 标题属性组 title_group = QGroupBox("标题属性") title_layout = QGridLayout() + title_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 标题文本 title_layout.addWidget(QLabel("标题文本:"), 0, 0) @@ -2791,37 +2942,62 @@ class PropertyPanelManager: # 标题颜色 title_layout.addWidget(QLabel("标题颜色:"), 1, 0) + title_color_row = QHBoxLayout() # 占位,保持对齐 + title_color_row.setContentsMargins(0, 0, 0, 0) + title_color_row.setSpacing(4) + # 标题R分量 - title_layout.addWidget(QLabel("R:"), 2, 0) + title_color_label_r = QLabel("R:") + title_color_label_r.setAlignment(Qt.AlignVCenter) + title_color_label_r.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + title_color_row.addWidget(title_color_label_r) title_r_spin = QDoubleSpinBox() title_r_spin.setRange(0.0, 1.0) title_r_spin.setSingleStep(0.01) title_r_spin.setValue(current_title_color[0]) - title_layout.addWidget(title_r_spin, 2, 1) + title_color_row.addWidget(title_r_spin) # 标题G分量 - title_layout.addWidget(QLabel("G:"), 2, 2) + title_color_label_g = QLabel("G:") + title_color_label_g.setAlignment(Qt.AlignVCenter) + title_color_label_g.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + title_color_row.addWidget(title_color_label_g) title_g_spin = QDoubleSpinBox() title_g_spin.setRange(0.0, 1.0) title_g_spin.setSingleStep(0.01) title_g_spin.setValue(current_title_color[1]) - title_layout.addWidget(title_g_spin, 2, 3) + title_color_row.addWidget(title_g_spin) + + title_layout.addLayout(title_color_row, 1, 1, 1, 3) + + title_layout.addWidget(QLabel(""), 2, 0) # 占位 + title_color_row_ = QHBoxLayout() # 占位,保持对齐 + title_color_row_.setContentsMargins(0, 0, 0, 0) + title_color_row_.setSpacing(4) # 标题B分量 - title_layout.addWidget(QLabel("B:"), 3, 0) + title_color_label_b = QLabel("B:") + title_color_label_b.setAlignment(Qt.AlignVCenter) + title_color_label_b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + title_color_row_.addWidget(title_color_label_b) title_b_spin = QDoubleSpinBox() title_b_spin.setRange(0.0, 1.0) title_b_spin.setSingleStep(0.01) title_b_spin.setValue(current_title_color[2]) - title_layout.addWidget(title_b_spin, 3, 1) + title_color_row_.addWidget(title_b_spin) # 标题A分量 - title_layout.addWidget(QLabel("A:"), 3, 2) + title_color_label_a = QLabel("A:") + title_color_label_a.setAlignment(Qt.AlignVCenter) + title_color_label_a.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + title_color_row_.addWidget(title_color_label_a) title_a_spin = QDoubleSpinBox() title_a_spin.setRange(0.0, 1.0) title_a_spin.setSingleStep(0.01) title_a_spin.setValue(current_title_color[3]) - title_layout.addWidget(title_a_spin, 3, 3) + title_color_row_.addWidget(title_a_spin) + + title_layout.addLayout(title_color_row_, 2, 1, 1, 3) # 标题颜色选择按钮 title_color_button = QPushButton("选择标题颜色") @@ -2839,20 +3015,31 @@ class PropertyPanelManager: # 标题位置控制 (仅对2D面板) if not is_3d_panel: - title_layout.addWidget(QLabel("标题位置:"), 6, 0) - title_layout.addWidget(QLabel("X:"), 7, 0) + title_position_row = QHBoxLayout() + title_position_row.setContentsMargins(0, 0, 0, 0) + title_position_row.setSpacing(4) + + title_position_label = QLabel("标题位置") + title_layout.addWidget(title_position_label, 6, 0) + title_x_spin = QDoubleSpinBox() title_x_spin.setRange(-1.0, 1.0) title_x_spin.setSingleStep(0.01) title_x_spin.setValue(current_title_pos[0]) - title_layout.addWidget(title_x_spin, 7, 1) - title_layout.addWidget(QLabel("Y:"), 7, 2) title_y_spin = QDoubleSpinBox() title_y_spin.setRange(-1.0, 1.0) title_y_spin.setSingleStep(0.01) title_y_spin.setValue(current_title_pos[1]) - title_layout.addWidget(title_y_spin, 7, 3) + + for axis_label, spinbox in (("X:", title_x_spin), ("Y:", title_y_spin)): + label = QLabel(axis_label) + label.setAlignment(Qt.AlignVCenter) + label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + title_position_row.addWidget(label) + title_position_row.addWidget(spinbox) + + title_layout.addLayout(title_position_row, 6, 1, 1, 3) # 连接标题属性变化信号 def onTitleTextChanged(): @@ -2901,6 +3088,7 @@ class PropertyPanelManager: # 内容属性组 content_group = QGroupBox("内容属性") content_layout = QGridLayout() + content_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 内容文本 content_layout.addWidget(QLabel("内容文本:"), 0, 0) @@ -2911,38 +3099,63 @@ class PropertyPanelManager: # 内容颜色 content_layout.addWidget(QLabel("内容颜色:"), 1, 0) + content_color_row = QHBoxLayout() + content_color_row.setContentsMargins(0, 0, 0, 0) + content_color_row.setSpacing(4) + # 内容R分量 - content_layout.addWidget(QLabel("R:"), 2, 0) + content_r = QLabel("R:") + content_r.setAlignment(Qt.AlignVCenter) + content_r.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + content_color_row.addWidget(content_r) content_r_spin = QDoubleSpinBox() content_r_spin.setRange(0.0, 1.0) content_r_spin.setSingleStep(0.01) content_r_spin.setValue(current_content_color[0]) - content_layout.addWidget(content_r_spin, 2, 1) + content_color_row.addWidget(content_r_spin) # 内容G分量 - content_layout.addWidget(QLabel("G:"), 2, 2) + content_g = QLabel("G:") + content_g.setAlignment(Qt.AlignVCenter) + content_g.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + content_color_row.addWidget(content_g) content_g_spin = QDoubleSpinBox() content_g_spin.setRange(0.0, 1.0) content_g_spin.setSingleStep(0.01) content_g_spin.setValue(current_content_color[1]) - content_layout.addWidget(content_g_spin, 2, 3) + content_color_row.addWidget(content_g_spin) + + content_layout.addLayout(content_color_row, 1, 1, 1, 3) + + content_color_row_ = QHBoxLayout() + content_color_row_.setContentsMargins(0, 0, 0, 0) + content_color_row_.setSpacing(4) + content_layout.addWidget(QLabel(":"), 2, 0) # 内容B分量 - content_layout.addWidget(QLabel("B:"), 3, 0) + content_b = QLabel("B:") + content_b.setAlignment(Qt.AlignVCenter) + content_b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + content_color_row_.addWidget(content_b) content_b_spin = QDoubleSpinBox() content_b_spin.setRange(0.0, 1.0) content_b_spin.setSingleStep(0.01) content_b_spin.setValue(current_content_color[2]) - content_layout.addWidget(content_b_spin, 3, 1) + content_color_row_.addWidget(content_b_spin) # 内容A分量 - content_layout.addWidget(QLabel("A:"), 3, 2) + content_a = QLabel("A:") + content_a.setAlignment(Qt.AlignVCenter) + content_a.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + content_color_row_.addWidget(content_a) content_a_spin = QDoubleSpinBox() content_a_spin.setRange(0.0, 1.0) content_a_spin.setSingleStep(0.01) content_a_spin.setValue(current_content_color[3]) - content_layout.addWidget(content_a_spin, 3, 3) + content_color_row_.addWidget(content_a_spin) + + content_layout.addLayout(content_color_row_, 2, 1, 1, 3) # 内容颜色选择按钮 content_color_button = QPushButton("选择内容颜色") @@ -2960,20 +3173,32 @@ class PropertyPanelManager: # 内容位置控制 (仅对2D面板) if not is_3d_panel: - content_layout.addWidget(QLabel("内容位置:"), 6, 0) - content_layout.addWidget(QLabel("X:"), 7, 0) + content_layout.addLayout(QLabel("内容位置:"), 6, 0) + content_pos_layout = QHBoxLayout() + content_pos_layout.setContentsMargins(0, 0, 0, 0) + content_pos_layout.setSpacing(4) + + content_x = QLabel("X:") + content_x.setAlignment(Qt.AlignVCenter) + content_x.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + content_pos_layout.addWidget(content_x) content_x_spin = QDoubleSpinBox() content_x_spin.setRange(-1.0, 1.0) content_x_spin.setSingleStep(0.01) content_x_spin.setValue(current_content_pos[0]) - content_layout.addWidget(content_x_spin, 7, 1) + content_pos_layout.addWidget(content_x_spin) - content_layout.addWidget(QLabel("Y:"), 7, 2) + content_y = QLabel("Y:") + content_y.setAlignment(Qt.AlignVCenter) + content_y.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + content_pos_layout.addWidget(content_y) content_y_spin = QDoubleSpinBox() content_y_spin.setRange(-1.0, 1.0) content_y_spin.setSingleStep(0.01) content_y_spin.setValue(current_content_pos[1]) - content_layout.addWidget(content_y_spin, 7, 3) + content_pos_layout.addWidget(content_y_spin) + + content_layout.addLayout(content_pos_layout, 6, 1, 1, 3) # 连接内容属性变化信号 def onContentTextChanged(): @@ -3703,7 +3928,7 @@ class PropertyPanelManager: current_radius = float(spherical_video.getTag("original_radius") or "5.0") radius_spin.setValue(current_radius) radius_spin.valueChanged.connect(lambda value: self._updateSphericalVideoRadius(spherical_video, value)) - sphere_layout.addWidget(radius_spin, 0, 1) + sphere_layout.addWidget(radius_spin, 0, 1, 1, 3) sphere_group.setLayout(sphere_layout) self._propertyLayout.addWidget(sphere_group) @@ -3948,6 +4173,7 @@ class PropertyPanelManager: video_info_group = QGroupBox("视频信息") video_info_layout = QGridLayout() + video_info_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 显示当前视频文件路径 - 改进版本 video_path = video_screen.getTag("video_path") if video_screen.hasTag("video_path") else "" @@ -3964,70 +4190,75 @@ class PropertyPanelManager: display_path = video_path path_label.setText(display_path) path_label.setStyleSheet("color: #00AAFF;") - video_info_layout.addWidget(path_label, 0, 1) + video_info_layout.addWidget(path_label, 0, 1, 1, 3) elif os.path.exists(video_path): # 显示本地文件信息 video_info_layout.addWidget(QLabel("视频文件:"), 0, 0) path_label = QLabel(os.path.basename(video_path)) path_label.setWordWrap(True) path_label.setStyleSheet("color: #00AAFF;") - video_info_layout.addWidget(path_label, 0, 1) + video_info_layout.addWidget(path_label, 0, 1, 1, 3) else: # 文件不存在 video_info_layout.addWidget(QLabel("状态:"), 0, 0) status_label = QLabel("文件不存在") status_label.setStyleSheet("color: red;") - video_info_layout.addWidget(status_label, 0, 1) + video_info_layout.addWidget(status_label, 0, 1, 1, 3) else: # 无视频 video_info_layout.addWidget(QLabel("状态:"), 0, 0) status_label = QLabel("无视频文件") status_label.setStyleSheet("color: red;") - video_info_layout.addWidget(status_label, 0, 1) + video_info_layout.addWidget(status_label, 0, 1, 1, 3) video_info_group.setLayout(video_info_layout) self._propertyLayout.addWidget(video_info_group) video_control_group = QGroupBox("视频控制") - video_control_layout = QHBoxLayout() + video_control_layout = QGridLayout() + video_control_layout.setColumnMinimumWidth(0, self.column_minimum_width) + video_control_h_layout = QHBoxLayout() # 播放按钮 play_btn = QPushButton("▶️ 播放") play_btn.clicked.connect(lambda: self.world.gui_manager.playVideo(video_screen)) - video_control_layout.addWidget(play_btn) + video_control_h_layout.addWidget(play_btn) # 暂停按钮 pause_btn = QPushButton("⏸️ 暂停") pause_btn.clicked.connect(lambda: self.world.gui_manager.pauseVideo(video_screen)) - video_control_layout.addWidget(pause_btn) + video_control_h_layout.addWidget(pause_btn) # 停止按钮 stop_btn = QPushButton("⏹️ 停止") stop_btn.clicked.connect(lambda: self.world.gui_manager.stopVideo(video_screen)) - video_control_layout.addWidget(stop_btn) + video_control_h_layout.addWidget(stop_btn) - video_control_group.setLayout(video_control_layout) - self._propertyLayout.addWidget(video_control_group) + video_control_layout.addLayout(video_control_h_layout, 0, 0, 1, 4) # 加载新视频按钮 load_btn = QPushButton("📁 加载新视频...") load_btn.clicked.connect(lambda: self._loadNewVideo(video_screen, item)) - self._propertyLayout.addWidget(load_btn) + video_control_layout.addWidget(load_btn, 1, 0, 1, 4) + + video_control_group.setLayout(video_control_layout) + self._propertyLayout.addWidget(video_control_group) # 添加URL输入区域 url_group = QGroupBox("网络视频") - url_layout = QHBoxLayout() + url_layout = QGridLayout() + url_layout.setColumnMinimumWidth(0, self.column_minimum_width) self.url_input = QLineEdit() self.url_input.setPlaceholderText("输入视频流URL") - url_layout.addWidget(self.url_input) + url_layout.addWidget(self.url_input, 0, 0, 1, 3) load_url_btn = QPushButton("加载URL") self._current_load_url_btn_3d = load_url_btn load_url_btn.clicked.connect( lambda: self._loadVideoFromURLWithOpenCV_3D(video_screen, self.url_input.text().strip())) - url_layout.addWidget(load_url_btn) + url_layout.addWidget(load_url_btn, 0, 3) url_group.setLayout(url_layout) self._propertyLayout.addWidget(url_group) @@ -4045,6 +4276,7 @@ class PropertyPanelManager: # 视频信息组 video_info_group = QGroupBox("2D视频信息") video_info_layout = QGridLayout() + video_info_layout.setColumnMinimumWidth(0, self.column_minimum_width) # 显示当前视频文件路径 # 在显示视频信息时区分本地文件和网络URL @@ -4056,26 +4288,26 @@ class PropertyPanelManager: path_label = QLabel(video_path) path_label.setWordWrap(True) path_label.setStyleSheet("color: #00AAFF;") - video_info_layout.addWidget(path_label, 0, 1) + video_info_layout.addWidget(path_label, 0, 1, 1, 3) elif os.path.exists(video_path): # 显示本地文件信息 video_info_layout.addWidget(QLabel("视频文件:"), 0, 0) path_label = QLabel(os.path.basename(video_path)) path_label.setWordWrap(True) path_label.setStyleSheet("color: #00AAFF;") - video_info_layout.addWidget(path_label, 0, 1) + video_info_layout.addWidget(path_label, 0, 1, 1, 3) else: # 文件不存在 video_info_layout.addWidget(QLabel("状态:"), 0, 0) status_label = QLabel("文件不存在") status_label.setStyleSheet("color: red;") - video_info_layout.addWidget(status_label, 0, 1) + video_info_layout.addWidget(status_label, 0, 1, 1, 3) else: # 无视频 video_info_layout.addWidget(QLabel("状态:"), 0, 0) status_label = QLabel("无视频文件") status_label.setStyleSheet("color: red;") - video_info_layout.addWidget(status_label, 0, 1) + video_info_layout.addWidget(status_label, 0, 1, 1, 3) video_screen.setBin("fixed", 0) @@ -4084,45 +4316,48 @@ class PropertyPanelManager: # 视频控制组 video_control_group = QGroupBox("2D视频控制") - video_control_layout = QHBoxLayout() + video_control_layout = QGridLayout() + video_control_h_layout = QHBoxLayout() # 播放按钮 play_btn = QPushButton("▶️ 播放") play_btn.clicked.connect(lambda: self.play2DVideo(video_screen)) - video_control_layout.addWidget(play_btn) + video_control_h_layout.addWidget(play_btn) # 暂停按钮 pause_btn = QPushButton("⏸️ 暂停") pause_btn.clicked.connect(lambda: self.pause2DVideo(video_screen)) - video_control_layout.addWidget(pause_btn) + video_control_h_layout.addWidget(pause_btn) # 停止按钮 stop_btn = QPushButton("⏹️ 停止") stop_btn.clicked.connect(lambda: self._stop2DVideo(video_screen)) - video_control_layout.addWidget(stop_btn) + video_control_h_layout.addWidget(stop_btn) + + video_control_layout.addLayout(video_control_h_layout, 0, 0, 1, 4) video_control_group.setLayout(video_control_layout) - self._propertyLayout.addWidget(video_control_group) # 加载新视频按钮 load_btn = QPushButton("📁 加载新视频...") load_btn.clicked.connect(lambda: self._loadNew2DVideo(video_screen, item)) - self._propertyLayout.addWidget(load_btn) + video_control_layout.addWidget(load_btn, 1, 0, 1, 4) + self._propertyLayout.addWidget(video_control_group) # 添加URL输入区域 url_group = QGroupBox("网络视频") - url_layout = QHBoxLayout() + url_layout = QGridLayout() self.url_input = QLineEdit() self.url_input.setPlaceholderText("输入视频流URL") - url_layout.addWidget(self.url_input) + url_layout.addWidget(self.url_input, 0, 0, 1, 3) load_url_btn = QPushButton("加载URL") self._current_load_url_btn_2d = load_url_btn load_url_btn.clicked.connect( lambda: self._loadVideoFromURLWithOpenCV(video_screen, self.url_input.text().strip())) - url_layout.addWidget(load_url_btn) + url_layout.addWidget(load_url_btn, 0, 3) url_group.setLayout(url_layout) self._propertyLayout.addWidget(url_group) @@ -5072,123 +5307,137 @@ class PropertyPanelManager: # 变换属性组 transform_group = QGroupBox("变换 Transform") transform_layout = QGridLayout() + transform_layout.setColumnMinimumWidth(0, self.column_minimum_width) + + transform_layout.addWidget(QLabel("位置"), 0, 0) # 位置属性 current_pos = light_object.pos - transform_layout.addWidget(QLabel("位置"), 0, 0) - - # X, Y, Z 标签居中 - x_label = QLabel("X") - y_label = QLabel("Y") - z_label = QLabel("Z") - x_label.setAlignment(Qt.AlignCenter) - y_label.setAlignment(Qt.AlignCenter) - z_label.setAlignment(Qt.AlignCenter) - - transform_layout.addWidget(x_label, 0, 1) - transform_layout.addWidget(y_label, 0, 2) - transform_layout.addWidget(z_label, 0, 3) + position_row = QHBoxLayout() + position_row.setContentsMargins(0, 0, 0, 0) + position_row.setSpacing(4) xPos = QDoubleSpinBox() xPos.setRange(-1000, 1000) xPos.setValue(current_pos.getX()) xPos.valueChanged.connect(lambda v: self._updateLightPosition(light_object, model, 'x', v)) - transform_layout.addWidget(xPos, 1, 1) yPos = QDoubleSpinBox() yPos.setRange(-1000, 1000) yPos.setValue(current_pos.getY()) yPos.valueChanged.connect(lambda v: self._updateLightPosition(light_object, model, 'y', v)) - transform_layout.addWidget(yPos, 1, 2) zPos = QDoubleSpinBox() zPos.setRange(-1000, 1000) zPos.setValue(current_pos.getZ()) zPos.valueChanged.connect(lambda v: self._updateLightPosition(light_object, model, 'z', v)) - transform_layout.addWidget(zPos, 1, 3) - # 世界位置(只读) + for axis_label, widget in (("X:", xPos), ("Y:", yPos), ("Z:", zPos)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + position_row.addWidget(axis) + position_row.addWidget(widget) + + transform_layout.addLayout(position_row, 0, 1, 1, 3) + worldPos = model.getPos(self.world.render) - transform_layout.addWidget(QLabel("世界位置"), 2, 0) + world_position_row = QHBoxLayout() + world_position_row.setContentsMargins(0, 0, 0, 0) + world_position_row.setSpacing(4) + transform_layout.addWidget(QLabel("世界位置"), 1, 0) worldXPos = QDoubleSpinBox() worldXPos.setRange(-1000, 1000) worldXPos.setValue(worldPos.getX()) worldXPos.setReadOnly(True) - transform_layout.addWidget(worldXPos, 3, 1) worldYPos = QDoubleSpinBox() worldYPos.setRange(-1000, 1000) worldYPos.setValue(worldPos.getY()) worldYPos.setReadOnly(True) - transform_layout.addWidget(worldYPos, 3, 2) worldZPos = QDoubleSpinBox() worldZPos.setRange(-1000, 1000) worldZPos.setValue(worldPos.getZ()) worldZPos.setReadOnly(True) - transform_layout.addWidget(worldZPos, 3, 3) + + for axis_label, widget in (("X:", worldXPos), ("Y:", worldYPos), ("Z:", worldZPos)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + world_position_row.addWidget(axis) + world_position_row.addWidget(widget) + + transform_layout.addLayout(world_position_row, 1, 1, 1, 3) # 旋转属性(仅聚光灯) + next_row = 2 if hasattr(light_object, 'direction'): current_hpr = model.getHpr() - transform_layout.addWidget(QLabel("旋转"), 4, 0) - - # H, P, R 标签居中 - h_label = QLabel("H") - p_label = QLabel("P") - r_label = QLabel("R") - h_label.setAlignment(Qt.AlignCenter) - p_label.setAlignment(Qt.AlignCenter) - r_label.setAlignment(Qt.AlignCenter) - - transform_layout.addWidget(h_label, 4, 1) - transform_layout.addWidget(p_label, 4, 2) - transform_layout.addWidget(r_label, 4, 3) + rotation_row = QHBoxLayout() + rotation_row.setContentsMargins(0, 0, 0, 0) + rotation_row.setSpacing(4) + transform_layout.addWidget(QLabel("旋转"), next_row, 0) hRot = QDoubleSpinBox() hRot.setRange(-180, 180) hRot.setValue(current_hpr.getX()) hRot.valueChanged.connect(lambda v: self._updateLightRotation(light_object, model, 'h', v)) - transform_layout.addWidget(hRot, 5, 1) pRot = QDoubleSpinBox() pRot.setRange(-180, 180) pRot.setValue(current_hpr.getY()) pRot.valueChanged.connect(lambda v: self._updateLightRotation(light_object, model, 'p', v)) - transform_layout.addWidget(pRot, 5, 2) rRot = QDoubleSpinBox() rRot.setRange(-180, 180) rRot.setValue(current_hpr.getZ()) rRot.valueChanged.connect(lambda v: self._updateLightRotation(light_object, model, 'r', v)) - transform_layout.addWidget(rRot, 5, 3) + + for axis_label, widget in (("H:", hRot), ("P:", pRot), ("R:", rRot)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + rotation_row.addWidget(axis) + rotation_row.addWidget(widget) + + transform_layout.addLayout(rotation_row, next_row, 1, 1, 3) + next_row += 1 # 缩放属性 current_scale = model.getScale() - scale_row = 6 if hasattr(light_object, 'direction') else 4 - transform_layout.addWidget(QLabel("缩放"), scale_row, 0) + scale_row = QHBoxLayout() + scale_row.setContentsMargins(0, 0, 0, 0) + scale_row.setSpacing(4) + transform_layout.addWidget(QLabel("缩放"), next_row, 0) xScaleSpinBox = QDoubleSpinBox() xScaleSpinBox.setRange(0.01, 100) xScaleSpinBox.setSingleStep(0.1) xScaleSpinBox.setValue(current_scale.getX()) xScaleSpinBox.valueChanged.connect(lambda v: self._updateLightScale(model, 'x', v)) - transform_layout.addWidget(xScaleSpinBox, scale_row + 1, 1) yScaleSpinBox = QDoubleSpinBox() yScaleSpinBox.setRange(0.01, 100) yScaleSpinBox.setSingleStep(0.1) yScaleSpinBox.setValue(current_scale.getY()) yScaleSpinBox.valueChanged.connect(lambda v: self._updateLightScale(model, 'y', v)) - transform_layout.addWidget(yScaleSpinBox, scale_row + 1, 2) zScaleSpinBox = QDoubleSpinBox() zScaleSpinBox.setRange(0.01, 100) zScaleSpinBox.setSingleStep(0.1) zScaleSpinBox.setValue(current_scale.getZ()) zScaleSpinBox.valueChanged.connect(lambda v: self._updateLightScale(model, 'z', v)) - transform_layout.addWidget(zScaleSpinBox, scale_row + 1, 3) + + for axis_label, widget in (("X:", xScaleSpinBox), ("Y:", yScaleSpinBox), ("Z:", zScaleSpinBox)): + axis = QLabel(axis_label) + axis.setAlignment(Qt.AlignVCenter) + axis.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + scale_row.addWidget(axis) + scale_row.addWidget(widget) + + transform_layout.addLayout(scale_row, next_row, 1, 1, 3) transform_group.setLayout(transform_layout) self._propertyLayout.addWidget(transform_group) @@ -5203,7 +5452,7 @@ class PropertyPanelManager: energySpinBox.setRange(0, 10000) energySpinBox.setValue(light_object.energy) energySpinBox.valueChanged.connect(lambda v: self._updateLightEnergy(light_object, v)) - light_layout.addWidget(energySpinBox, 0, 1) + light_layout.addWidget(energySpinBox, 0, 1, 1, 3) # 半径 light_layout.addWidget(QLabel("半径:"), 1, 0) @@ -5211,7 +5460,7 @@ class PropertyPanelManager: radiusSpinBox.setRange(1, 2000) radiusSpinBox.setValue(light_object.radius) radiusSpinBox.valueChanged.connect(lambda v: self._updateLightRadius(light_object, v)) - light_layout.addWidget(radiusSpinBox, 1, 1) + light_layout.addWidget(radiusSpinBox, 1, 1, 1, 3) # 视野角度(仅聚光灯) if hasattr(light_object, 'fov'): @@ -5220,7 +5469,7 @@ class PropertyPanelManager: fovSpinBox.setRange(1, 180) fovSpinBox.setValue(light_object.fov) fovSpinBox.valueChanged.connect(lambda v: self._updateLightFOV(light_object, v)) - light_layout.addWidget(fovSpinBox, 2, 1) + light_layout.addWidget(fovSpinBox, 2, 1, 1, 3) light_group.setLayout(light_layout) self._propertyLayout.addWidget(light_group) @@ -5419,43 +5668,45 @@ class PropertyPanelManager: if base_color is not None: print(f"材质基础颜色: {base_color}") - # 基础颜色标题 + # 基础颜色编辑 color_row = 2 if material_status != "标准PBR材质" else 1 - material_layout.addWidget(QLabel("基础颜色"), color_row, 0) - # R, G, B 标签 - r_label = QLabel("R") - g_label = QLabel("G") - b_label = QLabel("B") - r_label.setAlignment(Qt.AlignCenter) - g_label.setAlignment(Qt.AlignCenter) - b_label.setAlignment(Qt.AlignCenter) - - material_layout.addWidget(r_label, color_row, 1) - material_layout.addWidget(g_label, color_row, 2) - material_layout.addWidget(b_label, color_row, 3) - - # RGB 数值框 r_spinbox = QDoubleSpinBox() r_spinbox.setRange(0.0, 1.0) r_spinbox.setSingleStep(0.01) r_spinbox.setValue(base_color.x) r_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialBaseColor(mat, 'r', v)) - material_layout.addWidget(r_spinbox, color_row + 1, 1) g_spinbox = QDoubleSpinBox() g_spinbox.setRange(0.0, 1.0) g_spinbox.setSingleStep(0.01) g_spinbox.setValue(base_color.y) g_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialBaseColor(mat, 'g', v)) - material_layout.addWidget(g_spinbox, color_row + 1, 2) b_spinbox = QDoubleSpinBox() b_spinbox.setRange(0.0, 1.0) b_spinbox.setSingleStep(0.01) b_spinbox.setValue(base_color.z) b_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialBaseColor(mat, 'b', v)) - material_layout.addWidget(b_spinbox, color_row + 1, 3) + + title_label = QLabel("基础颜色") + material_layout.addWidget(title_label, color_row, 0) + + base_color_layout = QHBoxLayout() + base_color_layout.setContentsMargins(0, 0, 0, 0) + base_color_layout.setSpacing(4) + + for label_text, spinbox in (("R:", r_spinbox), ("G:", g_spinbox), ("B:", b_spinbox)): + channel_label = QLabel(label_text) + channel_label.setAlignment(Qt.AlignVCenter) + channel_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + base_color_layout.addWidget(channel_label) + spinbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + base_color_layout.addWidget(spinbox) + + # base_color_layout.addStretch(1) + + material_layout.addLayout(base_color_layout, color_row, 1, 1, 4) else: no_base_color_label = QLabel("无法获取材质基础颜色") no_base_color_label.setStyleSheet("color:#888;font-style:italic;font-size:10px;") @@ -5463,7 +5714,7 @@ class PropertyPanelManager: material_layout.addWidget(no_base_color_label, 1, 1, 1, 3) # 材质属性行 - current_row = color_row + 2 if base_color is not None else 2 + current_row = color_row + 1 if base_color is not None else 2 # 粗糙度 if hasattr(material, 'roughness') and material.roughness is not None: @@ -5476,18 +5727,18 @@ class PropertyPanelManager: roughness_spinbox.setValue(roughness_value) roughness_spinbox.valueChanged.connect( lambda v, mat=material: self._updateMaterialRoughness(mat, v)) - material_layout.addWidget(roughness_spinbox, current_row, 1) + material_layout.addWidget(roughness_spinbox, current_row, 1, 1, 1) except (TypeError, ValueError) as e: print(f"粗糙度值无效: {material.roughness}, 错误: {e}") material_layout.addWidget(QLabel("粗糙度:"), current_row, 0) no_roughness_label = QLabel("粗糙度值无效") no_roughness_label.setStyleSheet("color:#888;font-style:italic;font-size:10px;") - material_layout.addWidget(no_roughness_label, current_row, 1) + material_layout.addWidget(no_roughness_label, current_row, 1, 1, 1) else: material_layout.addWidget(QLabel("粗糙度:"), current_row, 0) no_roughness_label = QLabel("不支持") no_roughness_label.setStyleSheet("color:#888;font-style:italic;font-size:10px;") - material_layout.addWidget(no_roughness_label, current_row, 1) + material_layout.addWidget(no_roughness_label, current_row, 1, 1, 1) current_row += 1 @@ -5501,18 +5752,18 @@ class PropertyPanelManager: metallic_spinbox.setSingleStep(0.01) metallic_spinbox.setValue(metallic_value) metallic_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialMetallic(mat, v)) - material_layout.addWidget(metallic_spinbox, current_row, 1) + material_layout.addWidget(metallic_spinbox, current_row, 1, 1, 1) except (TypeError, ValueError) as e: print(f"金属性值无效: {material.metallic}, 错误: {e}") material_layout.addWidget(QLabel("金属性:"), current_row, 0) no_metallic_label = QLabel("值无效") no_metallic_label.setStyleSheet("color:#888;font-style:italic;font-size:10px;") - material_layout.addWidget(no_metallic_label, current_row, 1) + material_layout.addWidget(no_metallic_label, current_row, 1, 1, 1) else: material_layout.addWidget(QLabel("金属性:"), current_row, 0) no_metallic_label = QLabel("不支持") no_metallic_label.setStyleSheet("color:#888;font-style:italic;font-size:10px;") - material_layout.addWidget(no_metallic_label, current_row, 1) + material_layout.addWidget(no_metallic_label, current_row, 1, 1, 1) current_row += 1 @@ -5526,18 +5777,18 @@ class PropertyPanelManager: ior_spinbox.setSingleStep(0.01) ior_spinbox.setValue(ior_value) ior_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialIOR(mat, v)) - material_layout.addWidget(ior_spinbox, current_row, 1) + material_layout.addWidget(ior_spinbox, current_row, 1, 1, 1) except (TypeError, ValueError) as e: print(f"折射率值无效: {material.refractive_index}, 错误: {e}") material_layout.addWidget(QLabel("折射率:"), current_row, 0) no_ior_label = QLabel("折射率值无效") no_ior_label.setStyleSheet("color:#888;font-style:italic;font-size:10px;") - material_layout.addWidget(no_ior_label, current_row, 1) + material_layout.addWidget(no_ior_label, current_row, 1, 1, 1) else: material_layout.addWidget(QLabel("折射率:"), current_row, 0) no_ior_label = QLabel("此材质不支持折射率编辑") no_ior_label.setStyleSheet("color:#888;font-style:italic;font-size:10px;") - material_layout.addWidget(no_ior_label, current_row, 1) + material_layout.addWidget(no_ior_label, current_row, 1, 1, 1) current_row += 1 @@ -9353,6 +9604,11 @@ except Exception as e: # 创建碰撞检测组 collision_group = QGroupBox("碰撞检测") collision_layout = QGridLayout() + collision_layout.setColumnMinimumWidth(0, self.column_minimum_width) + # collision_layout.setColumnStretch(0, 0) + # collision_layout.setColumnStretch(1, 1) + # collision_layout.setColumnStretch(2, 1) + # collision_layout.setColumnStretch(3, 1) # 检查模型是否已有碰撞 has_collision = self._hasCollision(model) @@ -9369,16 +9625,24 @@ except Exception as e: header_layout.setContentsMargins(0, 0, 0, 0) header_layout.setSpacing(8) + # 调整列宽,保证控件对齐 + # collision_layout.setColumnStretch(0, 0) + # collision_layout.setColumnStretch(1, 0) + # collision_layout.setColumnStretch(2, 0) + # collision_layout.setColumnStretch(3, 0) + # 碰撞状态行 + status_layout = QHBoxLayout() status_label = QLabel("状态:") - collision_layout.addWidget(status_label, 0, 0) + status_layout.addWidget(status_label) # 状态徽章(使用固定宽度样式) if has_collision: self.collision_status_badge = self.createFixedStatusBadge("已启用", "green") else: self.collision_status_badge = self.createFixedStatusBadge("未启用", "red") - collision_layout.addWidget(self.collision_status_badge, 0, 1) + status_layout.addWidget(self.collision_status_badge) + collision_layout.addLayout(status_layout, 0, 0, 1, 1) # 形状选择标签(始终显示) self.collision_shape_label = QLabel("碰撞形状:") @@ -9393,7 +9657,7 @@ except Exception as e: "平面 (Plane)", "自动选择 (Auto)" ]) - collision_layout.addWidget(self.collision_shape_combo, 1, 1) + collision_layout.addWidget(self.collision_shape_combo, 1, 1, 1, 3) # 保存布局引用,用于动态添加/移除控件 self.collision_layout = collision_layout @@ -9416,15 +9680,19 @@ except Exception as e: # 显示/隐藏切换按钮(使用现代化样式) visibility_text = "隐藏碰撞" if is_collision_visible else "显示碰撞" - self.collision_visibility_button = self.createModernButton(visibility_text, "primary") + self.collision_visibility_button = self.createModernButton(visibility_text) + self.collision_visibility_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.collision_visibility_button.setStyleSheet(self._collision_button_style()) self.collision_visibility_button.clicked.connect(lambda: self._toggleCollisionVisibility(model)) - collision_layout.addWidget(self.collision_visibility_button, current_row, 0, 1, 2) + collision_layout.addWidget(self.collision_visibility_button, current_row, 0, 1, 4) current_row += 1 # 移除碰撞按钮(使用现代化样式) - self.collision_button = self.createModernButton("移除碰撞", "danger") + self.collision_button = self.createModernButton("移除碰撞") + self.collision_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.collision_button.setStyleSheet(self._collision_button_style()) self.collision_button.clicked.connect(lambda: self._removeCollisionAndUpdate(model)) - collision_layout.addWidget(self.collision_button, current_row, 0, 1, 2) + collision_layout.addWidget(self.collision_button, current_row, 0, 1, 4) else: # 如果没有碰撞,设置默认选择并允许编辑 self.collision_shape_combo.setCurrentText("球形 (Sphere)") @@ -9438,9 +9706,11 @@ except Exception as e: self.collision_visibility_button.setVisible(False) # 添加碰撞按钮(使用现代化样式) - self.collision_button = self.createModernButton("添加碰撞", "success") + self.collision_button = self.createModernButton("添加碰撞") + self.collision_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.collision_button.setStyleSheet(self._collision_button_style()) self.collision_button.clicked.connect(lambda: self._addCollisionAndUpdate(model)) - collision_layout.addWidget(self.collision_button, current_row, 0, 1, 2) + collision_layout.addWidget(self.collision_button, current_row, 0, 1, 4) collision_group.setLayout(collision_layout) self._propertyLayout.addWidget(collision_group) @@ -9456,32 +9726,47 @@ except Exception as e: # 位置调整控件(所有类型都有) pos_label = QLabel("位置偏移:") - layout.addWidget(pos_label, current_row, 0) - current_row += 1 + pos_label.setAlignment(Qt.AlignLeft | Qt.AlignTop) + pos_label.setVisible(True) # 确保可见 # X, Y, Z 位置调整 - self.collision_pos_x = self._createCollisionSpinBox(-1000000, 1000000, 2) - self.collision_pos_y = self._createCollisionSpinBox(-1000000, 1000000, 2) - self.collision_pos_z = self._createCollisionSpinBox(-1000000, 1000000, 2) + self.collision_pos_x = self._createCollisionSpinBox(-100, 100, 2) + self.collision_pos_y = self._createCollisionSpinBox(-100, 100, 2) + self.collision_pos_z = self._createCollisionSpinBox(-100, 100, 2) - # 只在没有现有碰撞时设置默认值,否则由_loadCurrentCollisionParameters加载实际值 - if not self._hasCollision(model): - # 设置默认位置偏移(无偏移) - self.collision_pos_x.setValue(0.0) - self.collision_pos_y.setValue(0.0) - self.collision_pos_z.setValue(0.0) + for spinbox in (self.collision_pos_x, self.collision_pos_y, self.collision_pos_z): + spinbox.setMinimumWidth(140) + spinbox.setVisible(True) - layout.addWidget(QLabel("X:"), current_row, 0) - layout.addWidget(self.collision_pos_x, current_row, 1) - current_row += 1 + axis_controls = [ + ("X:", self.collision_pos_x), + ("Y:", self.collision_pos_y), + ("Z:", self.collision_pos_z), + ] - layout.addWidget(QLabel("Y:"), current_row, 0) - layout.addWidget(self.collision_pos_y, current_row, 1) - current_row += 1 + for offset, (axis_text, spinbox) in enumerate(axis_controls): + row = current_row + offset + axis_layout = QHBoxLayout() + if offset == 0: + layout.addWidget(pos_label, row, 0) + # axis_label.addWidget(pos_label) + else: + spacer = QLabel("") + spacer.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + spacer.setVisible(True) + layout.addWidget(spacer, row, 0) + # axis_layout.addWidget(spacer) - layout.addWidget(QLabel("Z:"), current_row, 0) - layout.addWidget(self.collision_pos_z, current_row, 1) - current_row += 1 + axis_label = QLabel(axis_text) + axis_label.setAlignment(Qt.AlignVCenter) + axis_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + axis_layout.addWidget(axis_label) + axis_layout.addWidget(spinbox) + # layout.addWidget(axis_label, row, 1) + # layout.addWidget(spinbox, row, 2, 1, 2) + layout.addLayout(axis_layout, row, 1, 1, 3) + + current_row += len(axis_controls) # 连接位置变化信号 self.collision_pos_x.valueChanged.connect(lambda v: self._updateCollisionPosition(model, 'x', v)) @@ -9522,6 +9807,7 @@ except Exception as e: # 半径调整 radius_label = QLabel("半径:") layout.addWidget(radius_label, current_row, 0) + # layout.addWidget(QLabel(""), current_row, 1) self.collision_radius = self._createCollisionSpinBox(0.01, 100000, 2) @@ -9541,7 +9827,7 @@ except Exception as e: self.collision_radius.setValue(default_radius) self.collision_radius.valueChanged.connect(lambda v: self._updateSphereRadius(model, v)) - layout.addWidget(self.collision_radius, current_row, 1) + layout.addWidget(self.collision_radius, current_row, 1, 1, 3) current_row += 1 return current_row @@ -9552,7 +9838,8 @@ except Exception as e: size_label = QLabel("尺寸:") layout.addWidget(size_label, current_row, 0) - current_row += 1 + # layout.addWidget(QLabel(""), current_row, 1) + # current_row += 1 # 宽度、长度、高度 self.collision_width = self._createCollisionSpinBox(0.001, 100000, 2) @@ -9578,17 +9865,33 @@ except Exception as e: self.collision_length.setValue(model_size.y) self.collision_height.setValue(model_size.z) - layout.addWidget(QLabel("宽度:"), current_row, 0) - layout.addWidget(self.collision_width, current_row, 1) - current_row += 1 + box_controls = [ + ("宽度:", self.collision_width), + ("长度:", self.collision_length), + ("高度:", self.collision_height), + ] - layout.addWidget(QLabel("长度:"), current_row, 0) - layout.addWidget(self.collision_length, current_row, 1) - current_row += 1 + for offset, (text, spinbox) in enumerate(box_controls): + text_layout = QHBoxLayout() + if offset == 0: + layout.addWidget(size_label, current_row, 0) + else: + spacer = QLabel("") + spacer.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + spacer.setVisible(True) + layout.addWidget(spacer, current_row, 0) + # placeholder = QLabel("") + # layout.addWidget(placeholder, current_row, 0) - layout.addWidget(QLabel("高度:"), current_row, 0) - layout.addWidget(self.collision_height, current_row, 1) - current_row += 1 + axis_label = QLabel(text) + axis_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + text_layout.addWidget(axis_label) + text_layout.addWidget(spinbox) + layout.addLayout(text_layout, current_row, 1, 1, 3) + # layout.addWidget(axis_label, current_row, 1) + # layout.addWidget(spinbox, current_row, 2) + current_row += 1 # 连接信号 self.collision_width.valueChanged.connect(lambda v: self._updateBoxSize(model, 'width', v)) @@ -9604,6 +9907,7 @@ except Exception as e: # 半径和高度 radius_label = QLabel("半径:") layout.addWidget(radius_label, current_row, 0) + # layout.addWidget(QLabel(""), current_row, 1) self.collision_capsule_radius = self._createCollisionSpinBox(0.01, 100000, 2) @@ -9627,11 +9931,12 @@ except Exception as e: self.collision_capsule_radius.setValue(default_radius) self.collision_capsule_radius.valueChanged.connect(lambda v: self._updateCapsuleRadius(model, v)) - layout.addWidget(self.collision_capsule_radius, current_row, 1) + layout.addWidget(self.collision_capsule_radius, current_row, 1, 1, 3) current_row += 1 height_label = QLabel("高度:") layout.addWidget(height_label, current_row, 0) + # layout.addWidget(QLabel(""), current_row, 1) self.collision_capsule_height = self._createCollisionSpinBox(0.01, 10000, 2) @@ -9651,7 +9956,7 @@ except Exception as e: self.collision_capsule_height.setValue(model_size.z) self.collision_capsule_height.valueChanged.connect(lambda v: self._updateCapsuleHeight(model, v)) - layout.addWidget(self.collision_capsule_height, current_row, 1) + layout.addWidget(self.collision_capsule_height, current_row, 1, 1, 3) current_row += 1 return current_row @@ -9663,7 +9968,8 @@ except Exception as e: # 法向量 normal_label = QLabel("法向量:") layout.addWidget(normal_label, current_row, 0) - current_row += 1 + # layout.addWidget(QLabel(""), current_row, 1) + # current_row += 1 self.collision_normal_x = self._createCollisionSpinBox(-1, 1, 2) self.collision_normal_y = self._createCollisionSpinBox(-1, 1, 2) @@ -9676,16 +9982,40 @@ except Exception as e: self.collision_normal_y.setValue(0.0) self.collision_normal_z.setValue(1.0) - layout.addWidget(QLabel("Nx:"), current_row, 0) - layout.addWidget(self.collision_normal_x, current_row, 1) + # placeholder_nx = QLabel("") + # layout.addWidget(placeholder_nx, current_row, 0) + + hbox_nx = QHBoxLayout() + axis_label_nx = QLabel("Nx:") + axis_label_nx.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis_label_nx.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + hbox_nx.addWidget(axis_label_nx) + hbox_nx.addWidget(self.collision_normal_x) + layout.addLayout(hbox_nx, current_row, 1, 1, 3) current_row += 1 - layout.addWidget(QLabel("Ny:"), current_row, 0) - layout.addWidget(self.collision_normal_y, current_row, 1) + placeholder_ny = QLabel("") + layout.addWidget(placeholder_ny, current_row, 0) + + hbox_ny = QHBoxLayout() + axis_label_ny = QLabel("Ny:") + axis_label_ny.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis_label_ny.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + hbox_ny.addWidget(axis_label_ny) + hbox_ny.addWidget(self.collision_normal_y) + layout.addLayout(hbox_ny, current_row, 1, 1, 3) current_row += 1 - layout.addWidget(QLabel("Nz:"), current_row, 0) - layout.addWidget(self.collision_normal_z, current_row, 1) + placeholder_nz = QLabel("") + layout.addWidget(placeholder_nz, current_row, 0) + + hbox_nz = QHBoxLayout() + axis_label_nz = QLabel("Nz:") + axis_label_nz.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis_label_nz.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + hbox_nz.addWidget(axis_label_nz) + hbox_nz.addWidget(self.collision_normal_z) + layout.addLayout(hbox_nz, current_row, 1, 1, 3) current_row += 1 # 连接信号 @@ -9752,28 +10082,8 @@ except Exception as e: button_text = "隐藏碰撞" if is_visible else "显示碰撞" self.collision_visibility_button.setText(button_text) - # 应用主要按钮样式 - self.collision_visibility_button.setStyleSheet(""" - QPushButton { - background-color: rgba(77, 116, 189, 0.8); - color: #ffffff; - border: 1px solid #4d74bd; - border-radius: 4px; - padding: 8px 12px; - font-family: 'Microsoft YaHei', 'Inter', sans-serif; - font-size: 10px; - font-weight: 500; - min-height: 16px; - } - QPushButton:hover { - background-color: rgba(77, 116, 189, 1.0); - border: 1px solid rgba(77, 116, 189, 1.0); - } - QPushButton:pressed { - background-color: rgba(61, 96, 169, 1.0); - border: 1px solid rgba(61, 96, 169, 1.0); - } - """) + # 应用统一按钮样式 + self.collision_visibility_button.setStyleSheet(self._collision_button_style()) print(f"更新可见性按钮:{model.getName()} - {'可见' if is_visible else '隐藏'}") except Exception as e: @@ -9961,10 +10271,11 @@ except Exception as e: return self._updating_collision_panel = True - + print("-------------------------------------") if hasattr(self, 'collision_button') and hasattr(self, 'collision_status_badge') and hasattr(self, 'collision_shape_combo'): has_collision = self._hasCollision(model) + print(f"模型 {model.getName()} 是否有碰撞体: {has_collision}-------------------------------------------------") # 更新状态徽章(使用固定宽度) if has_collision: @@ -9983,7 +10294,7 @@ except Exception as e: # 移除旧徽章并添加新徽章 parent_layout.removeWidget(old_badge) old_badge.deleteLater() - parent_layout.addWidget(new_badge, 0, 1) # 状态徽章在第0行第1列 + parent_layout.addWidget(new_badge, 0, 0, 1, 1) # 状态徽章在第0行第1列 self.collision_status_badge = new_badge break @@ -9992,28 +10303,8 @@ except Exception as e: if hasattr(self, 'collision_button'): # 更新按钮文本和样式(简单方法:直接设置文本和样式) self.collision_button.setText("移除碰撞") - # 应用危险按钮样式 - self.collision_button.setStyleSheet(""" - QPushButton { - background-color: rgba(255, 99, 99, 0.15); - color: #ff6363; - border: 1px solid rgba(255, 99, 99, 0.6); - border-radius: 4px; - padding: 8px 12px; - font-family: 'Microsoft YaHei', 'Inter', sans-serif; - font-size: 10px; - font-weight: 400; - min-height: 16px; - } - QPushButton:hover { - background-color: rgba(255, 99, 99, 0.25); - border: 1px solid #ff6363; - } - QPushButton:pressed { - background-color: rgba(255, 99, 99, 0.4); - color: #ffffff; - } - """) + # 应用统一按钮样式 + self.collision_button.setStyleSheet(self._collision_button_style()) # 重新连接信号 try: @@ -10046,28 +10337,8 @@ except Exception as e: # 无碰撞:显示添加按钮,下拉框变为可编辑 if hasattr(self, 'collision_button'): self.collision_button.setText("添加碰撞") - # 应用成功按钮样式 - self.collision_button.setStyleSheet(""" - QPushButton { - background-color: rgba(45, 255, 196, 0.15); - color: #2dffc4; - border: 1px solid rgba(45, 255, 196, 0.6); - border-radius: 4px; - padding: 8px 12px; - font-family: 'Microsoft YaHei', 'Inter', sans-serif; - font-size: 10px; - font-weight: 400; - min-height: 16px; - } - QPushButton:hover { - background-color: rgba(45, 255, 196, 0.25); - border: 1px solid #2dffc4; - } - QPushButton:pressed { - background-color: rgba(45, 255, 196, 0.4); - color: #ffffff; - } - """) + # 应用统一按钮样式 + self.collision_button.setStyleSheet(self._collision_button_style()) # 先断开所有连接,再重新连接 try: @@ -10538,35 +10809,47 @@ except Exception as e: # 位置调整控件 pos_label = QLabel("位置偏移:") + pos_label.setAlignment(Qt.AlignLeft | Qt.AlignTop) pos_label.setVisible(True) # 确保可见 - layout.addWidget(pos_label, current_row, 0) - current_row += 1 # X, Y, Z 位置调整 self.collision_pos_x = self._createCollisionSpinBox(-100, 100, 2) self.collision_pos_y = self._createCollisionSpinBox(-100, 100, 2) self.collision_pos_z = self._createCollisionSpinBox(-100, 100, 2) - x_label = QLabel("X:") - x_label.setVisible(True) - layout.addWidget(x_label, current_row, 0) - self.collision_pos_x.setVisible(True) - layout.addWidget(self.collision_pos_x, current_row, 1) - current_row += 1 + for spinbox in (self.collision_pos_x, self.collision_pos_y, self.collision_pos_z): + spinbox.setMinimumWidth(140) + spinbox.setVisible(True) - y_label = QLabel("Y:") - y_label.setVisible(True) - layout.addWidget(y_label, current_row, 0) - self.collision_pos_y.setVisible(True) - layout.addWidget(self.collision_pos_y, current_row, 1) - current_row += 1 + axis_controls = [ + ("X:", self.collision_pos_x), + ("Y:", self.collision_pos_y), + ("Z:", self.collision_pos_z), + ] - z_label = QLabel("Z:") - z_label.setVisible(True) - layout.addWidget(z_label, current_row, 0) - self.collision_pos_z.setVisible(True) - layout.addWidget(self.collision_pos_z, current_row, 1) - current_row += 1 + for offset, (axis_text, spinbox) in enumerate(axis_controls): + row = current_row + offset + axis_layout = QHBoxLayout() + if offset == 0: + layout.addWidget(pos_label, row, 0) + # axis_label.addWidget(pos_label) + else: + spacer = QLabel("") + spacer.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + spacer.setVisible(True) + layout.addWidget(spacer, row, 0) + # axis_layout.addWidget(spacer) + + axis_label = QLabel(axis_text) + axis_label.setAlignment(Qt.AlignVCenter) + axis_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + axis_layout.addWidget(axis_label) + axis_layout.addWidget(spinbox) + # layout.addWidget(axis_label, row, 1) + # layout.addWidget(spinbox, row, 2, 1, 2) + layout.addLayout(axis_layout, row, 1, 1, 3) + + current_row += len(axis_controls) # 连接位置变化信号 self.collision_pos_x.valueChanged.connect(lambda v: self._updateCollisionPosition(model, 'x', v)) @@ -10601,11 +10884,12 @@ except Exception as e: radius_label = QLabel("半径:") radius_label.setVisible(True) layout.addWidget(radius_label, current_row, 0) + layout.addWidget(QLabel(""), current_row, 1) self.collision_radius = self._createCollisionSpinBox(0.01, 10000, 2) self.collision_radius.setVisible(True) self.collision_radius.valueChanged.connect(lambda v: self._updateSphereRadius(model, v)) - layout.addWidget(self.collision_radius, current_row, 1) + layout.addWidget(self.collision_radius, current_row, 1, 1, 3) current_row += 1 return current_row @@ -10617,32 +10901,38 @@ except Exception as e: size_label = QLabel("尺寸:") size_label.setVisible(True) layout.addWidget(size_label, current_row, 0) - current_row += 1 + # layout.addWidget(QLabel(""), current_row, 1) + # current_row += 1 self.collision_width = self._createCollisionSpinBox(0.01, 10000, 2) self.collision_length = self._createCollisionSpinBox(0.01, 10000, 2) self.collision_height = self._createCollisionSpinBox(0.01, 10000, 2) - width_label = QLabel("宽度:") - width_label.setVisible(True) - layout.addWidget(width_label, current_row, 0) - self.collision_width.setVisible(True) - layout.addWidget(self.collision_width, current_row, 1) - current_row += 1 + box_controls = [ + ("宽度:", self.collision_width), + ("长度:", self.collision_length), + ("高度:", self.collision_height), + ] - length_label = QLabel("长度:") - length_label.setVisible(True) - layout.addWidget(length_label, current_row, 0) - self.collision_length.setVisible(True) - layout.addWidget(self.collision_length, current_row, 1) - current_row += 1 + for offset,(text, spinbox) in enumerate(box_controls): + axis_layout = QHBoxLayout() + if offset == 0: + layout.addWidget(size_label, current_row, 0) + else: + spacer = QLabel("") + spacer.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + spacer.setVisible(True) + layout.addWidget(spacer, current_row, 0) - height_label = QLabel("高度:") - height_label.setVisible(True) - layout.addWidget(height_label, current_row, 0) - self.collision_height.setVisible(True) - layout.addWidget(self.collision_height, current_row, 1) - current_row += 1 + axis_label = QLabel(text) + axis_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + axis_label.setVisible(True) + axis_layout.addWidget(axis_label) + spinbox.setVisible(True) + axis_layout.addWidget(spinbox) + layout.addLayout(axis_layout, current_row, 1, 1, 3) + current_row += 1 # 连接信号 self.collision_width.valueChanged.connect(lambda v: self._updateBoxSize(model, 'width', v)) @@ -10660,7 +10950,7 @@ except Exception as e: self.collision_capsule_radius = self._createCollisionSpinBox(0.01, 10000, 2) self.collision_capsule_radius.valueChanged.connect(lambda v: self._updateCapsuleRadius(model, v)) - layout.addWidget(self.collision_capsule_radius, current_row, 1) + layout.addWidget(self.collision_capsule_radius, current_row, 1, 1 , 3) current_row += 1 height_label = QLabel("高度:") @@ -10668,7 +10958,7 @@ except Exception as e: self.collision_capsule_height = self._createCollisionSpinBox(0.01, 10000, 2) self.collision_capsule_height.valueChanged.connect(lambda v: self._updateCapsuleHeight(model, v)) - layout.addWidget(self.collision_capsule_height, current_row, 1) + layout.addWidget(self.collision_capsule_height, current_row, 1, 1, 3) current_row += 1 return current_row @@ -10679,22 +10969,42 @@ except Exception as e: normal_label = QLabel("法向量:") layout.addWidget(normal_label, current_row, 0) - current_row += 1 self.collision_normal_x = self._createCollisionSpinBox(-1, 1, 2) self.collision_normal_y = self._createCollisionSpinBox(-1, 1, 2) self.collision_normal_z = self._createCollisionSpinBox(-1, 1, 2) - layout.addWidget(QLabel("Nx:"), current_row, 0) - layout.addWidget(self.collision_normal_x, current_row, 1) + nx_layout = QHBoxLayout() + axis_label = QLabel("Nx:") + axis_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + nx_layout.addWidget(axis_label) + nx_layout.addWidget(self.collision_normal_x) + layout.addLayout(nx_layout, current_row, 1, 1, 3) current_row += 1 - layout.addWidget(QLabel("Ny:"), current_row, 0) - layout.addWidget(self.collision_normal_y, current_row, 1) + ny_layout = QHBoxLayout() + placeholder = QLabel("") + layout.addWidget(placeholder, current_row, 0) + + axis_label = QLabel("Ny:") + axis_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + ny_layout.addWidget(axis_label) + ny_layout.addWidget(self.collision_normal_y) + layout.addLayout(ny_layout, current_row, 1, 1, 3) current_row += 1 - layout.addWidget(QLabel("Nz:"), current_row, 0) - layout.addWidget(self.collision_normal_z, current_row, 1) + nz_layout = QHBoxLayout() + placeholder = QLabel("") + layout.addWidget(placeholder, current_row, 0) + + axis_label = QLabel("Nz:") + axis_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + axis_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + nz_layout.addWidget(axis_label) + nz_layout.addWidget(self.collision_normal_z) + layout.addLayout(nz_layout, current_row, 1, 1, 3) current_row += 1 # 连接信号 @@ -10715,8 +11025,9 @@ except Exception as e: current_row = layout.rowCount() self.collision_visibility_button = QPushButton("隐藏碰撞" if is_collision_visible else "显示碰撞") + self.collision_visibility_button.setStyleSheet(self._collision_button_style()) self.collision_visibility_button.clicked.connect(lambda: self._toggleCollisionVisibility(model)) - layout.addWidget(self.collision_visibility_button, current_row - 1, 0, 1, 2) + layout.addWidget(self.collision_visibility_button, current_row - 1, 0, 1, 4) except Exception as e: print(f"添加可见性按钮失败: {e}") @@ -10729,12 +11040,12 @@ except Exception as e: # 移动可见性按钮 if hasattr(self, 'collision_visibility_button'): - layout.addWidget(self.collision_visibility_button, new_row, 0, 1, 2) + layout.addWidget(self.collision_visibility_button, new_row, 0, 1, 4) new_row += 1 - + # 移动主按钮 if hasattr(self, 'collision_button'): - layout.addWidget(self.collision_button, new_row, 0, 1, 2) + layout.addWidget(self.collision_button, new_row, 0, 1, 4) except Exception as e: print(f"重新定位按钮失败: {e}") @@ -10793,4 +11104,4 @@ except Exception as e: except Exception as e: print(f"隐藏碰撞参数控件失败: {e}") import traceback - traceback.print_exc() + traceback.print_exc() \ No newline at end of file diff --git a/ui/widgets.py b/ui/widgets.py index a4280eb8..b88c8400 100644 --- a/ui/widgets.py +++ b/ui/widgets.py @@ -15,7 +15,7 @@ from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QTreeView, QTreeWidget, QTreeWidgetItem, QWidget, QFileDialog, QMessageBox, QAbstractItemView, QMenu, QDockWidget, QButtonGroup, QToolButton, QFrame, QSizePolicy) from PyQt5.QtCore import Qt, QUrl, QMimeData, QPoint, QSize -from PyQt5.QtGui import QDrag, QPainter, QPixmap, QPen, QBrush +from PyQt5.QtGui import QDrag, QPainter, QPixmap, QPen, QBrush, QFont from PyQt5.sip import wrapinstance from direct.showbase.ShowBaseGlobal import aspect2d from panda3d.core import ModelRoot, NodePath, CollisionNode @@ -592,6 +592,13 @@ class CustomFileView(QTreeView): if parent is None: parent = wrapinstance(0, QWidget) super().__init__(parent) + base_font = self.font() + base_font.setFamily("Inter") + base_font.setPointSize(10) + base_font.setWeight(QFont.Normal) + self.setFont(base_font) + if self.model(): + self.model().rowsInserted.connect(self._handle_rows_inserted) self.world = world self.setupUI() self.setupDragDrop() @@ -2136,7 +2143,7 @@ class UniversalMessageDialog(QDialog): INFO = "info" def __init__(self, parent, title, message, message_type=INFO, show_cancel=True, - confirm_text="确认", cancel_text="取消", icon_size=QSize(48, 48)): + confirm_text="确认", cancel_text="取消", icon_size=QSize(20, 20)): """ 初始化通用消息对话框 @@ -2353,14 +2360,20 @@ class UniversalMessageDialog(QDialog): message_area = QHBoxLayout() message_area.setContentsMargins(0, 0, 0, 0) - message_area.setSpacing(15) + message_area.setSpacing(10) + # 用一个垂直布局包裹icon_label,确保顶部对齐 + icon_vbox = QVBoxLayout() + icon_vbox.setContentsMargins(0, 0, 0, 0) + icon_vbox.setSpacing(0) icon_label = QLabel() if self._message_icon and not self._message_icon.isNull(): icon_label.setPixmap(self._message_icon.pixmap(self.icon_size)) icon_label.setAlignment(Qt.AlignTop | Qt.AlignLeft) icon_label.setFixedSize(self.icon_size) - message_area.addWidget(icon_label) + icon_vbox.addWidget(icon_label, alignment=Qt.AlignTop) + icon_vbox.addStretch() + message_area.addLayout(icon_vbox) self.message_label = QLabel(message) self.message_label.setObjectName('messageLabel') @@ -3517,15 +3530,51 @@ class CustomTreeWidget(QTreeWidget): sceneRoot = QTreeWidgetItem(self, ['场景']) sceneRoot.setData(0, Qt.UserRole, self.world.render) sceneRoot.setData(0, Qt.UserRole + 1, "SCENE_ROOT") + self._apply_font_to_item(sceneRoot) # 添加相机节点 cameraItem = QTreeWidgetItem(sceneRoot, ['相机']) cameraItem.setData(0, Qt.UserRole, self.world.cam) cameraItem.setData(0, Qt.UserRole + 1, "CAMERA_NODE") + self._apply_font_to_item(cameraItem) # 添加地板节点 if hasattr(self.world, 'ground') and self.world.ground: groundItem = QTreeWidgetItem(sceneRoot, ['地板']) groundItem.setData(0, Qt.UserRole, self.world.ground) groundItem.setData(0,Qt.UserRole + 1, "SCENE_NODE") + self._apply_font_to_item(groundItem) + + def _apply_font_to_item(self, item): + """根据节点层级设置字体大小""" + if not item: + return + font = QFont(self.font()) + marker = item.data(0, Qt.UserRole + 1) + if item.parent() is None and (marker == "SCENE_ROOT" or item.text(0) == '场景'): + font.setPointSize(12) + font.setWeight(QFont.Medium) + else: + font.setPointSize(10) + font.setWeight(QFont.Normal) + item.setFont(0, font) + + def _apply_font_recursively(self, item): + """递归应用字体样式""" + if not item: + return + self._apply_font_to_item(item) + for index in range(item.childCount()): + self._apply_font_recursively(item.child(index)) + + def _handle_rows_inserted(self, parent_index, first, last): + """在插入新节点时自动调整字体""" + model = self.model() + if not model: + return + for row in range(first, last + 1): + index = model.index(row, 0, parent_index) + item = self.itemFromIndex(index) + if item: + self._apply_font_recursively(item) def _cleanup_panda_node_resources(self, panda_node): """一个集中的辅助函数,用于清理与Panda3D节点相关的所有资源。"""