1.UI更新

This commit is contained in:
陈横 2025-10-15 15:29:01 +08:00
parent 54fca09914
commit 64c7a30bc2
2 changed files with 750 additions and 186 deletions

View File

@ -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):
"""打开拆装配置界面"""

View File

@ -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
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}")
print(f"建文件夹: {new_folder_path}")
except OSError as e:
print(f"创建文件夹失败: {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
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:
with open(new_file_path, "w", encoding="utf-8") as f:
f.write("")
self._refreshWithStatePreservation()
print(f"建文件: {new_file_path}")
print(f"建文件: {new_file_path}")
except OSError as e:
print(f"创建文件失败: {e}")
print(f"新建文件失败: {e}")
def renameItem(self, item):
"""重命名文件或文件夹"""
@ -808,19 +825,26 @@ 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
new_name = dialog.text()
if not new_name or new_name == old_name:
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)
try:
os.rename(old_path, new_path)
item.setText(0, new_name)
item.setData(0, Qt.UserRole, new_path)
self._refreshWithStatePreservation()
print(f"重命名: {old_path} -> {new_path}")
except OSError as e:
print(f"重命名失败: {e}")
def deleteItem(self, item):
"""删除文件或文件夹"""
import os
@ -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):
"""自定义场景树部件"""