This commit is contained in:
陈横 2025-10-17 16:56:28 +08:00
parent 086301e375
commit de6446b886
47 changed files with 3370 additions and 667 deletions

10
.gitignore vendored
View File

@ -123,3 +123,13 @@ Thumbs.db
*.temp
*.bak
*.backup
BUTTON_CRASH_FIX.md
.gitignore
CORNER_RADIUS_FIX.md
CORNER_RADIUS_UPDATE.md
FIGMA_LAYOUT_IMPROVEMENTS.md
LOGO_AREA_IMPROVEMENTS.md
.codex/settings/kiroCodex-settings.json
MetaCore/LOGO_AREA_REDESIGN.md
MetaCore/LAYOUT_OPTIMIZATION_SUMMARY.md
MetaCore/CROSS_PLATFORM_VENV.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,46 @@
[
{
"id": 4,
"title": "XNWX",
"date": "2025-10-11 12:02:48",
"type": "imported",
"image": "C:\\Users\\29381\\Desktop\\XNWX\\XNWX.png",
"path": "C:\\Users\\29381\\Desktop",
"project_dir": "C:\\Users\\29381\\Desktop\\XNWX",
"description": "从文件夹 XNWX 导入",
"status": "normal"
},
{
"id": 1,
"title": "示例项目1",
"date": "2024-06-08 15:56:35",
"type": "empty",
"image": "📁",
"path": "C:\\Users\\29381\\Desktop\\MetaCore-startup\\MetaCore\\MetaCore",
"project_dir": "C:\\Users\\29381\\Desktop\\MetaCore-startup\\MetaCore\\MetaCore",
"description": "这是一个示例空白项目",
"status": "normal"
},
{
"id": 2,
"title": "示例项目2",
"date": "2023-01-10 12:09:04",
"type": "empty",
"image": "📁",
"path": "C:\\Users\\29381\\Desktop\\MetaCore-startup\\MetaCore\\MetaCore",
"project_dir": "C:\\Users\\29381\\Desktop\\MetaCore-startup\\MetaCore\\MetaCore",
"description": "这是另一个示例空白项目",
"status": "normal"
},
{
"id": 3,
"title": "我的第一个项目",
"date": "2024-06-07 06:57:46",
"type": "empty",
"image": "📁",
"path": "C:\\Users\\29381\\Desktop\\MetaCore-startup\\MetaCore\\MetaCore",
"project_dir": "C:\\Users\\29381\\Desktop\\MetaCore-startup\\MetaCore\\MetaCore",
"description": "从这里开始您的第一个项目",
"status": "normal"
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View File

@ -31,6 +31,8 @@ from pathlib import Path # 现代路径处理
from typing import List, Dict, Optional # 类型提示
import glob
import subprocess
from PyQt5.QtWidgets import QDialog
from ui.widget import UniversalMessageDialog
# 第三方库导入
from PyQt5.QtCore import QObject, pyqtSignal, QFileSystemWatcher, QTimer # Qt核心对象和信号系统
@ -751,17 +753,21 @@ if __name__ == "__main__":
project_names = [p.title for p in deleted_projects]
project_list = "\n".join([f"{name}" for name in project_names])
reply = QMessageBox.question(
None,
"多个项目目录已删除",
f"检测到以下 {len(deleted_projects)} 个项目的目录已被删除:\n\n"
f"{project_list}\n\n"
f"是否要从项目列表中移除这些项目?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes
)
# reply = QMessageBox.question(
# None,
# "多个项目目录已删除",
# f"检测到以下 {len(deleted_projects)} 个项目的目录已被删除:\n\n"
# f"{project_list}\n\n"
# f"是否要从项目列表中移除这些项目?",
# QMessageBox.Yes | QMessageBox.No,
# QMessageBox.Yes
# )
reply = UniversalMessageDialog.show_info("多个项目目录已删除", f"检测到以下 {len(deleted_projects)} 个项目的目录已被删除:\n\n"
f"{project_list}\n\n"
f"是否要从项目列表中移除这些项目?",
True, "确定", "取消")
if reply == QMessageBox.Yes:
if reply == QDialog.Accepted:
for project in deleted_projects:
self.remove_project_from_watcher(project)
self.remove_project(project.id)
@ -851,16 +857,19 @@ if __name__ == "__main__":
if project.status != 'normal':
return
reply = QMessageBox.question(
None,
"项目目录已删除",
f"检测到项目 \"{project.title}\" 的目录已被删除:\n{project.project_dir}\n\n"
f"是否要从项目列表中移除此项目?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes
)
# reply = QMessageBox.question(
# None,
# "项目目录已删除",
# f"检测到项目 \"{project.title}\" 的目录已被删除:\n{project.project_dir}\n\n"
# f"是否要从项目列表中移除此项目?",
# QMessageBox.Yes | QMessageBox.No,
# QMessageBox.Yes
# )
reply = UniversalMessageDialog.show_info("项目目录已删除", f"检测到项目 \"{project.title}\" 的目录已被删除:\n{project.project_dir}\n\n"
f"是否要从项目列表中移除此项目?",
True, "确定", "取消")
if reply == QMessageBox.Yes:
if reply == QDialog.Accepted:
self.remove_project_from_watcher(project)
self.remove_project(project.id)
print(f"已自动移除被删除的项目: {project.title}")

View File

@ -10,6 +10,8 @@ from PyQt5.QtCore import *
from PyQt5.QtGui import *
from data.project_manager import ProjectManager
from ui.icon_manager import IconManager
from ui.widget import UniversalMessageDialog
class CreateProjectDialog(QDialog):
"""创建项目对话框"""
@ -21,14 +23,28 @@ class CreateProjectDialog(QDialog):
self.selected_template = "empty"
self.selected_path = ""
# 自定义标题栏相关控件
self.title_bar = None
self.title_label = None
self.close_btn = None
self.drag_offset = None
# 设置对话框对象名称以应用样式
self.setObjectName("CreateProjectDialog")
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_StyledBackground, True)
self.init_ui()
self.connect_signals()
# 设置对话框属性
self.setWindowTitle("创建新的项目")
if self.title_label is not None:
self.title_label.setText(self.windowTitle())
self.setModal(True)
self.setMinimumSize(1000, 725)
self.resize(1000, 725) # 设置默认尺寸
self.setMinimumSize(1177, 689) # 根据Figma设计调整尺寸
self.setMaximumSize(1177, 689)
self.resize(1177, 689) # 设置默认尺寸
# 设置默认项目位置
self.set_default_location_from_settings()
@ -41,29 +57,59 @@ class CreateProjectDialog(QDialog):
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# 标题栏 - 使用默认系统标题栏
# self.create_title_bar(layout)
# 自定义标题栏
self.title_bar = self.create_title_bar()
layout.addWidget(self.title_bar)
# 主要内容区域
self.create_main_content(layout)
# 底部按钮
self.create_button_area(layout)
def create_title_bar(self):
"""创建与导入项目对话框一致的自定义标题栏"""
title_bar = QWidget()
title_bar.setObjectName("customTitleBar")
layout = QHBoxLayout(title_bar)
layout.setContentsMargins(18, 12, 14, 12)
layout.setSpacing(12)
self.title_label = QLabel(self.windowTitle())
self.title_label.setObjectName("customTitleLabel")
self.title_label.setAttribute(Qt.WA_TransparentForMouseEvents, True)
layout.addWidget(self.title_label)
layout.addStretch()
if IconManager.icon_exists("close_bt_icon"):
close_icon = IconManager.get_icon("close_bt_icon", QSize(18, 18))
self.close_btn = QPushButton()
self.close_btn.setIcon(close_icon)
else:
self.close_btn = QPushButton("X")
self.close_btn.setObjectName("customCloseBtn")
self.close_btn.setFixedSize(18, 18)
self.close_btn.setCursor(Qt.PointingHandCursor)
self.close_btn.clicked.connect(self.reject)
layout.addWidget(self.close_btn)
title_bar.installEventFilter(self)
title_bar.setCursor(Qt.ArrowCursor)
return title_bar
def create_main_content(self, layout):
"""创建主要内容"""
content_widget = QWidget()
content_widget.setObjectName("createProjectContent")
content_layout = QHBoxLayout(content_widget)
content_layout.setContentsMargins(0, 0, 0, 0)
content_layout.setSpacing(0)
content_layout.setContentsMargins(10, 0, 10, 15) # 根据Figma设计调整边距
content_layout.setSpacing(4) # 添加小间距
# 左侧模板选择区域 (2/3)
# 左侧模板选择区域
self.create_template_section(content_layout)
# 右侧项目信息区域 (1/3)
# 右侧项目信息区域
self.create_project_info_section(content_layout)
layout.addWidget(content_widget)
@ -73,24 +119,28 @@ class CreateProjectDialog(QDialog):
template_widget = QWidget()
template_widget.setObjectName("templateSection")
template_layout = QVBoxLayout(template_widget)
template_layout.setContentsMargins(30, 30, 20, 30)
# 标题
title_label = QLabel("选择项目模板")
title_label.setObjectName("sectionTitle")
template_layout.setContentsMargins(0, 0, 0, 0)
template_layout.setSpacing(4)
# 标题栏
title_label = QLabel("选择项目模版") # 与Figma设计保持一致
title_label.setObjectName("templateHeaderLabel")
title_label.setFixedSize(724, 33)
template_layout.addWidget(title_label)
template_layout.addSpacing(4)
# 模板网格
self.create_template_grid(template_layout)
# 设置固定比例 (2/3)
template_widget.setMinimumWidth(600)
layout.addWidget(template_widget, 2)
# 设置固定宽度 (根据Figma设计)
template_widget.setFixedWidth(724)
layout.addWidget(template_widget)
def create_template_grid(self, layout):
"""创建模板网格"""
# 滚动区域
scroll_area = QScrollArea()
scroll_area.setFixedSize(724, 595)
scroll_area.setObjectName("templateScrollArea")
scroll_area.setWidgetResizable(True)
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
@ -100,20 +150,20 @@ class CreateProjectDialog(QDialog):
templates_widget = QWidget()
templates_widget.setObjectName("templatesContainer")
templates_layout = QGridLayout(templates_widget)
templates_layout.setSpacing(20) # 增加间距,适配固定尺寸按钮
templates_layout.setContentsMargins(10, 10, 10, 10) # 添加容器边距
templates_layout.setSpacing(15) # 根据Figma设计调整间距
templates_layout.setContentsMargins(15, 15, 15, 15) # 调整容器边距
templates_layout.setAlignment(Qt.AlignTop | Qt.AlignLeft) # 左上对齐
# 模板数据 - 当前只有空白项目模板,后续可以添加更多模板
templates = [
("empty", "📄", "空白项目模板"),
("empty", "empty_folder_icon", "空白项目模版"), # 移除emoji图标与Figma设计一致
# 后续可以添加的模板示例:
# ("web", "🌐", "Web应用模板"),
# ("mobile", "📱", "移动应用模板"),
# ("desktop", "🖥️", "桌面应用模板"),
# ("api", "🔌", "API服务模板"),
# ("data", "📊", "数据分析模板"),
# ("game", "🎮", "游戏开发模板"),
# ("web", "", "Web应用模板"),
# ("mobile", "", "移动应用模板"),
# ("desktop", "", "桌面应用模板"),
# ("api", "", "API服务模板"),
# ("data", "", "数据分析模板"),
# ("game", "", "游戏开发模板"),
]
# 创建模板项
@ -133,13 +183,13 @@ class CreateProjectDialog(QDialog):
scroll_area.setWidget(templates_widget)
layout.addWidget(scroll_area)
def create_template_item(self, template_id, icon, name):
def create_template_item(self, template_id, icon_path, name):
"""创建模板项"""
template_btn = QPushButton()
template_btn.setObjectName("templateItem")
template_btn.setCheckable(True)
# 设置固定尺寸,确保所有模板按钮大小一致,便于后续添加多个模板
template_btn.setFixedSize(180, 140)
# 根据Figma设计调整尺寸 (149x127)
template_btn.setFixedSize(149, 127)
# 设置默认选中
if template_id == "empty":
@ -148,14 +198,20 @@ class CreateProjectDialog(QDialog):
# 布局
btn_layout = QVBoxLayout(template_btn)
btn_layout.setAlignment(Qt.AlignCenter)
btn_layout.setSpacing(12)
btn_layout.setContentsMargins(20, 24, 20, 24)
btn_layout.setSpacing(27) # 减小间距
btn_layout.setContentsMargins(15, 20, 15, 20)
# 图标
icon_label = QLabel(icon)
icon_label.setObjectName("templateIcon")
icon_label.setAlignment(Qt.AlignCenter)
btn_layout.addWidget(icon_label)
# 图标区域 - 根据Figma设计这里是一个空的框架
if icon_path and IconManager.icon_exists(icon_path):
icon = IconManager.get_icon(icon_path, QSize(40, 40))
icon_label = QLabel() # 创建QLabel而不是QIcon
icon_label.setObjectName("templateIcon")
icon_label.setAlignment(Qt.AlignCenter)
icon_label.setPixmap(icon.pixmap(QSize(40, 40))) # 设置图标
btn_layout.addWidget(icon_label)
else:
# 添加空白空间以保持布局一致
btn_layout.addStretch()
# 名称
name_label = QLabel(name)
@ -183,43 +239,54 @@ class CreateProjectDialog(QDialog):
info_widget = QWidget()
info_widget.setObjectName("projectInfoSection")
info_layout = QVBoxLayout(info_widget)
info_layout.setContentsMargins(20, 30, 30, 30)
info_layout.setContentsMargins(15, 10, 15, 10) # 调整边距
info_layout.setSpacing(15)
# 标题和描述容器
header_widget = QWidget()
header_widget.setObjectName("projectInfoHeader")
header_layout = QVBoxLayout(header_widget)
header_layout.setContentsMargins(0, 0, 0, 0)
header_layout.setSpacing(10)
# 标题
title_label = QLabel("项目信息")
title_label.setObjectName("sectionTitle")
info_layout.addWidget(title_label)
header_layout.addWidget(title_label)
# 模板描述
self.description_label = QLabel("创建一个空白项目,您可以从头开始构建您的应用程序。包含基础的项目结构和配置文件,适用于任何类型的项目开发。")
self.description_label.setObjectName("templateDescription")
self.description_label.setWordWrap(True)
info_layout.addWidget(self.description_label)
header_layout.addWidget(self.description_label)
info_layout.addWidget(header_widget)
# 表单
self.create_project_form(info_layout)
info_layout.addStretch()
# 设置固定比例 (1/3),增加最大宽度以容纳更长的错误信息
info_widget.setMaximumWidth(350) # 从300增加到350
layout.addWidget(info_widget, 1)
# 设置固定宽度 (根据Figma设计)
info_widget.setFixedWidth(427)
layout.addWidget(info_widget)
def create_project_form(self, layout):
"""创建项目表单"""
form_layout = QVBoxLayout()
form_layout.setSpacing(18)
form_layout.setSpacing(15)
# 项目名称
name_group = QVBoxLayout()
name_group.setSpacing(4) # 减小间距,让错误提示更紧贴
name_label = QLabel("项目名称")
name_label.setObjectName("formLabel")
name_group.addWidget(name_label)
name_group.setSpacing(10)
self.name_input = QLineEdit()
self.name_input.setObjectName("formInput")
self.name_input.setMinimumSize(397, 32)
self.name_input.setPlaceholderText("输入项目名称")
name_group.addWidget(self.name_input)
@ -241,11 +308,12 @@ class CreateProjectDialog(QDialog):
desc_label = QLabel("项目描述")
desc_label.setObjectName("formLabel")
desc_group.addWidget(desc_label)
desc_group.setSpacing(10)
self.desc_input = QTextEdit()
self.desc_input.setObjectName("formTextArea")
self.desc_input.setPlaceholderText("输入项目描述(可选)")
self.desc_input.setMaximumHeight(70)
self.desc_input.setMinimumSize(397, 96)
desc_group.addWidget(self.desc_input)
form_layout.addLayout(desc_group)
@ -255,32 +323,38 @@ class CreateProjectDialog(QDialog):
location_label = QLabel("项目位置")
location_label.setObjectName("formLabel")
location_group.addWidget(location_label)
location_group.setSpacing(10)
location_layout = QHBoxLayout()
self.location_input = QLineEdit()
self.location_input.setObjectName("formInput")
self.location_input.setFixedSize(280, 32)
self.location_input.setPlaceholderText("点击浏览选择位置")
self.location_input.setReadOnly(True)
location_layout.addWidget(self.location_input)
browse_btn = QPushButton("📁")
browse_btn = QPushButton("")
if IconManager.icon_exists('file_icon'):
browse_btn.setIcon(IconManager.get_icon('file_icon', QSize(20, 20)))
browse_btn.setObjectName("browseBtn")
browse_btn.setFixedSize(32, 32) # 减小尺寸40x32 -> 32x32
browse_btn.setFixedSize(103, 32) # 减小尺寸40x32 -> 32x32
browse_btn.setToolTip("选择项目保存位置")
browse_btn.clicked.connect(self.browse_location)
location_layout.addWidget(browse_btn)
location_group.addLayout(location_layout)
form_layout.addLayout(location_group)
form_layout.addWidget(self.create_button_area())
layout.addLayout(form_layout)
def create_button_area(self, layout):
def create_button_area(self):
"""创建按钮区域"""
button_widget = QWidget()
button_widget.setObjectName("buttonArea")
button_layout = QHBoxLayout(button_widget)
button_layout.setContentsMargins(30, 20, 30, 30)
button_layout.setContentsMargins(0, 185, 0, 10) # 根据Figma设计调整边距
button_layout.setSpacing(10) # 设置按钮间距
button_layout.addStretch()
@ -288,16 +362,18 @@ class CreateProjectDialog(QDialog):
# 取消按钮
cancel_btn = QPushButton("取消")
cancel_btn.setObjectName("secondaryBtn")
cancel_btn.setFixedSize(95, 36)
cancel_btn.clicked.connect(self.reject)
button_layout.addWidget(cancel_btn)
# 创建按钮
self.create_btn = QPushButton("创建项目")
self.create_btn = QPushButton("创建") # 与Figma设计保持一致
self.create_btn.setObjectName("primaryBtn")
self.create_btn.setFixedSize(95, 36)
self.create_btn.clicked.connect(self.create_project)
button_layout.addWidget(self.create_btn)
layout.addWidget(button_widget)
return button_widget
def update_template_description(self, template_id):
"""更新模板描述"""
@ -328,17 +404,18 @@ class CreateProjectDialog(QDialog):
# 验证输入
name = self.name_input.text().strip()
if not name:
QMessageBox.warning(self, "输入错误", "请输入项目名称")
# QMessageBox.warning(self, "输入错误", "请输入项目名称")
UniversalMessageDialog.show_warning(self, "输入错误", "请输入项目名称", False, "确定")
return
if not self.selected_path:
QMessageBox.warning(self, "输入错误", "请选择项目保存位置")
UniversalMessageDialog.show_warning(self, "输入错误", "请选择项目保存位置", False, "确定")
return
# 使用项目管理器的验证方法进行全面检查
is_valid, error_message = self.project_manager.validate_project_creation(name, self.selected_path)
if not is_valid:
QMessageBox.warning(self, "创建失败", error_message)
UniversalMessageDialog.show_warning(self, "创建失败", error_message, False, "确定")
return
# 创建项目
@ -355,18 +432,18 @@ class CreateProjectDialog(QDialog):
项目目录: {project.project_dir}
项目文件结构已创建完成包含
models/ - 模型文件夹
textures/ - 贴图文件夹
scenes/ - 场景文件夹
project.json - 项目配置文件
? models/ - 模型文件夹
? textures/ - 贴图文件夹
? scenes/ - 场景文件夹
? project.json - 项目配置文件
您可以开始在此基础上开发您的应用程序"""
QMessageBox.information(self, "创建成功", success_msg)
UniversalMessageDialog.show_info(self, "创建成功", success_msg, False, "确定")
self.accept()
except Exception as e:
QMessageBox.critical(self, "创建失败", f"项目创建失败:{str(e)}")
UniversalMessageDialog.show_error(self, "创建失败", f"项目创建失败:{str(e)}", False, "确定")
def get_template_name(self, template_id):
"""获取模板名称"""
@ -445,6 +522,21 @@ class CreateProjectDialog(QDialog):
def eventFilter(self, watched, event):
"""处理标题栏拖动与按钮交互"""
if watched is self.title_bar:
if event.type() == QEvent.MouseButtonPress and event.button() == Qt.LeftButton:
self.drag_offset = event.globalPos() - self.frameGeometry().topLeft()
return True
if event.type() == QEvent.MouseMove and event.buttons() & Qt.LeftButton and self.drag_offset is not None:
self.move(event.globalPos() - self.drag_offset)
return True
if event.type() == QEvent.MouseButtonRelease and event.button() == Qt.LeftButton:
self.drag_offset = None
return True
return super().eventFilter(watched, event)
def center_dialog(self):
"""对话框居中"""
if self.parent():
@ -458,3 +550,18 @@ class CreateProjectDialog(QDialog):
x = (screen.width() - self.width()) // 2
y = (screen.height() - self.height()) // 2
self.move(x, y)

View File

@ -0,0 +1 @@

View File

@ -47,8 +47,29 @@ class IconManager:
'search': 'search_icon.png',
'infomation': 'infomation_icon.png',
'infomation_hover': 'infomation_hover_icon.png',
'infomation_check': 'infomation_check_icon.png',
# 为获取项目图片
'project_empty_icon': 'project_empty_icon.png',
# 卡片右键菜单
'refresh_projectcard': 'refresh_projectcard_icon.png',
'open_projectcard': 'open_projectcard_icon.png',
'remove_projectcard': 'remove_projectcard_icon.png',
# 导入对话框
'import_file': 'import_file_icon.png',
# 创建项目浏览图标
'file_icon': 'file_icon.png',
'empty_folder_icon': 'empty_folder_icon.png',
'close_bt_icon': 'close_bt_icon.png',
# 弹窗图标
'success_icon': 'success_icon.png',
'warning_icon': 'warning_icon.png',
'fail_icon': 'delete_fail_icon.png',
}
# 图标缓存
@ -202,4 +223,4 @@ class IconManager:
for icon_name in cls.ICON_FILES:
if not cls.icon_exists(icon_name):
missing_icons.append(icon_name)
return missing_icons
return missing_icons

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ from ui.import_project_dialog import ImportProjectDialog
from ui.project_settings_page import ProjectSettingsPage
from data.project_manager import ProjectManager
from ui.icon_manager import IconManager
from ui.widget import UniversalMessageDialog
class MainWindow(QMainWindow):
"""主窗口类"""
@ -282,20 +283,31 @@ class MainWindow(QMainWindow):
def show_about(self):
"""显示关于对话框"""
QMessageBox.about(self, "关于 MetaCore",
"MetaCore 项目管理平台\n\n"
"版本: 1.0.0\n"
"基于 PyQt5 开发\n\n"
"© 2024 MetaCore Team")
# QMessageBox.about(self, "关于 MetaCore",
# "MetaCore 项目管理平台\n\n"
# "版本: 1.0.0\n"
# "基于 PyQt5 开发\n\n"
# "© 2024 MetaCore Team")
UniversalMessageDialog.show_info(self,
"关于 MetaCore",
"MetaCore 项目管理平台\n\n"
"版本: 1.0.0\n"
"基于 PyQt5 开发\n\n"
"© 2024 MetaCore Team",
False, "确定")
def closeEvent(self, event):
"""关闭事件"""
reply = QMessageBox.question(self, '确认退出',
'确定要退出 MetaCore 吗?',
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
# reply = QMessageBox.question(self, '确认退出',
# '确定要退出 MetaCore 吗?',
# QMessageBox.Yes | QMessageBox.No,
# QMessageBox.No)
reply = UniversalMessageDialog.show_info(self,
'确认退出',
'确定要退出 MetaCore 吗?',
True, "", "")
if reply == QMessageBox.Yes:
if reply == QDialog.Accepted:
# 保存数据
self.project_manager.save_projects()
event.accept()

View File

@ -63,8 +63,8 @@ class ProjectArea(QWidget):
header_widget = QWidget()
header_widget.setObjectName("contentHeader")
header_layout = QHBoxLayout(header_widget)
header_layout.setContentsMargins(36, 26, 36, 28)
header_layout.setSpacing(16)
header_layout.setContentsMargins(36, 26, 36, 24)
header_layout.setSpacing(0)
breadcrumb_container = QWidget()
breadcrumb_container.setObjectName("breadcrumbContainer")
@ -115,7 +115,7 @@ class ProjectArea(QWidget):
# 网格布局
self.projects_layout = QGridLayout(self.projects_container)
self.projects_layout.setContentsMargins(36, 18, 6, 18)
self.projects_layout.setContentsMargins(36, 24, 6, 18)
self.projects_layout.setSpacing(24)
self.projects_layout.setAlignment(Qt.AlignTop) # 设置顶部对齐

View File

@ -15,7 +15,10 @@ from PyQt5.QtGui import *
from data.project_manager import ProjectManager, Project
from ui.icon_manager import IconManager
from ui.styles import StyleSheet
from ui.project_settings_page import ProjectSettingsPage
from ui.widget import UniversalMessageDialog
class ImageDisplayWidget(QWidget):
"""
一个专门用于显示带圆角图片的控件
@ -310,17 +313,45 @@ class ProjectCard(QWidget):
self.overlay_btn.setToolTip("确认删除项目")
else:
self.overlay_btn = QPushButton(self)
if IconManager.icon_exists('infomation'):
self.overlay_btn.setIcon(IconManager.get_icon('infomation', QSize(16, 16)))
self.overlay_btn.setIconSize(QSize(16, 16))
else:
self.overlay_btn.setText("")
self.overlay_btn.setObjectName("overlayInfoBtn")
self.overlay_btn.setCursor(Qt.PointingHandCursor)
self.overlay_btn.setAttribute(Qt.WA_Hover, True)
self.overlay_btn.installEventFilter(self)
self._set_overlay_icon("infomation", fallback_text="i")
self.overlay_btn.clicked.connect(self.show_context_menu)
# self.overlay_btn.setToolTip("项目信息")
# 设置按钮位置和大小
self.overlay_btn.setGeometry(248, 8, 20, 20)
def _set_overlay_icon(self, icon_name: str, fallback_text: str = ""):
"""Set overlay button icon with fallback text."""
if not hasattr(self, "overlay_btn"):
return
if IconManager.icon_exists(icon_name):
self.overlay_btn.setIcon(IconManager.get_icon(icon_name, QSize(16, 16)))
self.overlay_btn.setIconSize(QSize(16, 16))
self.overlay_btn.setText("")
else:
self.overlay_btn.setIcon(QIcon())
self.overlay_btn.setText(fallback_text)
def eventFilter(self, watched, event):
"""Handle hover/click states for the overlay button icon."""
if hasattr(self, "overlay_btn") and watched is self.overlay_btn:
if event.type() == QEvent.Enter:
self._set_overlay_icon("infomation_hover", fallback_text="i")
elif event.type() == QEvent.Leave:
self._set_overlay_icon("infomation", fallback_text="i")
elif event.type() == QEvent.MouseButtonPress and event.button() == Qt.LeftButton:
self._set_overlay_icon("infomation_check", fallback_text="v")
elif event.type() == QEvent.MouseButtonRelease and event.button() == Qt.LeftButton:
if self.overlay_btn.rect().contains(event.pos()):
self._set_overlay_icon("infomation_hover", fallback_text="i")
else:
self._set_overlay_icon("infomation", fallback_text="i")
return super().eventFilter(watched, event)
def create_bottom_overlay(self):
"""创建底部信息覆盖层"""
@ -557,10 +588,13 @@ class ProjectCard(QWidget):
def show_context_menu(self):
"""显示右键菜单"""
menu = QMenu(self)
menu.setWindowFlags(menu.windowFlags() | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint)
menu.setAttribute(Qt.WA_TranslucentBackground, True)
menu.setStyleSheet(StyleSheet.get_context_menu_style())
# 刷新预览图
if IconManager.icon_exists("Refresh"):
refresh_action = menu.addAction(IconManager.get_icon('Refresh'), "刷新预览图")
if IconManager.icon_exists("refresh_projectcard"):
refresh_action = menu.addAction(IconManager.get_icon('refresh_projectcard'), "刷新预览图")
else:
refresh_action = menu.addAction("🔄 刷新预览图")
refresh_action.triggered.connect(self.refresh_preview_image)
@ -568,8 +602,8 @@ class ProjectCard(QWidget):
menu.addSeparator()
# 在资源管理器显示
if IconManager.icon_exists('folder'):
show_in_explorer_action = menu.addAction(IconManager.get_icon('folder'), "在资源管理器显示")
if IconManager.icon_exists('open_projectcard'):
show_in_explorer_action = menu.addAction(IconManager.get_icon('open_projectcard'), "在资源管理器显示")
else:
show_in_explorer_action = menu.addAction("📁 在资源管理器显示")
show_in_explorer_action.triggered.connect(self.show_in_explorer)
@ -577,8 +611,8 @@ class ProjectCard(QWidget):
menu.addSeparator()
# 删除项目
if IconManager.icon_exists('delete'):
delete_action = menu.addAction(IconManager.get_icon('delete'), "移除项目")
if IconManager.icon_exists('remove_projectcard'):
delete_action = menu.addAction(IconManager.get_icon('remove_projectcard'), "移除项目")
else:
delete_action = menu.addAction("🗑️ 移除项目")
delete_action.triggered.connect(self.delete_project)
@ -602,18 +636,25 @@ class ProjectCard(QWidget):
# 使用项目管理器的验证方法进行全面检查
is_valid, error_message = self.project_manager.validate_project_open(project_path)
if not is_valid:
QMessageBox.warning(self, "无法打开项目", f"项目无法打开: {error_message}")
# QMessageBox.warning(self, "无法打开项目", f"项目无法打开: {error_message}")
UniversalMessageDialog.show_warning(self,
"无法打开项目", f"项目无法打开: {error_message}",
False, "确定")
return
# 验证通过,显示成功信息
# QMessageBox.information(self, "打开项目", f"正在打开项目: {self.project.title}")
# 使用question对话框提供明确的确认选项
reply = QMessageBox.question(self, "打开项目",
f"确定要打开项目: {self.project.title}?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes)
# reply = QMessageBox.question(self, "打开项目",
# f"确定要打开项目: {self.project.title}?",
# QMessageBox.Yes | QMessageBox.No,
# QMessageBox.Yes)
reply = UniversalMessageDialog.show_info(self,
"打开项目",
f"确定要打开项目: {self.project.title}?",
True, "确定", "取消")
if reply == QMessageBox.Yes:
if reply == QDialog.Accepted:
# 用户确认打开项目,继续执行打开逻辑
# TODO: 在这里添加实际的项目打开逻辑
print(f"正在打开项目路径: {project_path},正在启动应用程序: {self.project.title}")
@ -633,11 +674,15 @@ class ProjectCard(QWidget):
)
if not success:
QMessageBox.warning(
self,
"启动失败",
"无法启动,请检查打开项目路径。"
)
UniversalMessageDialog.show_warning(self,
"启动失败",
"无法启动,请检查打开项目路径。",
False, "确定")
# QMessageBox.warning(
# self,
# "启动失败",
# "无法启动,请检查打开项目路径。"
# )
# if success:
# QMessageBox.information(
# self,
@ -678,7 +723,11 @@ class ProjectCard(QWidget):
# except Exception as e:
# QMessageBox.critical(self, "错误", f"打开项目时发生错误:\n{str(e)}")
except Exception as e:
QMessageBox.critical(self, "错误", f"启动PyCharm时发生错误:\n{str(e)}")
# QMessageBox.critical(self, "错误", f"启动PyCharm时发生错误:\n{str(e)}")
UniversalMessageDialog.show_error(self,
"错误",
f"启动PyCharm时发生错误:\n{str(e)}",
False, "确定")
def on_pycharm_started(self):
"""PyCharm启动完成回调"""
@ -686,11 +735,15 @@ class ProjectCard(QWidget):
def on_project_method_called(self, target_project_path):
"""项目方法调用完成回调"""
QMessageBox.information(
self,
"操作完成",
f"已成功在PyCharm中打开项目: {target_project_path}"
)
# QMessageBox.information(
# self,
# "操作完成",
# f"已成功在PyCharm中打开项目: {target_project_path}"
# )
UniversalMessageDialog.show_info(self,
"操作完成",
f"已成功在PyCharm中打开项目: {target_project_path}",
False, "确定")
def show_in_explorer(self):
"""在资源管理器中显示项目目录"""
@ -699,7 +752,11 @@ class ProjectCard(QWidget):
project_path_str = self.project.project_dir if self.project.project_dir else self.project.path
if not project_path_str:
QMessageBox.warning(self, "路径不存在", "项目路径为空,请检查项目配置。")
# QMessageBox.warning(self, "路径不存在", "项目路径为空,请检查项目配置。")
UniversalMessageDialog.show_warning(self,
"路径不存在",
"项目路径为空,请检查项目配置。",
False, "确定")
return
# 使用pathlib处理路径
@ -713,8 +770,12 @@ class ProjectCard(QWidget):
# 检查项目路径是否存在
if not project_path.exists():
QMessageBox.warning(self, "路径不存在",
f"项目路径不存在或无效:\n{project_path}\n\n请检查项目是否已被移动或删除。")
# QMessageBox.warning(self, "路径不存在",
# f"项目路径不存在或无效:\n{project_path}\n\n请检查项目是否已被移动或删除。")
UniversalMessageDialog.show_warning(self,
"路径不存在",
f"项目路径不存在或无效:\n{project_path}\n\n请检查项目是否已被移动或删除。",
False, "确定")
return
# 获取操作系统类型
@ -725,7 +786,12 @@ class ProjectCard(QWidget):
# Windows使用os.startfile会自动使用系统默认的文件管理器
os.startfile(str(project_path))
except OSError as e:
QMessageBox.critical(self, "打开失败", f"无法打开资源管理器:\n{str(e)}")
# QMessageBox.critical(self, "打开失败", f"无法打开资源管理器:\n{str(e)}")
UniversalMessageDialog.show_error(self,
"打开失败",
f"无法打开资源管理器:\n{str(e)}",
False, "确定")
return
elif system == "darwin": # macOS
if project_path.is_file():
@ -752,39 +818,61 @@ class ProjectCard(QWidget):
# 最后尝试使用xdg-open
subprocess.run(['xdg-open', str(project_path)], check=True)
except (FileNotFoundError, subprocess.CalledProcessError):
QMessageBox.warning(self, "无法打开文件管理器",
"系统中没有找到合适的文件管理器。\n"
f"请手动打开路径: {project_path}")
# QMessageBox.warning(self, "无法打开文件管理器",
# "系统中没有找到合适的文件管理器。\n"
# f"请手动打开路径: {project_path}")
UniversalMessageDialog.show_warning(self,
"无法打开文件管理器",
"系统中没有找到合适的文件管理器。\n"
f"请手动打开路径: {project_path}",
False, "确定")
print(f"成功在资源管理器中打开: {project_path}")
except subprocess.CalledProcessError as e:
QMessageBox.critical(self, "打开失败",
f"无法打开资源管理器:\n{str(e)}")
# QMessageBox.critical(self, "打开失败",
# f"无法打开资源管理器:\n{str(e)}")
UniversalMessageDialog.show_error(self,
"打开失败",
f"无法打开资源管理器:\n{str(e)}",
False, "确定")
except Exception as e:
QMessageBox.critical(self, "错误",
f"打开资源管理器时发生错误:\n{str(e)}")
# QMessageBox.critical(self, "错误",
# f"打开资源管理器时发生错误:\n{str(e)}")
UniversalMessageDialog.show_error(self,
"错误",
f"打开资源管理器时发生错误:\n{str(e)}",
False, "确定")
def delete_project(self):
"""删除项目"""
reply = QMessageBox.question(self, "确认移除",
f"确定要移除项目 \"{self.project.title}\" 吗?\n此操作不可撤销。",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
# reply = QMessageBox.question(self, "确认移除",
# f"确定要移除项目 \"{self.project.title}\" 吗?\n此操作不可撤销。",
# QMessageBox.Yes | QMessageBox.No,
# QMessageBox.No)
reply = UniversalMessageDialog.show_info(self,
"确认移除",
f"确定要移除项目 \"{self.project.title}\" 吗?\n此操作不可撤销。",
True, "确定", "取消")
if reply == QMessageBox.Yes:
if reply == QDialog.Accepted:
self.project_manager.remove_project(self.project.id)
def confirm_delete_project(self):
"""确认删除待删除状态的项目"""
reply = QMessageBox.question(self, "确认删除项目",
f"确定要永久删除项目 \"{self.project.title}\" 吗?\n"
f"此操作不可撤销。\n\n"
f"提示:如果项目目录已恢复,您可以点击项目卡片来恢复项目。",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
# reply = QMessageBox.question(self, "确认删除项目",
# f"确定要永久删除项目 \"{self.project.title}\" 吗?\n"
# f"此操作不可撤销。\n\n"
# f"提示:如果项目目录已恢复,您可以点击项目卡片来恢复项目。",
# QMessageBox.Yes | QMessageBox.No,
# QMessageBox.No)
reply = UniversalMessageDialog.show_info(self,
"确认删除项目",
f"确定要永久删除项目 \"{self.project.title}\" 吗?\n"
f"此操作不可撤销。\n\n",
True, "确定", "取消")
if reply == QMessageBox.Yes:
if reply == QDialog.Accepted:
self.project_manager.confirm_delete_project(self.project.id)
def update_display(self):
@ -852,12 +940,20 @@ class ProjectCard(QWidget):
def try_restore_project(self):
"""尝试恢复待删除状态的项目"""
if self.project_manager.restore_project(self.project.id):
QMessageBox.information(self, "项目已恢复",
f"项目 \"{self.project.title}\" 已成功恢复!")
# QMessageBox.information(self, "项目已恢复",
# f"项目 \"{self.project.title}\" 已成功恢复!")
UniversalMessageDialog.show_info(self,
"项目已恢复",
f"项目 \"{self.project.title}\" 已成功恢复!",
False, "确定")
else:
QMessageBox.information(self, "项目目录不存在",
f"项目 \"{self.project.title}\" 的目录仍然不存在:\n{self.project.project_dir}\n\n"
f"提示:当您恢复项目目录后,系统会自动检测并恢复项目状态,无需手动操作。")
# QMessageBox.information(self, "项目目录不存在",
# f"项目 \"{self.project.title}\" 的目录仍然不存在:\n{self.project.project_dir}\n\n"
# f"提示:当您恢复项目目录后,系统会自动检测并恢复项目状态,无需手动操作。")
UniversalMessageDialog.show_info(self,
"项目目录不存在",
f"项目 \"{self.project.title}\" 的目录仍然不存在:\n{self.project.project_dir}\n\n"
f"提示:当您恢复项目目录后,系统会自动检测并恢复项目状态,无需手动操作。")
def enterEvent(self, event):
"""鼠标进入事件"""
@ -914,7 +1010,11 @@ class ProjectCard(QWidget):
# 获取项目路径
project_path = self.project.project_dir if self.project.project_dir else self.project.path
if not project_path or not Path(project_path).exists():
QMessageBox.warning(self, "路径不存在", "项目路径不存在,无法生成预览图。")
# QMessageBox.warning(self, "路径不存在", "项目路径不存在,无法生成预览图。")
UniversalMessageDialog.show_warning(self,
"路径不存在",
"项目路径不存在,无法生成预览图。",
False, "确定")
return
# 显示进度提示
@ -958,11 +1058,18 @@ class ProjectCard(QWidget):
# 刷新显示以显示默认图标
self.refresh_image_display()
QMessageBox.warning(self, "生成失败",
"无法生成预览图,已使用默认图标。\n\n可能原因:\n"
"• 项目目录中没有图片文件\n"
"• 图片文件格式不支持\n"
"• 权限不足")
# QMessageBox.warning(self, "生成失败",
# "无法生成预览图,已使用默认图标。\n\n可能原因\n"
# "• 项目目录中没有图片文件\n"
# "• 图片文件格式不支持\n"
# "• 权限不足")
UniversalMessageDialog.show_warning(self,
"生成失败",
"无法生成预览图,已使用默认图标。\n\n可能原因: \n"
"• 项目目录中没有图片文件\n"
"• 图片文件格式不支持\n"
"• 权限不足",
False, "确定")
except Exception as e:
# 发生异常时也使用project_empty_icon作为默认图片
@ -972,7 +1079,11 @@ class ProjectCard(QWidget):
# 刷新显示以显示默认图标
self.refresh_image_display()
QMessageBox.critical(self, "错误", f"刷新预览图时发生错误,已使用默认图标:\n{str(e)}")
# QMessageBox.critical(self, "错误", f"刷新预览图时发生错误,已使用默认图标:\n{str(e)}")
UniversalMessageDialog.show_error(self,
"错误",
f"刷新预览图时发生错误,已使用默认图标:\n{str(e)}",
False, "确定")
finally:
# 恢复按钮状态
if target_btn:

View File

@ -34,131 +34,209 @@ class ProjectSettingsPage(QWidget):
def init_ui(self):
"""初始化用户界面"""
layout = QVBoxLayout(self)
layout.setContentsMargins(30, 30, 30, 30)
layout.setSpacing(20)
# 页面标题
self.create_page_header(layout)
# 默认项目位置设置
self.create_default_location_section(layout)
self.setObjectName("settingsPage")
self.setAttribute(Qt.WA_StyledBackground, True)
self.create_open_location_section(layout)
# 项目创建设置
# self.create_project_creation_section(layout)
# 按钮区域
self.create_button_section(layout)
# 添加弹性空间
layout.addStretch()
def create_page_header(self, layout):
"""创建页面标题"""
self.main_layout = QVBoxLayout(self)
self.main_layout.setContentsMargins(0, 30, 24, 30)
self.main_layout.setSpacing(0)
self.content_container = QFrame()
self.content_container.setObjectName("settingsContentArea")
self.content_container.setFrameShape(QFrame.NoFrame)
self.content_container.setAttribute(Qt.WA_StyledBackground, True)
self.main_layout.addWidget(self.content_container)
self.content_layout = QVBoxLayout(self.content_container)
self.content_layout.setContentsMargins(0, 0, 0, 0)
self.content_layout.setSpacing(0)
header_widget = self.create_page_header()
self.content_layout.addWidget(header_widget)
body_widget = self.create_settings_body()
self.content_layout.addWidget(body_widget)
self.content_layout.addStretch()
def create_page_header(self):
"""创建面包屑与标题区域"""
header_widget = QWidget()
header_widget.setObjectName("contentHeader")
header_layout = QVBoxLayout(header_widget)
header_layout.setContentsMargins(0, 0, 0, 0)
header_layout.setSpacing(8)
header_layout.setContentsMargins(36, 32, 36, 24)
header_layout.setSpacing(0)
breadcrumb_widget = QWidget()
breadcrumb_widget.setObjectName("breadcrumbContainer")
breadcrumb_layout = QHBoxLayout(breadcrumb_widget)
breadcrumb_layout.setContentsMargins(0, 0, 0, 0)
breadcrumb_layout.setSpacing(8)
breadcrumb_base = QLabel("设置中心")
breadcrumb_base.setObjectName("breadcrumbBase")
breadcrumb_layout.addWidget(breadcrumb_base)
breadcrumb_separator = QLabel("/")
breadcrumb_separator.setObjectName("breadcrumbSeparator")
breadcrumb_layout.addWidget(breadcrumb_separator)
breadcrumb_current = QLabel("项目设置")
breadcrumb_current.setObjectName("breadcrumbCurrent")
breadcrumb_layout.addWidget(breadcrumb_current)
breadcrumb_layout.addStretch()
header_layout.addWidget(breadcrumb_widget)
# title_label = QLabel("项目设置")
# title_label.setObjectName("settingsTitle")
# header_layout.addWidget(title_label)
# subtitle_label = QLabel("配置项目创建和管理的相关设置。")
# subtitle_label.setObjectName("settingsSubtitle")
# subtitle_label.setWordWrap(True)
# header_layout.addWidget(subtitle_label)
return header_widget
def create_settings_body(self):
"""创建设置内容区域,结构与项目区域组件保持一致"""
scroll_area = QScrollArea()
scroll_area.setObjectName("projectScrollArea")
scroll_area.setFrameShape(QFrame.NoFrame)
scroll_area.setWidgetResizable(True)
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
body_container = QWidget()
body_container.setObjectName("settingsBody")
body_layout = QVBoxLayout(body_container)
# body_layout.setContentsMargins(36, 24, 6, 18)
body_layout.setSpacing(24)
settings_card = self.create_settings_card()
body_layout.addWidget(settings_card)
body_layout.addStretch()
scroll_area.setWidget(body_container)
# 主标题
title_label = QLabel("项目设置")
title_label.setObjectName("pageTitle")
header_layout.addWidget(title_label)
# 副标题
subtitle_label = QLabel("配置项目创建和管理的相关设置")
subtitle_label.setObjectName("pageSubtitle")
header_layout.addWidget(subtitle_label)
layout.addWidget(header_widget)
def create_default_location_section(self, layout):
"""创建默认项目位置设置区域"""
# 分组框
group_box = QGroupBox("默认项目位置")
group_box.setObjectName("settingsGroup")
group_layout = QVBoxLayout(group_box)
group_layout.setSpacing(15)
# 说明文字
desc_label = QLabel("设置新项目的默认创建位置。")
desc_label.setObjectName("settingsDescription")
return scroll_area
def create_settings_card(self):
"""创建主体设置卡片内容"""
card = QFrame()
card.setObjectName("settingsCard")
card.setFrameShape(QFrame.NoFrame)
card.setAttribute(Qt.WA_StyledBackground, True)
card_layout = QVBoxLayout(card)
card_layout.setContentsMargins(0, 0, 0, 0)
card_layout.setSpacing(0)
sections = [
{
"title": "默认项目位置",
"description": "设置新项目的默认创建位置。",
"placeholder": "选择默认项目创建位置...",
"browse": self.browse_default_location,
"reset": self.reset_default_location,
"attr": "path_input",
},
{
"title": "打开设置",
"description": "设置新项目的默认打开位置。",
"placeholder": "选择默认项目打开位置...",
"browse": self.browse_open_location,
"reset": self.reset_open_location,
"attr": "open_path_input",
},
]
total = len(sections)
for index, section_info in enumerate(sections):
is_first = index == 0
is_last = index == total - 1
section_widget = self.create_path_section(
title=section_info["title"],
description=section_info["description"],
placeholder=section_info["placeholder"],
browse_handler=section_info["browse"],
reset_handler=section_info["reset"],
input_attr=section_info["attr"],
is_first=is_first,
is_last=is_last,
)
card_layout.addWidget(section_widget)
return card
def create_path_section(
self,
title,
description,
placeholder,
browse_handler,
reset_handler,
input_attr,
is_first=False,
is_last=False,
):
"""创建路径类型设置行"""
section = QFrame()
section.setObjectName("settingsRow")
section.setFrameShape(QFrame.NoFrame)
section.setAttribute(Qt.WA_StyledBackground, True)
section.setProperty("isFirstRow", "true" if is_first else "false")
section.setProperty("isLastRow", "true" if is_last else "false")
section.style().unpolish(section)
section.style().polish(section)
section_layout = QVBoxLayout(section)
bottom_margin = 24 if is_last else 0
section_layout.setContentsMargins(32, 24, 32, bottom_margin)
section_layout.setSpacing(0)
title_label = QLabel(title)
title_label.setObjectName("settingsSectionTitle")
section_layout.addWidget(title_label)
section_layout.addSpacing(8)
desc_label = QLabel(description)
desc_label.setObjectName("settingsSectionDesc")
desc_label.setWordWrap(True)
group_layout.addWidget(desc_label)
# 当前路径显示和选择
path_widget = QWidget()
path_layout = QHBoxLayout(path_widget)
path_layout.setContentsMargins(0, 0, 0, 0)
path_layout.setSpacing(10)
# 路径输入框
self.path_input = QLineEdit()
self.path_input.setObjectName("pathInput")
self.path_input.setPlaceholderText("选择默认项目创建位置...")
self.path_input.setReadOnly(True) # 只读,通过按钮选择
path_layout.addWidget(self.path_input)
# 浏览按钮
self.browse_btn = QPushButton("浏览...")
self.browse_btn.setObjectName("browseBtn")
self.browse_btn.clicked.connect(self.browse_default_location)
path_layout.addWidget(self.browse_btn)
# 重置按钮
self.reset_btn = QPushButton("重置")
self.reset_btn.setObjectName("resetBtn")
self.reset_btn.clicked.connect(self.reset_default_location)
path_layout.addWidget(self.reset_btn)
group_layout.addWidget(path_widget)
layout.addWidget(group_box)
section_layout.addWidget(desc_label)
section_layout.addSpacing(15)
def create_open_location_section(self, layout):
"""创建打开位置设置区域"""
# 分组框
group_box = QGroupBox("打开位置")
group_box.setObjectName("settingsGroup")
group_layout = QVBoxLayout(group_box)
group_layout.setSpacing(15)
field_row = QWidget()
field_row.setObjectName("settingsFieldRow")
field_layout = QHBoxLayout(field_row)
field_layout.setContentsMargins(19, 0, 0, 0)
field_layout.setSpacing(12)
# 说明文字
desc_label = QLabel("设置项目文件的默认打开位置。")
desc_label.setObjectName("settingsDescription")
desc_label.setWordWrap(True)
group_layout.addWidget(desc_label)
path_input = QLineEdit()
path_input.setObjectName("settingsPathInput")
path_input.setPlaceholderText(placeholder)
path_input.setReadOnly(True)
path_input.setFixedHeight(48)
# path_input.setMinimumWidth(1099)
field_layout.addWidget(path_input, stretch=1)
# 当前路径显示和选择
path_widget = QWidget()
path_layout = QHBoxLayout(path_widget)
path_layout.setContentsMargins(0, 0, 0, 0)
path_layout.setSpacing(10)
browse_btn = QPushButton("浏览...")
browse_btn.setObjectName("settingsPrimaryBtn")
browse_btn.setCursor(Qt.PointingHandCursor)
browse_btn.setFixedSize(175, 48)
browse_btn.clicked.connect(browse_handler)
field_layout.addWidget(browse_btn)
# 路径输入框
self.open_path_input = QLineEdit()
self.open_path_input.setObjectName("pathInput")
self.open_path_input.setPlaceholderText("选择默认项目打开位置...")
self.open_path_input.setReadOnly(True) # 只读,通过按钮选择
path_layout.addWidget(self.open_path_input)
reset_btn = QPushButton("重置")
reset_btn.setObjectName("settingsGhostBtn")
reset_btn.setCursor(Qt.PointingHandCursor)
reset_btn.setFixedSize(175, 48)
reset_btn.clicked.connect(reset_handler)
field_layout.addWidget(reset_btn)
# 浏览按钮
self.open_browse_btn = QPushButton("浏览...")
self.open_browse_btn.setObjectName("browseBtn")
self.open_browse_btn.clicked.connect(self.browse_open_location)
path_layout.addWidget(self.open_browse_btn)
section_layout.addWidget(field_row)
# 重置按钮
self.open_reset_btn = QPushButton("重置")
self.open_reset_btn.setObjectName("resetBtn")
self.open_reset_btn.clicked.connect(self.reset_open_location)
path_layout.addWidget(self.open_reset_btn)
group_layout.addWidget(path_widget)
layout.addWidget(group_box)
setattr(self, input_attr, path_input)
return section
def browse_open_location(self):
"""浏览选择打开位置"""
@ -179,6 +257,7 @@ class ProjectSettingsPage(QWidget):
"""重置为默认打开位置"""
default_path = self.get_default_projects_path()
self.open_path_input.setText(default_path)
self.save_settings_immediately("default_open_location", self.open_path_input.text())
def create_project_creation_section(self, layout):
"""创建项目创建设置区域"""
@ -238,6 +317,7 @@ class ProjectSettingsPage(QWidget):
"""重置为默认位置"""
default_path = self.get_default_projects_path()
self.path_input.setText(default_path)
self.save_settings_immediately("default_project_location", self.path_input.text())
def load_settings(self):
"""加载保存的设置"""

View File

@ -140,19 +140,19 @@ class Sidebar(QWidget):
# 我的项目分组
self.create_nav_section(nav_layout, "我的项目", [
("📊", "项目概述", "overview"),
("📋", "项目管理", "management"),
# ("📋", "项目管理", "management"),
])
# 资源管理分组
self.create_nav_section(nav_layout, "资源管理", [
("🏷", "资源分类", "resource_category"),
("📦", "资源管理", "resource_management"),
])
# self.create_nav_section(nav_layout, "资源管理", [
# ("🏷", "资源分类", "resource_category"),
# ("📦", "资源管理", "resource_management"),
# ])
# 设置中心分组
self.create_nav_section(nav_layout, "设置中心", [
("📁", "项目设置", "project_settings"),
("", "系统设置", "system_settings"),
# ("⚙", "系统设置", "system_settings"),
])
nav_layout.addStretch()
@ -184,14 +184,14 @@ class Sidebar(QWidget):
items_widget = QWidget()
items_widget.setObjectName("navItems")
items_layout = QVBoxLayout(items_widget)
items_layout.setContentsMargins(0, 10, 0, 0)
items_layout.setContentsMargins(50, 10, 0, 0)
items_layout.setSpacing(10)
# 创建导航项
for icon, text, filter_type in items:
nav_item = self.create_nav_item(icon, text, filter_type)
items_layout.addWidget(nav_item)
items_layout.addWidget(nav_item, alignment=Qt.AlignLeft)
section_layout.addWidget(items_widget)
# 展开/收起功能
@ -229,6 +229,8 @@ class Sidebar(QWidget):
item_btn.setObjectName("navItem")
item_btn.setCheckable(True)
item_btn.setFixedSize(QSize(186, 30))
item_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
# 设置默认选中
if filter_type == "overview":

File diff suppressed because it is too large Load Diff

411
MetaCore/ui/widget.py Normal file
View File

@ -0,0 +1,411 @@
import os
import re
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout,
QLineEdit, QPushButton, QLabel,
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, QFont
from PyQt5.sip import wrapinstance
from ui.icon_manager import IconManager
class UniversalMessageDialog(QDialog):
"""通用消息对话框类 - 支持不同图标和按钮配置"""
# 消息类型枚举
SUCCESS = "success_icon"
WARNING = "warning_icon"
ERROR = "fail_icon"
INFO = "info"
def __init__(self, parent, title, message, message_type=INFO, show_cancel=True,
confirm_text="确认", cancel_text="取消", icon_size=QSize(20, 20)):
"""
初始化通用消息对话框
Args:
parent: 父窗口
title: 对话框标题
message: 消息内容
message_type: 消息类型 (SUCCESS, WARNING, ERROR, INFO)
show_cancel: 是否显示取消按钮
confirm_text: 确认按钮文字
cancel_text: 取消按钮文字
icon_size: 图标尺寸
"""
super().__init__(parent)
self.setWindowTitle(title)
self.setObjectName("universalMessageDialog")
self.setModal(True)
self.resize(508, 134)
self.setMinimumSize(508, 134)
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground, True)
# 对话框配置
self.message_type = message_type
self.show_cancel = show_cancel
self.confirm_text = confirm_text
self.cancel_text = cancel_text
self.icon_size = icon_size
# 拖拽相关
self.dragging = False
self.drag_position = QPoint()
# 图标管理
self.icon_manager = IconManager()
self._title_icon_size = QSize(18, 18)
self._icon_close = self.icon_manager.get_icon('close_bt_icon', self._title_icon_size)
# 根据消息类型获取对应图标
self._message_icon = self._get_message_icon()
# 设置样式
self._setup_styles()
# 创建UI
self._create_ui(message)
def _get_message_icon(self):
"""根据消息类型获取对应图标"""
icon_map = {
self.SUCCESS: 'success_icon',
self.WARNING: 'warning_icon',
self.ERROR: 'fail_icon',
self.INFO: 'success_icon' # 默认使用成功图标
}
icon_name = icon_map.get(self.message_type, 'success_icon')
return self.icon_manager.get_icon(icon_name, self.icon_size)
def _setup_styles(self):
"""设置对话框样式"""
self.setStyleSheet("""
QDialog#universalMessageDialog {
background-color: transparent;
border: none;
}
QFrame#baseFrame {
background-color: #19191B;
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;
}
QFrame#titleSeparator {
background-color: #3E3E42;
border: none;
min-height: 1px;
max-height: 1px;
}
QWidget#contentWidget {
background-color: transparent;
border: none;
}
QLabel#iconLabel {
background-color: transparent;
}
QLabel#messageLabel {
background-color: transparent;
color: #EBEBEB;
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
font-size: 13px;
font-weight: 400;
letter-spacing: 0.6px;
line-height: 1.4;
padding: 0px;
margin: 0px;
}
QWidget#buttonWidget {
background-color: transparent;
}
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;
max-width: 90px;
min-height: 28px;
max-height: 28px;
}
QPushButton:hover {
background-color: #3067C0;
color: #FFFFFF;
}
QPushButton:pressed {
background-color: #2556A0;
color: #FFFFFF;
}
QPushButton#primaryButton {
background-color: rgba(89, 98, 118, 0.5);
border: none;
color: #EBEBEB;
font-weight: 300;
}
QPushButton#primaryButton:hover {
background-color: #2556A0;
}
QPushButton#primaryButton:pressed {
background-color: #1E4A8C;
}
QPushButton#secondaryButton {
background-color: rgba(89, 98, 118, 0.5);
border: none;
color: #EBEBEB;
}
QPushButton#secondaryButton:hover {
background-color: #3067C0;
color: #FFFFFF;
}
QPushButton#secondaryButton:pressed {
background-color: #2556A0;
color: #FFFFFF;
}
""")
def _create_ui(self, message):
"""构建用户界面"""
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(25, 14, 25, 14)
base_layout.setSpacing(0)
self._create_title_bar()
base_layout.addWidget(self.title_bar)
base_layout.addSpacing(4)
title_separator = QFrame()
title_separator.setObjectName('titleSeparator')
title_separator.setFrameShape(QFrame.HLine)
title_separator.setFrameShadow(QFrame.Plain)
base_layout.addWidget(title_separator)
base_layout.addSpacing(10)
content_widget = QWidget()
content_widget.setObjectName('contentWidget')
content_layout = QVBoxLayout(content_widget)
content_layout.setContentsMargins(0, 0, 0, 0)
content_layout.setSpacing(10)
message_area = QHBoxLayout()
message_area.setContentsMargins(0, 0, 0, 0)
message_area.setSpacing(10)
# 用一个垂直布局包裹icon_label确保顶部对齐
icon_vbox = QVBoxLayout()
icon_vbox.setContentsMargins(0, 0, 0, 0)
icon_vbox.setSpacing(0)
icon_label = QLabel()
icon_label.setObjectName('iconLabel')
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)
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')
self.message_label.setWordWrap(True)
self.message_label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
self.message_label.setMinimumHeight(self.icon_size.height())
self.message_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
message_area.addWidget(self.message_label, 1)
message_area.addStretch()
content_layout.addLayout(message_area)
base_layout.addWidget(content_widget)
base_layout.addSpacing(10)
button_widget = QWidget()
button_widget.setObjectName('buttonWidget')
button_layout = QHBoxLayout(button_widget)
button_layout.setContentsMargins(0, 0, 0, 0)
button_layout.setSpacing(10)
button_layout.addStretch()
if self.show_cancel:
self.cancel_button = QPushButton(self.cancel_text)
self.cancel_button.setObjectName("secondaryButton")
self.cancel_button.clicked.connect(self.reject)
self.cancel_button.setFixedSize(90, 28)
button_layout.addWidget(self.cancel_button)
self.confirm_button = QPushButton(self.confirm_text)
self.confirm_button.setObjectName("primaryButton")
self.confirm_button.clicked.connect(self.accept)
self.confirm_button.setFixedSize(90, 28)
button_layout.addWidget(self.confirm_button)
self.confirm_button.setDefault(True)
self.confirm_button.setAutoDefault(True)
if self.show_cancel:
self.cancel_button.setAutoDefault(False)
base_layout.addWidget(button_widget)
main_layout.addWidget(base_frame)
def _create_title_bar(self):
"""创建自定义标题栏"""
self.title_bar = QFrame()
self.title_bar.setObjectName("titleBar")
title_layout = QHBoxLayout(self.title_bar)
title_layout.setContentsMargins(0, 0, 0, 0)
title_layout.setSpacing(0)
self.title_label = QLabel(self.windowTitle())
self.title_label.setObjectName("titleLabel")
self.title_label.setAlignment(Qt.AlignVCenter | Qt.AlignLeft)
title_layout.addWidget(self.title_label, 1)
title_layout.addStretch()
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._apply_title_bar_icons()
title_layout.addWidget(controls)
def _apply_title_bar_icons(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 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)
@staticmethod
def show_success(parent, title, message, show_cancel=False, confirm_text="确认"):
"""显示成功消息对话框"""
dialog = UniversalMessageDialog(
parent, title, message,
UniversalMessageDialog.SUCCESS,
show_cancel, confirm_text
)
return dialog.exec_()
@staticmethod
def show_warning(parent, title, message, show_cancel=True, confirm_text="确认", cancel_text="取消"):
"""显示警告消息对话框"""
dialog = UniversalMessageDialog(
parent, title, message,
UniversalMessageDialog.WARNING,
show_cancel, confirm_text, cancel_text
)
return dialog.exec_()
@staticmethod
def show_error(parent, title, message, show_cancel=False, confirm_text="确认"):
"""显示错误消息对话框"""
dialog = UniversalMessageDialog(
parent, title, message,
UniversalMessageDialog.ERROR,
show_cancel, confirm_text
)
return dialog.exec_()
@staticmethod
def show_info(parent, title, message, show_cancel=True, confirm_text="确认", cancel_text="取消"):
"""显示信息消息对话框"""
dialog = UniversalMessageDialog(
parent, title, message,
UniversalMessageDialog.INFO,
show_cancel, confirm_text, cancel_text
)
return dialog.exec_()

View File

@ -1,4 +1,26 @@
[
{
"id": 4,
"title": "aasd",
"date": "2025-10-17 16:49:10",
"type": "empty",
"image": "C:\\Users\\29381\\Desktop\\aasd\\aasd.png",
"path": "C:\\Users\\29381\\Desktop",
"project_dir": "C:\\Users\\29381\\Desktop\\aasd",
"description": "asd",
"status": "normal"
},
{
"id": 3,
"title": "a",
"date": "2025-10-17 16:48:59",
"type": "imported",
"image": "C:\\Users\\29381\\Desktop\\a\\a.png",
"path": "C:\\Users\\29381\\Desktop",
"project_dir": "C:\\Users\\29381\\Desktop\\a",
"description": "从文件夹 a 导入",
"status": "normal"
},
{
"id": 2,
"title": "ab",
@ -15,7 +37,7 @@
"title": "XNWX",
"date": "2025-10-14 18:05:51",
"type": "imported",
"image": "C:\\Users\\29381\\Desktop\\MetaCore-startup\\MetaCore\\Resources\\ProjectPreviews\\preview_1_1760491477496.png",
"image": "C:\\Users\\29381\\Desktop\\MetaCore-startup\\MetaCore\\Resources\\ProjectPreviews\\preview_1_1760628516144.png",
"path": "C:\\Users\\29381\\Desktop",
"project_dir": "C:\\Users\\29381\\Desktop\\XNWX",
"description": "从文件夹 XNWX 导入",

47
scripts/example_script.py Normal file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
示例脚本 - 演示如何编写脚本
"""
from core.script_system import ScriptBase
class ExampleScript(ScriptBase):
"""示例脚本类"""
def __init__(self):
super().__init__()
self.counter = 0
self.rotation_speed = 30.0 # 度/秒
def start(self):
"""脚本开始时调用"""
self.log("示例脚本开始运行!")
self.log(f"挂载到对象: {self.gameObject.getName()}")
def update(self, dt):
"""每帧更新"""
self.counter += 1
# 每60帧输出一次信息
if self.counter % 60 == 0:
self.log(f"运行了 {self.counter}")
# 让对象旋转
if self.transform:
current_h = self.transform.getH()
new_h = current_h + self.rotation_speed * dt
self.transform.setH(new_h)
def on_destroy(self):
"""脚本销毁时调用"""
self.log("示例脚本被销毁")
def on_enable(self):
"""脚本启用时调用"""
self.log("示例脚本被启用")
def on_disable(self):
"""脚本禁用时调用"""
self.log("示例脚本被禁用")

133
tests/test_button_fix.py Normal file
View File

@ -0,0 +1,133 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试右上角按钮点击修复
"""
import sys
import os
from pathlib import Path
# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
sys.path.insert(0, str(project_root / 'MetaCore'))
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from data.project_manager import ProjectManager, Project
from ui.project_card import ProjectCard
from ui.styles import StyleSheet
class TestButtonWindow(QWidget):
"""测试按钮修复的窗口"""
def __init__(self):
super().__init__()
self.setWindowTitle("右上角按钮修复测试")
self.setGeometry(100, 100, 800, 600)
self.setStyleSheet(StyleSheet.get_main_stylesheet())
# 设置深色背景
self.setStyleSheet("""
QWidget {
background-color: #1a1a1a;
color: #ffffff;
}
""" + StyleSheet.get_main_stylesheet())
self.init_ui()
def init_ui(self):
"""初始化UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(40, 40, 40, 40)
layout.setSpacing(20)
# 标题
title = QLabel("右上角按钮修复测试")
title.setStyleSheet("""
QLabel {
font-size: 24px;
font-weight: bold;
color: #ffffff;
margin-bottom: 20px;
}
""")
title.setAlignment(Qt.AlignCenter)
layout.addWidget(title)
# 说明文字
description = QLabel("""
测试说明
点击右上角的信息按钮应该显示上下文菜单不应该闪退
菜单应该包含刷新预览图在资源管理器显示移除项目
所有菜单项都应该能正常工作
""")
description.setStyleSheet("""
QLabel {
font-size: 14px;
color: #cccccc;
line-height: 1.5;
margin-bottom: 20px;
}
""")
layout.addWidget(description)
# 创建测试卡片
self.create_test_cards(layout)
def create_test_cards(self, layout):
"""创建测试卡片"""
# 卡片容器
cards_widget = QWidget()
cards_layout = QHBoxLayout(cards_widget)
cards_layout.setSpacing(24)
cards_layout.setContentsMargins(20, 20, 20, 20)
# 创建项目管理器
project_manager = ProjectManager()
# 创建测试项目
test_project = Project(
id='test_1',
title='测试项目',
date='2024-10-14 15:30',
project_type='smart',
status='active',
path="/test/path/1",
project_dir="/test/path/1",
image=None
)
# 创建网格视图卡片
grid_card = ProjectCard(test_project, project_manager, view_mode="grid")
cards_layout.addWidget(grid_card)
# 创建列表视图卡片
list_card = ProjectCard(test_project, project_manager, view_mode="list")
cards_layout.addWidget(list_card)
cards_layout.addStretch()
layout.addWidget(cards_widget)
layout.addStretch()
def main():
"""主函数"""
app = QApplication(sys.argv)
# 设置应用程序属性
app.setApplicationName("按钮修复测试")
app.setOrganizationName("MetaCore")
# 创建并显示测试窗口
window = TestButtonWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@

218
tests/test_corner_radius.py Normal file
View File

@ -0,0 +1,218 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试项目卡片5px圆角效果
"""
import sys
import os
from pathlib import Path
# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
sys.path.insert(0, str(project_root / 'MetaCore'))
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from data.project_manager import ProjectManager, Project
from ui.project_card import ProjectCard
from ui.styles import StyleSheet
class CornerRadiusTestWindow(QWidget):
"""测试5px圆角效果的窗口"""
def __init__(self):
super().__init__()
self.setWindowTitle("项目卡片5px圆角测试")
self.setGeometry(100, 100, 1000, 700)
self.setStyleSheet(StyleSheet.get_main_stylesheet())
# 设置深色背景
self.setStyleSheet("""
QWidget {
background-color: #1a1a1a;
color: #ffffff;
}
""" + StyleSheet.get_main_stylesheet())
self.init_ui()
def init_ui(self):
"""初始化UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(40, 40, 40, 40)
layout.setSpacing(20)
# 标题
title = QLabel("项目卡片5px圆角效果测试")
title.setStyleSheet("""
QLabel {
font-size: 24px;
font-weight: bold;
color: #ffffff;
margin-bottom: 20px;
}
""")
title.setAlignment(Qt.AlignCenter)
layout.addWidget(title)
# 说明文字
description = QLabel("""
测试说明
所有项目卡片现在都使用5px的圆角
包括卡片外框背景图片和列表视图图标
圆角效果应该统一且美观
""")
description.setStyleSheet("""
QLabel {
font-size: 14px;
color: #cccccc;
line-height: 1.5;
margin-bottom: 20px;
}
""")
layout.addWidget(description)
# 创建测试卡片网格
self.create_test_grid(layout)
def create_test_grid(self, layout):
"""创建测试卡片网格"""
# 滚动区域
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
scroll_area.setStyleSheet("""
QScrollArea {
border: none;
background: transparent;
}
""")
# 网格容器
grid_widget = QWidget()
grid_layout = QGridLayout(grid_widget)
grid_layout.setSpacing(24)
grid_layout.setContentsMargins(20, 20, 20, 20)
# 创建项目管理器
project_manager = ProjectManager()
# 创建测试项目数据
test_projects = [
{
'id': '1',
'title': '智能家居系统',
'date': '2024-10-14 15:30',
'type': 'smart',
'status': 'active',
'image': None
},
{
'id': '2',
'title': 'VR体验项目',
'date': '2024-10-13 09:15',
'type': 'vr',
'status': 'active',
'image': str(project_root / 'MetaCore' / 'Resources' / 'ProjectPreviews' / 'preview_6_1760434704537.png') if (project_root / 'MetaCore' / 'Resources' / 'ProjectPreviews' / 'preview_6_1760434704537.png').exists() else None
},
{
'id': '3',
'title': '工业控制系统',
'date': '2024-10-12 14:20',
'type': 'industrial',
'status': 'active',
'image': None
},
{
'id': '4',
'title': '游戏开发',
'date': '2024-10-11 11:45',
'type': 'game',
'status': 'active',
'image': None
}
]
# 创建网格视图卡片
row, col = 0, 0
for project_data in test_projects:
project = Project(
id=project_data['id'],
title=project_data['title'],
date=project_data['date'],
project_type=project_data['type'],
status=project_data['status'],
path=f"/test/path/{project_data['id']}",
project_dir=f"/test/path/{project_data['id']}",
image=project_data['image']
)
# 创建项目卡片
card = ProjectCard(project, project_manager, view_mode="grid")
grid_layout.addWidget(card, row, col)
col += 1
if col >= 3: # 每行3个卡片
col = 0
row += 1
# 添加列表视图示例
list_title = QLabel("列表视图示例也使用5px圆角")
list_title.setStyleSheet("""
QLabel {
font-size: 18px;
font-weight: bold;
color: #ffffff;
margin: 20px 0 10px 0;
}
""")
# 创建列表视图卡片
list_widget = QWidget()
list_layout = QVBoxLayout(list_widget)
list_layout.setSpacing(8)
list_layout.setContentsMargins(0, 0, 0, 0)
for project_data in test_projects[:2]: # 只显示前两个
project = Project(
id=project_data['id'],
title=project_data['title'],
date=project_data['date'],
project_type=project_data['type'],
status=project_data['status'],
path=f"/test/path/{project_data['id']}",
project_dir=f"/test/path/{project_data['id']}",
image=project_data['image']
)
list_card = ProjectCard(project, project_manager, view_mode="list")
list_layout.addWidget(list_card)
# 添加到主网格
grid_layout.addWidget(list_title, row + 1, 0, 1, 3)
grid_layout.addWidget(list_widget, row + 2, 0, 1, 3)
scroll_area.setWidget(grid_widget)
layout.addWidget(scroll_area)
def main():
"""主函数"""
app = QApplication(sys.argv)
# 设置应用程序属性
app.setApplicationName("圆角测试")
app.setOrganizationName("MetaCore")
# 创建并显示测试窗口
window = CornerRadiusTestWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

43
tests/test_font.py Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""快速检测Inter字体"""
def quick_font_check():
try:
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QFontDatabase, QFont, QFontInfo
import sys
app = QApplication(sys.argv)
font_db = QFontDatabase()
families = font_db.families()
# 检查Inter
inter_families = [f for f in families if 'Inter' in f]
if inter_families:
print("✅ Inter字体已安装:")
for family in inter_families:
print(f" {family}")
# 测试实际使用
test_font = QFont("Inter", 14)
font_info = QFontInfo(test_font)
print(f"\n实际使用字体: {font_info.family()}")
if "Inter" in font_info.family():
print("✅ Inter字体可正常使用")
else:
print("❌ Inter字体回退到其他字体")
else:
print("❌ Inter字体未安装")
print("请从 https://fonts.google.com/specimen/Inter 下载安装")
app.quit()
except ImportError:
print("❌ PyQt5未安装无法检测字体")
if __name__ == "__main__":
quick_font_check()

View File

@ -0,0 +1,200 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试优化后的项目卡片组件
"""
import sys
import os
from pathlib import Path
# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
sys.path.insert(0, str(project_root / 'MetaCore'))
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from data.project_manager import ProjectManager, Project
from ui.project_card import ProjectCard
from ui.styles import StyleSheet
class TestWindow(QWidget):
"""测试窗口"""
def __init__(self):
super().__init__()
self.setWindowTitle("优化后的项目卡片测试")
self.setGeometry(100, 100, 1200, 800)
self.setStyleSheet(StyleSheet.get_main_stylesheet())
# 设置深色背景
self.setStyleSheet("""
QWidget {
background-color: #1a1a1a;
color: #ffffff;
}
""" + StyleSheet.get_main_stylesheet())
self.init_ui()
def init_ui(self):
"""初始化UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(40, 40, 40, 40)
layout.setSpacing(20)
# 标题
title = QLabel("项目卡片优化效果展示")
title.setStyleSheet("""
QLabel {
font-size: 24px;
font-weight: bold;
color: #ffffff;
margin-bottom: 20px;
}
""")
title.setAlignment(Qt.AlignCenter)
layout.addWidget(title)
# 说明文字
description = QLabel("""
根据Figma设计优化的项目卡片特性
图片作为背景填充整个卡片 (276x191px)
底部半透明信息覆盖层显示项目详情
右上角操作按钮悬浮显示
无图片时使用project_empty_icon作为默认背景
完全匹配Figma设计规范的颜色和字体
""")
description.setStyleSheet("""
QLabel {
font-size: 14px;
color: #cccccc;
line-height: 1.5;
margin-bottom: 20px;
}
""")
layout.addWidget(description)
# 创建项目卡片网格
self.create_project_grid(layout)
def create_project_grid(self, layout):
"""创建项目卡片网格"""
# 滚动区域
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
scroll_area.setStyleSheet("""
QScrollArea {
border: none;
background: transparent;
}
""")
# 网格容器
grid_widget = QWidget()
grid_layout = QGridLayout(grid_widget)
grid_layout.setSpacing(24)
grid_layout.setContentsMargins(20, 20, 20, 20)
# 创建项目管理器
project_manager = ProjectManager()
# 创建测试项目数据
test_projects = [
{
'id': '1',
'title': '智能家居控制系统',
'date': '2024-10-14 15:30',
'type': 'smart',
'status': 'active',
'image': None # 测试默认图标
},
{
'id': '2',
'title': 'VR虚拟现实项目',
'date': '2024-10-13 09:15',
'type': 'vr',
'status': 'active',
'image': str(project_root / 'MetaCore' / 'Resources' / 'ProjectPreviews' / 'preview_6_1760434704537.png')
},
{
'id': '3',
'title': '工业自动化系统',
'date': '2024-10-12 14:20',
'type': 'industrial',
'status': 'active',
'image': None
},
{
'id': '4',
'title': '游戏开发项目',
'date': '2024-10-11 11:45',
'type': 'game',
'status': 'pending_delete', # 测试待删除状态
'image': None
},
{
'id': '5',
'title': 'UI设计项目',
'date': '2024-10-10 16:30',
'type': 'design',
'status': 'active',
'image': None
},
{
'id': '6',
'title': '数据分析平台',
'date': '2024-10-09 13:15',
'type': 'smart',
'status': 'active',
'image': None
}
]
# 创建项目卡片
row, col = 0, 0
for project_data in test_projects:
project = Project(
id=project_data['id'],
title=project_data['title'],
date=project_data['date'],
project_type=project_data['type'],
status=project_data['status'],
path=f"/test/path/{project_data['id']}",
project_dir=f"/test/path/{project_data['id']}",
image=project_data['image']
)
# 创建项目卡片
card = ProjectCard(project, project_manager, view_mode="grid")
grid_layout.addWidget(card, row, col)
col += 1
if col >= 4: # 每行4个卡片
col = 0
row += 1
scroll_area.setWidget(grid_widget)
layout.addWidget(scroll_area)
def main():
"""主函数"""
app = QApplication(sys.argv)
# 设置应用程序属性
app.setApplicationName("项目卡片测试")
app.setOrganizationName("MetaCore")
# 创建并显示测试窗口
window = TestWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

View File

@ -0,0 +1,94 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简单的圆角测试
"""
import sys
import os
from pathlib import Path
# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
sys.path.insert(0, str(project_root / 'MetaCore'))
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from data.project_manager import ProjectManager, Project
from ui.project_card import ProjectCard
class SimpleCornerTest(QWidget):
"""简单圆角测试"""
def __init__(self):
super().__init__()
self.setWindowTitle("简单圆角测试")
self.setGeometry(100, 100, 600, 400)
# 设置深色背景
self.setStyleSheet("""
QWidget {
background-color: #2a2a2a;
}
""")
self.init_ui()
def init_ui(self):
"""初始化UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(50, 50, 50, 50)
layout.setSpacing(30)
# 标题
title = QLabel("项目卡片5px圆角测试")
title.setStyleSheet("""
QLabel {
color: white;
font-size: 20px;
font-weight: bold;
}
""")
title.setAlignment(Qt.AlignCenter)
layout.addWidget(title)
# 创建项目管理器和测试项目
project_manager = ProjectManager()
test_project = Project(
id='test_1',
title='测试项目',
date='2024-10-14',
project_type='smart',
status='active',
path="/test/path",
project_dir="/test/path",
image=None
)
# 创建项目卡片
card = ProjectCard(test_project, project_manager, view_mode="grid")
# 居中显示卡片
card_container = QWidget()
card_layout = QHBoxLayout(card_container)
card_layout.addStretch()
card_layout.addWidget(card)
card_layout.addStretch()
layout.addWidget(card_container)
layout.addStretch()
def main():
"""主函数"""
app = QApplication(sys.argv)
window = SimpleCornerTest()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()