1.UI更新
This commit is contained in:
parent
54fca09914
commit
64c7a30bc2
@ -16,13 +16,336 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QMenu, QAction
|
||||
QLabel, QLineEdit, QFormLayout, QDoubleSpinBox, QScrollArea,
|
||||
QFileSystemModel, QButtonGroup, QToolButton, QPushButton, QHBoxLayout,
|
||||
QComboBox, QGroupBox, QInputDialog, QFileDialog, QMessageBox, QDesktopWidget, QDialog,
|
||||
QSpinBox, QFrame, QRadioButton, QTextEdit, QTabWidget)
|
||||
QSpinBox, QFrame, QRadioButton, QTextEdit, QTabWidget, QSizePolicy)
|
||||
from PyQt5.QtCore import Qt, QDir, QTimer, QSize, QPoint, QUrl, QRect
|
||||
from direct.showbase.ShowBaseGlobal import aspect2d
|
||||
from panda3d.core import OrthographicLens
|
||||
|
||||
from ui.widgets import CustomPanda3DWidget, CustomFileView, CustomTreeWidget,CustomAssetsTreeWidget, CustomConsoleDockWidget
|
||||
from ui.icon_manager import get_icon_manager, get_icon
|
||||
|
||||
|
||||
class StyledTerrainDialog(QDialog):
|
||||
"""与新建项目对话框风格一致的参数输入对话框"""
|
||||
|
||||
def __init__(self, parent, title, fields, primary_text="确认", secondary_text="取消"):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(title)
|
||||
self.setObjectName("styledTerrainDialog")
|
||||
self.setModal(True)
|
||||
self.resize(508, 241)
|
||||
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground, True)
|
||||
|
||||
self.dragging = False
|
||||
self.drag_position = QPoint()
|
||||
self.icon_manager = get_icon_manager()
|
||||
self._title_icon_size = QSize(18, 18)
|
||||
self._icon_close = self.icon_manager.get_icon('close_icon', self._title_icon_size)
|
||||
self.field_widgets = {}
|
||||
|
||||
self.setStyleSheet("""
|
||||
QDialog#styledTerrainDialog {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
QFrame#baseFrame {
|
||||
background-color: #000000;
|
||||
border: 1px solid #3E3E42;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QWidget#titleBar {
|
||||
background-color: transparent;
|
||||
border-radius: 5px 5px 0px 0px;
|
||||
min-height: 32px;
|
||||
max-height: 32px;
|
||||
}
|
||||
QLabel#titleLabel {
|
||||
color: #FFFFFF;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.7px;
|
||||
}
|
||||
QWidget#controlButtons QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: #EBEBEB;
|
||||
font-size: 14px;
|
||||
min-width: 18px;
|
||||
max-width: 18px;
|
||||
min-height: 18px;
|
||||
max-height: 18px;
|
||||
padding: 0px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
QWidget#controlButtons QPushButton:hover {
|
||||
background-color: #2A2D2E;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QPushButton#closeButton {
|
||||
border-radius: 0px 5px 0px 0px;
|
||||
}
|
||||
QPushButton#closeButton:hover {
|
||||
background-color: #2A2D2E;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QWidget#contentWidget {
|
||||
background-color: transparent;
|
||||
border-radius: 0px 0px 5px 5px;
|
||||
}
|
||||
QFrame#contentContainer {
|
||||
background-color: #19191B;
|
||||
border: 1px solid #2C2F36;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QLabel[role="fieldLabel"] {
|
||||
color: #EBEBEB;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.6px;
|
||||
}
|
||||
QLabel[role="hint"] {
|
||||
color: rgba(235, 235, 235, 0.6);
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.55px;
|
||||
padding: 0px;
|
||||
}
|
||||
QDoubleSpinBox, QSpinBox {
|
||||
background-color: rgba(89, 100, 113, 0.2);
|
||||
color: #EBEBEB;
|
||||
border: 1px solid rgba(76, 92, 110, 0.6);
|
||||
border-radius: 2px;
|
||||
padding: 0px 10px;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.55px;
|
||||
min-height: 30px;
|
||||
max-height: 30px;
|
||||
}
|
||||
QDoubleSpinBox:focus, QSpinBox:focus {
|
||||
border: 1px solid #3067C0;
|
||||
background-color: rgba(48, 103, 192, 0.1);
|
||||
}
|
||||
QDoubleSpinBox:hover, QSpinBox:hover {
|
||||
border: 1px solid #3067C0;
|
||||
background-color: rgba(89, 100, 113, 0.3);
|
||||
}
|
||||
QDoubleSpinBox:disabled, QSpinBox:disabled {
|
||||
background-color: rgba(89, 100, 113, 0.1);
|
||||
color: rgba(235, 235, 235, 0.4);
|
||||
border: 1px solid rgba(76, 92, 110, 0.2);
|
||||
}
|
||||
QPushButton {
|
||||
background-color: rgba(89, 98, 118, 0.5);
|
||||
color: #EBEBEB;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
padding: 0px 12px;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-weight: 300;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.5px;
|
||||
min-width: 90px;
|
||||
min-height: 30px;
|
||||
max-height: 30px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #3067C0;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #2556A0;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: rgba(89, 98, 118, 0.3);
|
||||
color: rgba(235, 235, 235, 0.4);
|
||||
}
|
||||
QPushButton#primaryButton {
|
||||
min-width: 120px;
|
||||
max-width: 120px;
|
||||
}
|
||||
QPushButton#secondaryButton {
|
||||
min-width: 120px;
|
||||
max-width: 120px;
|
||||
}
|
||||
""")
|
||||
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
|
||||
base_frame = QFrame()
|
||||
base_frame.setObjectName('baseFrame')
|
||||
base_frame.setFrameShape(QFrame.NoFrame)
|
||||
base_frame.setAttribute(Qt.WA_StyledBackground, True)
|
||||
|
||||
base_layout = QVBoxLayout(base_frame)
|
||||
base_layout.setContentsMargins(0, 0, 0, 0)
|
||||
base_layout.setSpacing(0)
|
||||
|
||||
self.createTitleBar()
|
||||
base_layout.addWidget(self.title_bar)
|
||||
|
||||
content_widget = QWidget()
|
||||
content_widget.setObjectName('contentWidget')
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(15, 10, 15, 10)
|
||||
content_layout.setSpacing(0)
|
||||
|
||||
content_container = QFrame()
|
||||
content_container.setObjectName('contentContainer')
|
||||
content_container.setFrameShape(QFrame.NoFrame)
|
||||
content_container.setAttribute(Qt.WA_StyledBackground, True)
|
||||
|
||||
container_layout = QVBoxLayout(content_container)
|
||||
container_layout.setContentsMargins(15, 10, 15, 10)
|
||||
container_layout.setSpacing(10)
|
||||
|
||||
for field in fields:
|
||||
row_widget = QWidget()
|
||||
row_layout = QHBoxLayout(row_widget)
|
||||
row_layout.setContentsMargins(0, 0, 0, 0)
|
||||
row_layout.setSpacing(10)
|
||||
|
||||
label = QLabel(field.get("label", ""))
|
||||
label.setProperty('role', 'fieldLabel')
|
||||
label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||
label.setMinimumWidth(field.get("label_width", 80))
|
||||
label.setMaximumWidth(field.get("label_width", 120))
|
||||
row_layout.addWidget(label)
|
||||
|
||||
widget_type = field.get("type", "double")
|
||||
if widget_type == "int":
|
||||
widget = QSpinBox()
|
||||
widget.setRange(field.get("min", 0), field.get("max", 1000))
|
||||
widget.setSingleStep(field.get("step", 1))
|
||||
widget.setValue(field.get("value", field.get("default", 0)))
|
||||
else:
|
||||
widget = QDoubleSpinBox()
|
||||
widget.setRange(field.get("min", 0.0), field.get("max", 1000.0))
|
||||
widget.setSingleStep(field.get("step", 0.1))
|
||||
widget.setDecimals(field.get("decimals", 2))
|
||||
widget.setValue(field.get("value", field.get("default", 0.0)))
|
||||
|
||||
widget.setFixedHeight(30)
|
||||
widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
row_layout.addWidget(widget, 1)
|
||||
|
||||
if field.get("suffix"):
|
||||
suffix_label = QLabel(field["suffix"])
|
||||
suffix_label.setProperty('role', 'fieldLabel')
|
||||
row_layout.addWidget(suffix_label)
|
||||
|
||||
self.field_widgets[field.get("name", field.get("label", ""))] = widget
|
||||
container_layout.addWidget(row_widget)
|
||||
|
||||
separator = QFrame()
|
||||
separator.setFrameShape(QFrame.HLine)
|
||||
separator.setFrameShadow(QFrame.Plain)
|
||||
separator.setFixedHeight(1)
|
||||
separator.setStyleSheet("background-color: #2C2F36; border: none;")
|
||||
container_layout.addWidget(separator)
|
||||
|
||||
button_row_widget = QWidget()
|
||||
button_row_layout = QHBoxLayout(button_row_widget)
|
||||
button_row_layout.setContentsMargins(0, 0, 0, 0)
|
||||
button_row_layout.setSpacing(10)
|
||||
button_row_layout.addStretch()
|
||||
|
||||
self.confirmButton = QPushButton(primary_text)
|
||||
self.confirmButton.setObjectName('primaryButton')
|
||||
self.confirmButton.setFixedSize(120, 30)
|
||||
self.confirmButton.clicked.connect(self.accept)
|
||||
button_row_layout.addWidget(self.confirmButton)
|
||||
|
||||
self.cancelButton = QPushButton(secondary_text)
|
||||
self.cancelButton.setObjectName('secondaryButton')
|
||||
self.cancelButton.setFixedSize(120, 30)
|
||||
self.cancelButton.clicked.connect(self.reject)
|
||||
button_row_layout.addWidget(self.cancelButton)
|
||||
|
||||
container_layout.addWidget(button_row_widget)
|
||||
|
||||
content_layout.addWidget(content_container, 0, Qt.AlignTop)
|
||||
base_layout.addWidget(content_widget)
|
||||
main_layout.addWidget(base_frame)
|
||||
|
||||
def createTitleBar(self):
|
||||
self.title_bar = QFrame()
|
||||
self.title_bar.setObjectName("titleBar")
|
||||
|
||||
title_layout = QHBoxLayout(self.title_bar)
|
||||
title_layout.setContentsMargins(12, 0, 12, 0)
|
||||
title_layout.setSpacing(0)
|
||||
|
||||
controls = QWidget()
|
||||
controls.setObjectName("controlButtons")
|
||||
controls_layout = QHBoxLayout(controls)
|
||||
controls_layout.setContentsMargins(0, 0, 0, 0)
|
||||
controls_layout.setSpacing(0)
|
||||
|
||||
self.close_button = QPushButton()
|
||||
self.close_button.setObjectName("closeButton")
|
||||
self.close_button.clicked.connect(self.reject)
|
||||
self.close_button.setFocusPolicy(Qt.NoFocus)
|
||||
controls_layout.addWidget(self.close_button)
|
||||
|
||||
self._applyTitleBarIcons()
|
||||
|
||||
left_placeholder = QWidget()
|
||||
left_placeholder.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
|
||||
title_layout.addWidget(left_placeholder)
|
||||
|
||||
self.title_label = QLabel(self.windowTitle())
|
||||
self.title_label.setObjectName("titleLabel")
|
||||
self.title_label.setAlignment(Qt.AlignCenter)
|
||||
title_layout.addWidget(self.title_label, 1)
|
||||
|
||||
title_layout.addWidget(controls)
|
||||
left_placeholder.setFixedWidth(controls.sizeHint().width())
|
||||
|
||||
def _applyTitleBarIcons(self):
|
||||
if self._icon_close:
|
||||
self.close_button.setIcon(self._icon_close)
|
||||
self.close_button.setIconSize(self._title_icon_size)
|
||||
self.close_button.setText("")
|
||||
self.close_button.setToolTip("关闭")
|
||||
|
||||
def setWindowTitle(self, title):
|
||||
super().setWindowTitle(title)
|
||||
if hasattr(self, "title_label"):
|
||||
self.title_label.setText(title)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.LeftButton and hasattr(self, "title_bar"):
|
||||
if self.title_bar.geometry().contains(event.pos()):
|
||||
self.dragging = True
|
||||
self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
|
||||
event.accept()
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if event.buttons() == Qt.LeftButton and self.dragging:
|
||||
self.move(event.globalPos() - self.drag_position)
|
||||
event.accept()
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.dragging = False
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
def get_value(self, name):
|
||||
widget = self.field_widgets.get(name)
|
||||
if isinstance(widget, (QDoubleSpinBox, QSpinBox)):
|
||||
return widget.value()
|
||||
return None
|
||||
try:
|
||||
from PyQt5.QtWebEngineWidgets import QWebEngineView
|
||||
WEB_ENGINE_AVAILABLE = True
|
||||
@ -4194,170 +4517,108 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def onCreateFlatTerrain(self):
|
||||
"""创建平面地形"""
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle("创建平面地形")
|
||||
dialog.setModal(True)
|
||||
dialog.resize(300,200)
|
||||
# 设置对话框样式
|
||||
dialog.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #252538;
|
||||
color: #e0e0ff;
|
||||
}
|
||||
QLabel {
|
||||
color: #e0e0ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #8b5cf6;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
min-width: 80px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #7c3aed;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #6d28d9;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #4c4c6e;
|
||||
color: #8888aa;
|
||||
}
|
||||
QDoubleSpinBox, QSpinBox {
|
||||
background-color: #2d2d44;
|
||||
color: #e0e0ff;
|
||||
border: 1px solid #3a3a4a;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
""")
|
||||
fields = [
|
||||
{
|
||||
"name": "width",
|
||||
"label": "宽度:",
|
||||
"type": "double",
|
||||
"min": 0.0,
|
||||
"max": 10000.0,
|
||||
"step": 0.1,
|
||||
"decimals": 2,
|
||||
"value": 0.30,
|
||||
"label_width": 60
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"label": "高度:",
|
||||
"type": "double",
|
||||
"min": 0.0,
|
||||
"max": 10000.0,
|
||||
"step": 0.1,
|
||||
"decimals": 2,
|
||||
"value": 0.30,
|
||||
"label_width": 60
|
||||
},
|
||||
{
|
||||
"name": "resolution",
|
||||
"label": "分辨率:",
|
||||
"type": "int",
|
||||
"min": 16,
|
||||
"max": 2048,
|
||||
"step": 16,
|
||||
"value": 256,
|
||||
"label_width": 60
|
||||
},
|
||||
]
|
||||
dialog = StyledTerrainDialog(self, "创建平面地形", fields, primary_text="创建", secondary_text="取消")
|
||||
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
||||
width_layout = QHBoxLayout()
|
||||
width_layout.addWidget(QLabel("宽度:"))
|
||||
width_spin = QDoubleSpinBox()
|
||||
width_spin.setRange(0,10000)
|
||||
width_spin.setValue(0.3)
|
||||
width_layout.addWidget(width_spin)
|
||||
layout.addLayout(width_layout)
|
||||
|
||||
# 高度
|
||||
height_layout = QHBoxLayout()
|
||||
height_layout.addWidget(QLabel("高度:"))
|
||||
height_spin = QDoubleSpinBox()
|
||||
height_spin.setRange(0, 10000)
|
||||
height_spin.setValue(0.3)
|
||||
height_layout.addWidget(height_spin)
|
||||
layout.addLayout(height_layout)
|
||||
|
||||
# 分辨率
|
||||
resolution_layout = QHBoxLayout()
|
||||
resolution_layout.addWidget(QLabel("分辨率:"))
|
||||
resolution_spin = QSpinBox()
|
||||
resolution_spin.setRange(16, 2048)
|
||||
resolution_spin.setValue(256)
|
||||
resolution_spin.setSingleStep(16)
|
||||
resolution_layout.addWidget(resolution_spin)
|
||||
layout.addLayout(resolution_layout)
|
||||
|
||||
# 按钮
|
||||
button_layout = QHBoxLayout()
|
||||
ok_button = QPushButton("创建")
|
||||
cancel_button = QPushButton("取消")
|
||||
button_layout.addWidget(ok_button)
|
||||
button_layout.addWidget(cancel_button)
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# 连接信号
|
||||
ok_button.clicked.connect(dialog.accept)
|
||||
cancel_button.clicked.connect(dialog.reject)
|
||||
|
||||
# 显示对话框
|
||||
if dialog.exec_() == QDialog.Accepted:
|
||||
width = width_spin.value()
|
||||
height = height_spin.value()
|
||||
resolution = resolution_spin.value()
|
||||
width = dialog.get_value("width")
|
||||
height = dialog.get_value("height")
|
||||
resolution = int(dialog.get_value("resolution"))
|
||||
|
||||
# 调用世界对象创建地形
|
||||
terrain_info = self.world.createFlatTerrain((width, height), resolution)
|
||||
if terrain_info:
|
||||
QMessageBox.information(self, "成功", "平面地形创建成功!")
|
||||
else:
|
||||
QMessageBox.warning(self, "错误", "平面地形创建失败!")
|
||||
QMessageBox.warning(self, "警告", "平面地形创建失败!")
|
||||
|
||||
def onCreateHeightmapTerrain(self):
|
||||
"""从高度图创建地形"""
|
||||
dialog = self.createStyledFileDialog(
|
||||
file_dialog = self.createStyledFileDialog(
|
||||
self,
|
||||
"选择高度图文件",
|
||||
"",
|
||||
"图像文件 (*.png *.jpg *.jpeg *.bmp *.tga);;所有文件 (*)"
|
||||
)
|
||||
|
||||
if dialog.exec_() == QDialog.Accepted:
|
||||
file_path = dialog.selectedFiles()[0]
|
||||
if file_dialog.exec_() == QDialog.Accepted:
|
||||
file_path = file_dialog.selectedFiles()[0]
|
||||
if file_path:
|
||||
#创建对话框获取地形参数
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle("设置地形参数")
|
||||
dialog.setModal(True)
|
||||
dialog.resize(300,250)
|
||||
fields = [
|
||||
{
|
||||
"name": "x_scale",
|
||||
"label": "X缩放:",
|
||||
"type": "double",
|
||||
"min": 0.1,
|
||||
"max": 1000.0,
|
||||
"step": 0.1,
|
||||
"decimals": 2,
|
||||
"value": 0.30,
|
||||
"label_width": 70
|
||||
},
|
||||
{
|
||||
"name": "y_scale",
|
||||
"label": "Y缩放:",
|
||||
"type": "double",
|
||||
"min": 0.1,
|
||||
"max": 1000.0,
|
||||
"step": 0.1,
|
||||
"decimals": 2,
|
||||
"value": 0.30,
|
||||
"label_width": 70
|
||||
},
|
||||
{
|
||||
"name": "z_scale",
|
||||
"label": "Z缩放:",
|
||||
"type": "double",
|
||||
"min": 0.1,
|
||||
"max": 1000.0,
|
||||
"step": 1.0,
|
||||
"decimals": 2,
|
||||
"value": 50.0,
|
||||
"label_width": 70
|
||||
},
|
||||
]
|
||||
|
||||
layout = QVBoxLayout(dialog)
|
||||
params_dialog = StyledTerrainDialog(self, "设置地形参数", fields, primary_text="创建", secondary_text="取消")
|
||||
|
||||
x_scale_layout = QHBoxLayout()
|
||||
x_scale_layout.addWidget(QLabel("X缩放:"))
|
||||
x_scale_spin = QDoubleSpinBox()
|
||||
x_scale_spin.setRange(0.1,1000)
|
||||
x_scale_spin.setValue(0.3)
|
||||
x_scale_spin.setSingleStep(10)
|
||||
x_scale_layout.addWidget(x_scale_spin)
|
||||
layout.addLayout(x_scale_layout)
|
||||
if params_dialog.exec_() == QDialog.Accepted:
|
||||
x_scale = params_dialog.get_value("x_scale")
|
||||
y_scale = params_dialog.get_value("y_scale")
|
||||
z_scale = params_dialog.get_value("z_scale")
|
||||
|
||||
# Y缩放
|
||||
y_scale_layout = QHBoxLayout()
|
||||
y_scale_layout.addWidget(QLabel("Y缩放:"))
|
||||
y_scale_spin = QDoubleSpinBox()
|
||||
y_scale_spin.setRange(0.1, 1000)
|
||||
y_scale_spin.setValue(0.3)
|
||||
y_scale_spin.setSingleStep(10)
|
||||
y_scale_layout.addWidget(y_scale_spin)
|
||||
layout.addLayout(y_scale_layout)
|
||||
|
||||
# Z缩放
|
||||
z_scale_layout = QHBoxLayout()
|
||||
z_scale_layout.addWidget(QLabel("Z缩放:"))
|
||||
z_scale_spin = QDoubleSpinBox()
|
||||
z_scale_spin.setRange(0.1, 1000)
|
||||
z_scale_spin.setValue(50)
|
||||
z_scale_spin.setSingleStep(5)
|
||||
z_scale_layout.addWidget(z_scale_spin)
|
||||
layout.addLayout(z_scale_layout)
|
||||
|
||||
# 按钮
|
||||
button_layout = QHBoxLayout()
|
||||
ok_button = QPushButton("创建")
|
||||
cancel_button = QPushButton("取消")
|
||||
button_layout.addWidget(ok_button)
|
||||
button_layout.addWidget(cancel_button)
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# 连接信号
|
||||
ok_button.clicked.connect(dialog.accept)
|
||||
cancel_button.clicked.connect(dialog.reject)
|
||||
|
||||
# 显示对话框
|
||||
if dialog.exec_() == QDialog.Accepted:
|
||||
x_scale = x_scale_spin.value()
|
||||
y_scale = y_scale_spin.value()
|
||||
z_scale = z_scale_spin.value()
|
||||
|
||||
# 调用世界对象创建地形
|
||||
terrain_info = self.world.createTerrainFromHeightMap(
|
||||
file_path,
|
||||
(x_scale, y_scale, z_scale)
|
||||
@ -4365,7 +4626,7 @@ class MainWindow(QMainWindow):
|
||||
if terrain_info:
|
||||
QMessageBox.information(self, "成功", "高度图地形创建成功!")
|
||||
else:
|
||||
QMessageBox.warning(self, "错误", "高度图地形创建失败!")
|
||||
QMessageBox.warning(self, "警告", "高度图地形创建失败!")
|
||||
|
||||
def onOpenAssemblyDisassemblyConfig(self):
|
||||
"""打开拆装配置界面"""
|
||||
|
||||
367
ui/widgets.py
367
ui/widgets.py
@ -60,10 +60,15 @@ class NewProjectDialog(QDialog):
|
||||
}
|
||||
QWidget#titleBar {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 5px 5px 0px 0px;
|
||||
min-height: 32px;
|
||||
max-height: 32px;
|
||||
}
|
||||
QWidget#titleBar QWidget {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
QLabel#titleLabel {
|
||||
color: #FFFFFF;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
@ -231,7 +236,7 @@ class NewProjectDialog(QDialog):
|
||||
content_widget = QWidget()
|
||||
content_widget.setObjectName('contentWidget')
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(15, 10, 15, 10)
|
||||
content_layout.setContentsMargins(10, 10, 10, 10)
|
||||
content_layout.setSpacing(0)
|
||||
|
||||
content_container = QFrame()
|
||||
@ -769,37 +774,49 @@ class CustomAssetsTreeWidget(QTreeWidget):
|
||||
self._restoreExpandedState(expanded_paths)
|
||||
|
||||
def createNewFolder(self, parent_item):
|
||||
"""创建新文件夹"""
|
||||
"""新建文件夹"""
|
||||
import os
|
||||
|
||||
parent_path = parent_item.data(0, Qt.UserRole)
|
||||
folder_name, ok = StyledMessageBox.getText(self, "新建文件夹", "文件夹名称:")
|
||||
dialog = StyledTextInputDialog(self, "新建文件夹", "文件夹名称", "请输入文件夹名称")
|
||||
if dialog.exec_() != QDialog.Accepted:
|
||||
return
|
||||
|
||||
folder_name = dialog.text()
|
||||
if not folder_name:
|
||||
return
|
||||
|
||||
new_folder_path = os.path.join(parent_path, folder_name)
|
||||
try:
|
||||
os.makedirs(new_folder_path, exist_ok=True)
|
||||
self._refreshWithStatePreservation()
|
||||
print(f"新建文件夹: {new_folder_path}")
|
||||
except OSError as e:
|
||||
print(f"新建文件夹失败: {e}")
|
||||
|
||||
if ok and folder_name:
|
||||
new_folder_path = os.path.join(parent_path, folder_name)
|
||||
try:
|
||||
os.makedirs(new_folder_path, exist_ok=True)
|
||||
self._refreshWithStatePreservation()
|
||||
print(f"创建文件夹: {new_folder_path}")
|
||||
except OSError as e:
|
||||
print(f"创建文件夹失败: {e}")
|
||||
|
||||
def createNewFile(self, parent_item):
|
||||
"""创建新文件"""
|
||||
"""新建文件"""
|
||||
import os
|
||||
|
||||
parent_path = parent_item.data(0, Qt.UserRole)
|
||||
file_name, ok = StyledMessageBox.getText(self, "新建文件", "文件名称:")
|
||||
dialog = StyledTextInputDialog(self, "新建文件", "文件名称", "请输入文件名称")
|
||||
if dialog.exec_() != QDialog.Accepted:
|
||||
return
|
||||
|
||||
file_name = dialog.text()
|
||||
if not file_name:
|
||||
return
|
||||
|
||||
new_file_path = os.path.join(parent_path, file_name)
|
||||
try:
|
||||
with open(new_file_path, "w", encoding="utf-8") as f:
|
||||
f.write("")
|
||||
self._refreshWithStatePreservation()
|
||||
print(f"新建文件: {new_file_path}")
|
||||
except OSError as e:
|
||||
print(f"新建文件失败: {e}")
|
||||
|
||||
if ok and file_name:
|
||||
new_file_path = os.path.join(parent_path, file_name)
|
||||
try:
|
||||
with open(new_file_path, 'w', encoding='utf-8') as f:
|
||||
f.write("")
|
||||
self._refreshWithStatePreservation()
|
||||
print(f"创建文件: {new_file_path}")
|
||||
except OSError as e:
|
||||
print(f"创建文件失败: {e}")
|
||||
|
||||
def renameItem(self, item):
|
||||
"""重命名文件或文件夹"""
|
||||
@ -808,18 +825,25 @@ class CustomAssetsTreeWidget(QTreeWidget):
|
||||
old_path = item.data(0, Qt.UserRole)
|
||||
old_name = os.path.basename(old_path)
|
||||
|
||||
new_name, ok = StyledMessageBox.getText(self, "重命名", "新名称:", text=old_name)
|
||||
dialog = StyledTextInputDialog(self, "重命名", "新名称", "请输入新名称", old_name)
|
||||
if dialog.exec_() != QDialog.Accepted:
|
||||
return
|
||||
|
||||
if ok and new_name and new_name != old_name:
|
||||
parent_dir = os.path.dirname(old_path)
|
||||
new_path = os.path.join(parent_dir, new_name)
|
||||
new_name = dialog.text()
|
||||
if not new_name or new_name == old_name:
|
||||
return
|
||||
|
||||
parent_dir = os.path.dirname(old_path)
|
||||
new_path = os.path.join(parent_dir, new_name)
|
||||
|
||||
try:
|
||||
os.rename(old_path, new_path)
|
||||
item.setText(0, new_name)
|
||||
item.setData(0, Qt.UserRole, new_path)
|
||||
self._refreshWithStatePreservation()
|
||||
except OSError as e:
|
||||
print(f"重命名失败: {e}")
|
||||
|
||||
try:
|
||||
os.rename(old_path, new_path)
|
||||
self._refreshWithStatePreservation()
|
||||
print(f"重命名: {old_path} -> {new_path}")
|
||||
except OSError as e:
|
||||
print(f"重命名失败: {e}")
|
||||
|
||||
def deleteItem(self, item):
|
||||
"""删除文件或文件夹"""
|
||||
@ -2081,6 +2105,285 @@ class StyledMessageBox:
|
||||
ok = dialog.exec_()
|
||||
return dialog.textValue(), ok
|
||||
|
||||
class StyledTextInputDialog(QDialog):
|
||||
"""与新建项目样式一致的文本输入对话框"""
|
||||
|
||||
def __init__(self, parent, title, label_text="", placeholder="", default_text=""):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(title)
|
||||
self.setObjectName("styledTextInputDialog")
|
||||
self.setModal(True)
|
||||
self.resize(420, 186)
|
||||
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground, True)
|
||||
|
||||
self.dragging = False
|
||||
self.drag_position = QPoint()
|
||||
self.icon_manager = get_icon_manager()
|
||||
self._title_icon_size = QSize(18, 18)
|
||||
self._icon_close = self.icon_manager.get_icon('close_icon', self._title_icon_size)
|
||||
|
||||
self.setStyleSheet("""
|
||||
QDialog#styledTextInputDialog {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
QFrame#baseFrame {
|
||||
background-color: #000000;
|
||||
border: 1px solid #3E3E42;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QWidget#titleBar {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 5px 5px 0px 0px;
|
||||
min-height: 32px;
|
||||
max-height: 32px;
|
||||
}
|
||||
QWidget#titleBar QWidget {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
QLabel#titleLabel {
|
||||
color: #FFFFFF;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.7px;
|
||||
}
|
||||
QWidget#controlButtons QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: #EBEBEB;
|
||||
font-size: 14px;
|
||||
min-width: 18px;
|
||||
max-width: 18px;
|
||||
min-height: 18px;
|
||||
max-height: 18px;
|
||||
padding: 0px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
QWidget#controlButtons QPushButton:hover {
|
||||
background-color: #2A2D2E;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QPushButton#closeButton {
|
||||
border-radius: 0px 5px 0px 0px;
|
||||
}
|
||||
QPushButton#closeButton:hover {
|
||||
background-color: #2A2D2E;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QWidget#contentWidget {
|
||||
background-color: transparent;
|
||||
border-radius: 0px 0px 5px 5px;
|
||||
}
|
||||
QFrame#contentContainer {
|
||||
background-color: #19191B;
|
||||
border: 1px solid #2C2F36;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QLabel[role="fieldLabel"] {
|
||||
color: #EBEBEB;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.6px;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: rgba(89, 100, 113, 0.2);
|
||||
color: #EBEBEB;
|
||||
border: 1px solid rgba(76, 92, 110, 0.6);
|
||||
border-radius: 2px;
|
||||
padding: 0px 10px;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.55px;
|
||||
min-height: 30px;
|
||||
max-height: 30px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #3067C0;
|
||||
background-color: rgba(48, 103, 192, 0.1);
|
||||
}
|
||||
QLineEdit:hover {
|
||||
border: 1px solid #3067C0;
|
||||
background-color: rgba(89, 100, 113, 0.3);
|
||||
}
|
||||
QPushButton {
|
||||
background-color: rgba(89, 98, 118, 0.5);
|
||||
color: #EBEBEB;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
padding: 0px 12px;
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
font-weight: 300;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.5px;
|
||||
min-width: 120px;
|
||||
min-height: 30px;
|
||||
max-height: 30px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #3067C0;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #2556A0;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
""")
|
||||
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
|
||||
base_frame = QFrame()
|
||||
base_frame.setObjectName('baseFrame')
|
||||
base_frame.setFrameShape(QFrame.NoFrame)
|
||||
base_frame.setAttribute(Qt.WA_StyledBackground, True)
|
||||
|
||||
base_layout = QVBoxLayout(base_frame)
|
||||
base_layout.setContentsMargins(0, 0, 0, 0)
|
||||
base_layout.setSpacing(0)
|
||||
|
||||
self._createTitleBar()
|
||||
base_layout.addWidget(self.title_bar)
|
||||
base_layout.addSpacing(10)
|
||||
|
||||
content_widget = QWidget()
|
||||
content_widget.setObjectName('contentWidget')
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(10, 0, 10, 10)
|
||||
content_layout.setSpacing(0)
|
||||
|
||||
content_container = QFrame()
|
||||
content_container.setObjectName('contentContainer')
|
||||
content_container.setFrameShape(QFrame.NoFrame)
|
||||
content_container.setAttribute(Qt.WA_StyledBackground, True)
|
||||
content_container.setFixedWidth(400)
|
||||
|
||||
container_layout = QVBoxLayout(content_container)
|
||||
container_layout.setContentsMargins(15, 10, 15, 10)
|
||||
container_layout.setSpacing(10)
|
||||
|
||||
if label_text:
|
||||
label = QLabel(label_text)
|
||||
label.setProperty('role', 'fieldLabel')
|
||||
container_layout.addWidget(label)
|
||||
|
||||
self.line_edit = QLineEdit()
|
||||
self.line_edit.setPlaceholderText(placeholder)
|
||||
self.line_edit.setText(default_text)
|
||||
container_layout.addWidget(self.line_edit)
|
||||
|
||||
separator = QFrame()
|
||||
separator.setFrameShape(QFrame.HLine)
|
||||
separator.setFrameShadow(QFrame.Plain)
|
||||
separator.setFixedHeight(1)
|
||||
separator.setStyleSheet("background-color: #2C2F36; border: none;")
|
||||
container_layout.addWidget(separator)
|
||||
|
||||
button_row = QHBoxLayout()
|
||||
button_row.setContentsMargins(0, 0, 0, 0)
|
||||
button_row.setSpacing(10)
|
||||
button_row.addStretch()
|
||||
|
||||
self.confirmButton = QPushButton("确认")
|
||||
self.confirmButton.setObjectName("primaryButton")
|
||||
self.confirmButton.clicked.connect(self._onAccept)
|
||||
button_row.addWidget(self.confirmButton)
|
||||
|
||||
self.cancelButton = QPushButton("取消")
|
||||
self.cancelButton.setObjectName("secondaryButton")
|
||||
self.cancelButton.clicked.connect(self.reject)
|
||||
button_row.addWidget(self.cancelButton)
|
||||
|
||||
container_layout.addLayout(button_row)
|
||||
|
||||
content_layout.addWidget(content_container, 0, Qt.AlignTop)
|
||||
base_layout.addWidget(content_widget)
|
||||
main_layout.addWidget(base_frame)
|
||||
|
||||
self.confirmButton.setDefault(True)
|
||||
self.confirmButton.setAutoDefault(True)
|
||||
self.line_edit.selectAll()
|
||||
self.line_edit.setFocus()
|
||||
|
||||
def _createTitleBar(self):
|
||||
self.title_bar = QFrame()
|
||||
self.title_bar.setObjectName("titleBar")
|
||||
|
||||
title_layout = QHBoxLayout(self.title_bar)
|
||||
title_layout.setContentsMargins(12, 6, 12, 6)
|
||||
title_layout.setSpacing(0)
|
||||
|
||||
controls = QWidget()
|
||||
controls.setObjectName("controlButtons")
|
||||
controls_layout = QHBoxLayout(controls)
|
||||
controls_layout.setContentsMargins(0, 0, 0, 0)
|
||||
controls_layout.setSpacing(0)
|
||||
|
||||
self.close_button = QPushButton()
|
||||
self.close_button.setObjectName("closeButton")
|
||||
self.close_button.clicked.connect(self.reject)
|
||||
self.close_button.setFocusPolicy(Qt.NoFocus)
|
||||
self.close_button.setFixedSize(18, 18)
|
||||
controls_layout.addWidget(self.close_button)
|
||||
|
||||
self._applyTitleBarIcons()
|
||||
|
||||
left_placeholder = QWidget()
|
||||
left_placeholder.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
|
||||
title_layout.addWidget(left_placeholder)
|
||||
|
||||
self.title_label = QLabel(self.windowTitle())
|
||||
self.title_label.setObjectName("titleLabel")
|
||||
self.title_label.setAlignment(Qt.AlignCenter)
|
||||
title_layout.addWidget(self.title_label, 1)
|
||||
|
||||
title_layout.addWidget(controls)
|
||||
left_placeholder.setFixedWidth(controls.sizeHint().width())
|
||||
|
||||
def _applyTitleBarIcons(self):
|
||||
if self._icon_close:
|
||||
self.close_button.setIcon(self._icon_close)
|
||||
self.close_button.setIconSize(self._title_icon_size)
|
||||
self.close_button.setText("")
|
||||
self.close_button.setToolTip("关闭")
|
||||
|
||||
def setWindowTitle(self, title):
|
||||
super().setWindowTitle(title)
|
||||
if hasattr(self, "title_label"):
|
||||
self.title_label.setText(title)
|
||||
|
||||
def _onAccept(self):
|
||||
if self.line_edit.text().strip():
|
||||
self.accept()
|
||||
|
||||
def text(self):
|
||||
return self.line_edit.text().strip()
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.LeftButton and self.title_bar.geometry().contains(event.pos()):
|
||||
self.dragging = True
|
||||
self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
|
||||
event.accept()
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if event.buttons() == Qt.LeftButton and self.dragging:
|
||||
self.move(event.globalPos() - self.drag_position)
|
||||
event.accept()
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.dragging = False
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
|
||||
class CustomTreeWidget(QTreeWidget):
|
||||
"""自定义场景树部件"""
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user