pyscreeps-arena 0.5.9.0__py3-none-any.whl → 0.5.9a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyscreeps_arena/compiler.py +17 -110
- pyscreeps_arena/core/const.py +1 -1
- pyscreeps_arena/project.7z +0 -0
- pyscreeps_arena/ui/project_ui.py +75 -695
- {pyscreeps_arena-0.5.9.0.dist-info → pyscreeps_arena-0.5.9a0.dist-info}/METADATA +1 -1
- {pyscreeps_arena-0.5.9.0.dist-info → pyscreeps_arena-0.5.9a0.dist-info}/RECORD +9 -9
- {pyscreeps_arena-0.5.9.0.dist-info → pyscreeps_arena-0.5.9a0.dist-info}/WHEEL +0 -0
- {pyscreeps_arena-0.5.9.0.dist-info → pyscreeps_arena-0.5.9a0.dist-info}/entry_points.txt +0 -0
- {pyscreeps_arena-0.5.9.0.dist-info → pyscreeps_arena-0.5.9a0.dist-info}/top_level.txt +0 -0
pyscreeps_arena/compiler.py
CHANGED
|
@@ -8,7 +8,6 @@ import shutil
|
|
|
8
8
|
import chardet
|
|
9
9
|
import subprocess
|
|
10
10
|
import pyperclip
|
|
11
|
-
import datetime
|
|
12
11
|
from colorama import Fore
|
|
13
12
|
from typing import List, Optional, Tuple, Union
|
|
14
13
|
|
|
@@ -53,7 +52,6 @@ def replace_src_prefix(file_list):
|
|
|
53
52
|
# """
|
|
54
53
|
# return PYFILE_PRAGMA_INSERTS + "\n" + content
|
|
55
54
|
class Compiler_Const:
|
|
56
|
-
CALLED_FUNCTIONS = ['behavior', 'sequence', 'selector', 'parallel', 'listen']
|
|
57
55
|
PROTO_DEFINES_DIRS = ["builtin", "library"]
|
|
58
56
|
FILE_STRONG_REPLACE = {
|
|
59
57
|
"std": {
|
|
@@ -110,14 +108,14 @@ export var loop = function () {
|
|
|
110
108
|
if (know.now === 1) {
|
|
111
109
|
std.show_welcome();
|
|
112
110
|
init (know);
|
|
113
|
-
|
|
111
|
+
|
|
114
112
|
}
|
|
115
113
|
step (know);
|
|
116
114
|
timeLine = get.cpu_us();
|
|
117
115
|
if (get._SCH_FLAG) sch.handle();
|
|
118
116
|
stepCost = get.cpu_us() - timeLine;
|
|
119
117
|
std.show_usage ();
|
|
120
|
-
|
|
118
|
+
print("knowCost:", knowCost, "monitorCost:", monitorCost, "stepCost:", stepCost);
|
|
121
119
|
if (know.draw) know.draw();
|
|
122
120
|
};
|
|
123
121
|
"""
|
|
@@ -650,82 +648,6 @@ class Compiler_Utils(Compiler_Const):
|
|
|
650
648
|
|
|
651
649
|
return result
|
|
652
650
|
|
|
653
|
-
@staticmethod
|
|
654
|
-
def stage_called_replace(caller_name: str, content: str) -> str:
|
|
655
|
-
"""
|
|
656
|
-
移除 '@<caller_name>(...)' 装饰器行,并在文末添加对应的 _<caller_name>Login 调用。
|
|
657
|
-
|
|
658
|
-
对于类方法: _<caller_name>Login("ClassName", "method_name", a, b, ...)
|
|
659
|
-
对于普通函数: _<caller_name>Login("", "function_name", a, b, ...)
|
|
660
|
-
"""
|
|
661
|
-
calls_to_add = []
|
|
662
|
-
deletions = []
|
|
663
|
-
|
|
664
|
-
# 1. 收集所有类定义的位置和缩进
|
|
665
|
-
class_pattern = re.compile(r'^(\s*)class\s+(\w+)', re.MULTILINE)
|
|
666
|
-
classes = [(m.start(), len(m.group(1)), m.group(2))
|
|
667
|
-
for m in class_pattern.finditer(content)]
|
|
668
|
-
|
|
669
|
-
# 2. 查找所有 @<caller_name>(...) 装饰器(支持多行参数)
|
|
670
|
-
decorator_pattern = re.compile(
|
|
671
|
-
r'^\s*@\s*' + re.escape(caller_name) + r'\s*\((.*?)\)\s*$\n?',
|
|
672
|
-
re.MULTILINE | re.DOTALL
|
|
673
|
-
)
|
|
674
|
-
|
|
675
|
-
for dec_match in decorator_pattern.finditer(content):
|
|
676
|
-
dec_start = dec_match.start()
|
|
677
|
-
dec_end = dec_match.end()
|
|
678
|
-
# 提取装饰器的参数
|
|
679
|
-
params_str = dec_match.group(1).strip()
|
|
680
|
-
|
|
681
|
-
# 查找接下来的函数定义(跳过可能的空行)
|
|
682
|
-
after_decorator = content[dec_end:]
|
|
683
|
-
func_match = re.search(r'^(\s*)def\s+([^\s\(]+)', after_decorator, re.MULTILINE)
|
|
684
|
-
|
|
685
|
-
if not func_match:
|
|
686
|
-
continue
|
|
687
|
-
|
|
688
|
-
func_indent_len = len(func_match.group(1))
|
|
689
|
-
func_name = func_match.group(2)
|
|
690
|
-
|
|
691
|
-
# 3. 确定类名:查找装饰器前最近的、缩进小于函数缩进的类
|
|
692
|
-
class_name = ""
|
|
693
|
-
for cls_pos, cls_indent_len, cls_name in reversed(classes):
|
|
694
|
-
if cls_pos < dec_match.start() and func_indent_len > cls_indent_len:
|
|
695
|
-
class_name = cls_name
|
|
696
|
-
break
|
|
697
|
-
|
|
698
|
-
# 4. 处理参数,保持参数的格式
|
|
699
|
-
# 移除参数中的换行和多余空格,保持参数列表的格式
|
|
700
|
-
params = []
|
|
701
|
-
if params_str:
|
|
702
|
-
# 简单处理参数,保持引号内的内容不变
|
|
703
|
-
# 这里可以根据需要进行更复杂的参数解析
|
|
704
|
-
params = [p.strip() for p in params_str.split(',') if p.strip()]
|
|
705
|
-
|
|
706
|
-
# 构建参数部分的字符串
|
|
707
|
-
params_part = ""
|
|
708
|
-
if params:
|
|
709
|
-
params_part = ", " + ", ".join(params)
|
|
710
|
-
|
|
711
|
-
# 5. 记录删除位置和调用信息
|
|
712
|
-
deletions.append((dec_start, dec_end))
|
|
713
|
-
calls_to_add.append(f'_{caller_name}Login("{class_name}", "{func_name}"{params_part})')
|
|
714
|
-
|
|
715
|
-
# 6. 应用删除(倒序避免位置偏移)
|
|
716
|
-
if not deletions:
|
|
717
|
-
return content
|
|
718
|
-
|
|
719
|
-
result = content
|
|
720
|
-
for start, end in sorted(deletions, key=lambda x: x[0], reverse=True):
|
|
721
|
-
result = result[:start] + result[end:]
|
|
722
|
-
|
|
723
|
-
# 7. 在文末添加调用
|
|
724
|
-
if calls_to_add:
|
|
725
|
-
result = '\n'.join(calls_to_add) + '\n' + result
|
|
726
|
-
|
|
727
|
-
return result
|
|
728
|
-
|
|
729
651
|
@staticmethod
|
|
730
652
|
def process_mate_code(code):
|
|
731
653
|
# 用于存储匹配到的信息
|
|
@@ -999,8 +921,6 @@ class Compiler(CompilerBase):
|
|
|
999
921
|
|
|
1000
922
|
# 将PYFILE_PRAGMA_INSERTS.replace("\t", "").replace(" ", "")插入到文件开头
|
|
1001
923
|
content = self.auto_read(fpath)
|
|
1002
|
-
# 移除"""$..."""代码块
|
|
1003
|
-
content = re.sub(r'"""\$[\s\S]*?"""', '', content)
|
|
1004
924
|
content = self.PYFILE_PRAGMA_INSERTS.replace("\t", "").replace(" ", "") + content
|
|
1005
925
|
# content = self.remove_long_docstring(content) # 移除长注释 | remove long docstring
|
|
1006
926
|
|
|
@@ -1157,13 +1077,10 @@ class Compiler(CompilerBase):
|
|
|
1157
1077
|
with open(fpath, 'w', encoding='utf-8') as f:
|
|
1158
1078
|
f.write(new_content)
|
|
1159
1079
|
|
|
1160
|
-
# ------------------------------------ 自定义:调用stage_recursive_replace
|
|
1080
|
+
# ------------------------------------ 自定义:调用stage_recursive_replace ------------------------------------ #
|
|
1161
1081
|
for fpath in py_fpath:
|
|
1162
1082
|
content = self.auto_read(fpath)
|
|
1163
1083
|
content = self.stage_recursive_replace(content) # 调用stage_recursive_replace
|
|
1164
|
-
# 调用stage_called_replace处理四个装饰器
|
|
1165
|
-
for caller in self.CALLED_FUNCTIONS:
|
|
1166
|
-
content = self.stage_called_replace(caller, content)
|
|
1167
1084
|
with open(fpath, 'w', encoding='utf-8') as f:
|
|
1168
1085
|
f.write(content)
|
|
1169
1086
|
|
|
@@ -1300,22 +1217,12 @@ class Compiler(CompilerBase):
|
|
|
1300
1217
|
:param min_js_files: list[str] # .min.js文件路径列表
|
|
1301
1218
|
:return: str
|
|
1302
1219
|
"""
|
|
1303
|
-
arena_name = const.ARENA_NAMES.get(config.arena, const.ARENA_NAMES[
|
|
1220
|
+
arena_name = const.ARENA_NAMES.get(config.arena, const.ARENA_NAMES['green']) # like green -> spawn_and_swamp
|
|
1304
1221
|
self.TOTAL_INSERT_AT_HEAD += self.ARENA_IMPORTS_GETTER[arena_name]() # add arena imports
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
timestring = current_time.strftime("%Y-%m-%d %H:%M")
|
|
1308
|
-
|
|
1309
|
-
total_js = f"""const __VERSION__ = '{const.VERSION}';
|
|
1310
|
-
const __PYTHON_VERSION__ = '{python_version_info}';""" + self.TOTAL_INSERT_AT_HEAD + f"""
|
|
1311
|
-
export var LANGUAGE = '{config.language}';
|
|
1312
|
-
"""
|
|
1313
|
-
|
|
1314
|
-
total_js += f"export var TIMESTAMP = {timestamp_ms};\n"
|
|
1315
|
-
total_js += f"export var TIMESTRING = '{timestring}';\n"
|
|
1316
|
-
total_js += f"""const __AUTHOR__ = '{const.AUTHOR}';
|
|
1317
|
-
const __AUTHOR_CN__ = '{const.BILIBILI_NAME}';"""
|
|
1222
|
+
total_js = f"const __VERSION__ = '{const.VERSION}';\nconst __PYTHON_VERSION__ = '{python_version_info}';" + self.TOTAL_INSERT_AT_HEAD + f"\nexport var LANGUAGE = '{config.language}';\n"
|
|
1223
|
+
total_js += f"const __AUTHOR__ = '{const.AUTHOR}';\nconst __AUTHOR_CN__ = '{const.BILIBILI_NAME}';"
|
|
1318
1224
|
|
|
1225
|
+
# 添加.min.js文件的import语句
|
|
1319
1226
|
if min_js_files:
|
|
1320
1227
|
for min_js_path in min_js_files:
|
|
1321
1228
|
min_js_filename = os.path.basename(min_js_path)
|
|
@@ -1435,6 +1342,14 @@ const __AUTHOR_CN__ = '{const.BILIBILI_NAME}';"""
|
|
|
1435
1342
|
dir_path = os.path.dirname(mjs_path)
|
|
1436
1343
|
build_dir_path = os.path.dirname(build_main_mjs)
|
|
1437
1344
|
|
|
1345
|
+
# 复制.min.js文件到目标目录
|
|
1346
|
+
for min_js_path in min_js_files:
|
|
1347
|
+
min_js_filename = os.path.basename(min_js_path)
|
|
1348
|
+
# 复制到build目录
|
|
1349
|
+
shutil.copy(min_js_path, os.path.join(build_dir_path, min_js_filename))
|
|
1350
|
+
# 复制到最终导出目录
|
|
1351
|
+
shutil.copy(min_js_path, os.path.join(dir_path, min_js_filename))
|
|
1352
|
+
|
|
1438
1353
|
# 生成total_js,传入.min.js文件列表
|
|
1439
1354
|
total_js = imports + "\n" + self.generate_total_js(
|
|
1440
1355
|
replace_src_prefix(modules), imps, sorts, self.FILE_STRONG_REPLACE, reps, min_js_files
|
|
@@ -1450,14 +1365,6 @@ const __AUTHOR_CN__ = '{const.BILIBILI_NAME}';"""
|
|
|
1450
1365
|
with open(mjs_path, 'w', encoding='utf-8') as f:
|
|
1451
1366
|
f.write(total_js)
|
|
1452
1367
|
|
|
1453
|
-
# 复制.min.js文件到目标目录
|
|
1454
|
-
for min_js_path in min_js_files:
|
|
1455
|
-
min_js_filename = os.path.basename(min_js_path)
|
|
1456
|
-
# 复制到build目录
|
|
1457
|
-
shutil.copy(min_js_path, os.path.join(build_dir_path, min_js_filename))
|
|
1458
|
-
# 复制到最终导出目录
|
|
1459
|
-
shutil.copy(min_js_path, os.path.join(dir_path, min_js_filename))
|
|
1460
|
-
|
|
1461
1368
|
core.lprint(GREEN.format('[6/6]'), LOC_DONE, " ", LOC_EXPORTING_TOTAL_MAIN_JS_FINISH, sep="", head="\r", ln=config.language)
|
|
1462
1369
|
|
|
1463
1370
|
if mjs_path != build_main_mjs:
|
|
@@ -1515,7 +1422,7 @@ if __name__ == '__main__':
|
|
|
1515
1422
|
# compiler.compile()
|
|
1516
1423
|
# compiler.clean()
|
|
1517
1424
|
test = """
|
|
1518
|
-
|
|
1425
|
+
|
|
1519
1426
|
def patrolling(self, c: Creep):
|
|
1520
1427
|
e = self.center.nearest(k.civilian.enemies, 5)
|
|
1521
1428
|
if e:
|
|
@@ -1525,6 +1432,6 @@ def patrolling(self, c: Creep):
|
|
|
1525
1432
|
):
|
|
1526
1433
|
case True: c.move(e, SWAMP_MOTION)
|
|
1527
1434
|
case False: c.move(self.bpos, SWAMP_MOTION)
|
|
1528
|
-
|
|
1435
|
+
|
|
1529
1436
|
"""
|
|
1530
1437
|
print(f"res=\n{Compiler.convert_match_to_if(test)}")
|
pyscreeps_arena/core/const.py
CHANGED
pyscreeps_arena/project.7z
CHANGED
|
Binary file
|
pyscreeps_arena/ui/project_ui.py
CHANGED
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
"""
|
|
5
5
|
import sys
|
|
6
6
|
import os
|
|
7
|
-
import json
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
|
10
9
|
QHBoxLayout, QLabel, QLineEdit, QPushButton,
|
|
11
|
-
QFileDialog, QMessageBox, QComboBox
|
|
12
|
-
from
|
|
13
|
-
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty
|
|
10
|
+
QFileDialog, QMessageBox, QComboBox)
|
|
11
|
+
from PyQt6.QtCore import Qt
|
|
14
12
|
from PyQt6.QtGui import QFont
|
|
15
13
|
from PyQt6.QtGui import QIcon
|
|
16
14
|
from pyscreeps_arena.core import const
|
|
@@ -18,307 +16,36 @@ from pyscreeps_arena.ui.rs_icon import get_pixmap
|
|
|
18
16
|
from pyscreeps_arena.afters import ToConfigAfter, ToEmptyAfter, ToPrefabAfter, ToCustomAfter
|
|
19
17
|
from PyQt6.QtWidgets import QRadioButton, QGroupBox
|
|
20
18
|
|
|
21
|
-
# Language mapping
|
|
22
|
-
LANG = {
|
|
23
|
-
'cn': {
|
|
24
|
-
'window_title': 'PyScreeps Arena - 项目创建器',
|
|
25
|
-
'title': 'PyScreeps Arena',
|
|
26
|
-
'version': '版本: {0}',
|
|
27
|
-
'author': '作者: {0}',
|
|
28
|
-
'github': 'GitHub: {0}',
|
|
29
|
-
'project_name': '项目名称:',
|
|
30
|
-
'name_placeholder': '输入项目名称...',
|
|
31
|
-
'save_location': '保存位置:',
|
|
32
|
-
'path_placeholder': '选择项目保存位置...',
|
|
33
|
-
'browse': '浏览...',
|
|
34
|
-
'language': '语 言:',
|
|
35
|
-
'arena': '竞技场:',
|
|
36
|
-
'difficulty': '难 度:',
|
|
37
|
-
'empty': '空白 (Empty)',
|
|
38
|
-
'basic': '基础 (Basic)',
|
|
39
|
-
'prefab': '预设 (Prefab)',
|
|
40
|
-
'pi': '预设继承 (P&&I)',
|
|
41
|
-
'create_project': '创建项目',
|
|
42
|
-
'cancel': '取消',
|
|
43
|
-
'path_exists': '路径已存在',
|
|
44
|
-
'path_exists_message': "路径 '{0}' 已存在。\n是否继续?",
|
|
45
|
-
'success': '成功',
|
|
46
|
-
'success_message': "项目 '{0}' 创建成功!\n路径: {1}",
|
|
47
|
-
'error': '错误',
|
|
48
|
-
'error_message': '项目创建失败:\n{0}',
|
|
49
|
-
'select_location': '选择项目保存位置',
|
|
50
|
-
'next_page': '下一页',
|
|
51
|
-
'previous_page': '上一页',
|
|
52
|
-
},
|
|
53
|
-
'en': {
|
|
54
|
-
'window_title': 'PyScreeps Arena - Project Creator',
|
|
55
|
-
'title': 'PyScreeps Arena',
|
|
56
|
-
'version': 'Version: {0}',
|
|
57
|
-
'author': 'Author: {0}',
|
|
58
|
-
'github': 'GitHub: {0}',
|
|
59
|
-
'project_name': 'Project Name:',
|
|
60
|
-
'name_placeholder': 'Enter project name...',
|
|
61
|
-
'save_location': 'Save Location:',
|
|
62
|
-
'path_placeholder': 'Select project save location...',
|
|
63
|
-
'browse': 'Browse...',
|
|
64
|
-
'language': 'Language:',
|
|
65
|
-
'arena': 'Arena:',
|
|
66
|
-
'difficulty': 'Difficulty:',
|
|
67
|
-
'empty': 'Empty',
|
|
68
|
-
'basic': 'Basic',
|
|
69
|
-
'prefab': 'Prefab',
|
|
70
|
-
'pi': 'Prefab & Inherit',
|
|
71
|
-
'create_project': 'Create Project',
|
|
72
|
-
'cancel': 'Cancel',
|
|
73
|
-
'path_exists': 'Path Exists',
|
|
74
|
-
'path_exists_message': "Path '{0}' already exists.\nContinue anyway?",
|
|
75
|
-
'success': 'Success',
|
|
76
|
-
'success_message': "Project '{0}' created successfully!\nPath: {1}",
|
|
77
|
-
'error': 'Error',
|
|
78
|
-
'error_message': 'Failed to create project:\n{0}',
|
|
79
|
-
'select_location': 'Select Project Save Location',
|
|
80
|
-
'next_page': 'Next Page',
|
|
81
|
-
'previous_page': 'Previous',
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
19
|
|
|
85
20
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
# Signals
|
|
91
|
-
projectCreated = pyqtSignal(str, str) # (project_name, project_path)
|
|
92
|
-
projectCancelled = pyqtSignal()
|
|
93
|
-
languageChanged = pyqtSignal(str) # (language_code)
|
|
94
|
-
|
|
95
|
-
def __init__(self, parent=None):
|
|
96
|
-
super().__init__(parent)
|
|
21
|
+
class ProjectCreatorUI(QMainWindow):
|
|
22
|
+
def __init__(self):
|
|
23
|
+
super().__init__()
|
|
97
24
|
self._proj_name = ""
|
|
98
25
|
self._proj_path = ""
|
|
99
26
|
self._init_ui()
|
|
100
|
-
|
|
101
|
-
def lang(self, key, *args):
|
|
102
|
-
"""Get translation for the current language"""
|
|
103
|
-
# Get language code from combo box data
|
|
104
|
-
lang = self._lang_combo.currentData() or 'cn'
|
|
105
|
-
|
|
106
|
-
if lang not in LANG:
|
|
107
|
-
lang = 'cn'
|
|
108
|
-
return LANG[lang][key].format(*args)
|
|
109
|
-
|
|
110
|
-
def _get_settings_path(self):
|
|
111
|
-
"""Get cross-platform settings file path"""
|
|
112
|
-
# Get user's home directory
|
|
113
|
-
home_dir = os.path.expanduser("~")
|
|
114
|
-
|
|
115
|
-
# Create settings directory if it doesn't exist
|
|
116
|
-
settings_dir = os.path.join(home_dir, ".psaui")
|
|
117
|
-
os.makedirs(settings_dir, exist_ok=True)
|
|
118
|
-
|
|
119
|
-
# Return settings file path
|
|
120
|
-
return os.path.join(settings_dir, "psaui.json")
|
|
121
|
-
|
|
122
|
-
def _save_settings(self):
|
|
123
|
-
"""Save settings to psaui.json"""
|
|
124
|
-
settings = {
|
|
125
|
-
"language": self._lang_combo.currentIndex(),
|
|
126
|
-
"path": self._path_input.text()
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
try:
|
|
130
|
-
settings_path = self._get_settings_path()
|
|
131
|
-
with open(settings_path, 'w', encoding='utf-8') as f:
|
|
132
|
-
json.dump(settings, f, indent=2, ensure_ascii=False)
|
|
133
|
-
except Exception as e:
|
|
134
|
-
print(f"Error saving settings: {e}")
|
|
135
|
-
|
|
136
|
-
def _load_settings(self):
|
|
137
|
-
"""Load settings from psaui.json if it exists"""
|
|
138
|
-
try:
|
|
139
|
-
settings_path = self._get_settings_path()
|
|
140
|
-
if os.path.exists(settings_path):
|
|
141
|
-
with open(settings_path, 'r', encoding='utf-8') as f:
|
|
142
|
-
settings = json.load(f)
|
|
143
|
-
|
|
144
|
-
# Load language setting
|
|
145
|
-
if "language" in settings:
|
|
146
|
-
language_index = settings["language"]
|
|
147
|
-
if 0 <= language_index < self._lang_combo.count():
|
|
148
|
-
self._lang_combo.setCurrentIndex(language_index)
|
|
149
|
-
|
|
150
|
-
# Load path setting
|
|
151
|
-
if "path" in settings and settings["path"]:
|
|
152
|
-
self._path_input.setText(settings["path"])
|
|
153
|
-
self._proj_path = settings["path"]
|
|
154
|
-
except Exception as e:
|
|
155
|
-
print(f"Error loading settings: {e}")
|
|
156
|
-
|
|
157
|
-
def _update_language_options(self):
|
|
158
|
-
"""Update language combo box options based on current language"""
|
|
159
|
-
# Get current language
|
|
160
|
-
lang_text = self._lang_combo.currentText()
|
|
161
|
-
current_lang = lang_text.split('(')[1].strip(')') if '(' in lang_text else 'cn'
|
|
162
|
-
|
|
163
|
-
# Save current index
|
|
164
|
-
current_index = self._lang_combo.currentIndex()
|
|
165
|
-
|
|
166
|
-
# Disconnect signal to avoid recursion
|
|
167
|
-
self._lang_combo.currentTextChanged.disconnect(self._update_language)
|
|
168
|
-
|
|
169
|
-
# Clear existing items
|
|
170
|
-
self._lang_combo.clear()
|
|
171
|
-
|
|
172
|
-
# Add items based on current language
|
|
173
|
-
if current_lang == 'en':
|
|
174
|
-
# In English mode, show only language codes
|
|
175
|
-
self._lang_combo.addItems(["cn", "en"])
|
|
176
|
-
else:
|
|
177
|
-
# In Chinese mode, show full language names
|
|
178
|
-
self._lang_combo.addItems(["中文 (cn)", "英文 (en)"])
|
|
179
|
-
|
|
180
|
-
# Restore current index
|
|
181
|
-
if current_index < self._lang_combo.count():
|
|
182
|
-
self._lang_combo.setCurrentIndex(current_index)
|
|
183
|
-
|
|
184
|
-
# Reconnect signal
|
|
185
|
-
self._lang_combo.currentTextChanged.connect(self._update_language)
|
|
186
|
-
|
|
187
|
-
def _update_language(self):
|
|
188
|
-
"""Update all UI elements to use the current language"""
|
|
189
|
-
# Get current language code
|
|
190
|
-
current_lang = self._lang_combo.currentData() or 'cn'
|
|
191
|
-
|
|
192
|
-
# Update window title
|
|
193
|
-
self.setWindowTitle(self.lang('window_title'))
|
|
194
|
-
|
|
195
|
-
# Update title
|
|
196
|
-
self._title_label.setText(self.lang('title'))
|
|
197
|
-
|
|
198
|
-
# Update info labels
|
|
199
|
-
self._version_label.setText(self.lang('version', const.VERSION))
|
|
200
|
-
self._author_label.setText(self.lang('author', const.AUTHOR))
|
|
201
|
-
self._github_label.setText(self.lang('github', const.GITHUB_NAME))
|
|
202
|
-
|
|
203
|
-
# Update input labels and placeholders
|
|
204
|
-
current_lang = self._lang_combo.currentData() or 'cn'
|
|
205
|
-
if current_lang == 'en':
|
|
206
|
-
self._name_label.setText('Name:')
|
|
207
|
-
else:
|
|
208
|
-
self._name_label.setText(self.lang('project_name'))
|
|
209
|
-
self._name_input.setPlaceholderText(self.lang('name_placeholder'))
|
|
210
|
-
self._path_label.setText(self.lang('save_location'))
|
|
211
|
-
self._path_input.setPlaceholderText(self.lang('path_placeholder'))
|
|
212
|
-
|
|
213
|
-
# Update buttons
|
|
214
|
-
self._browse_btn.setText(self.lang('browse'))
|
|
215
|
-
if hasattr(self, '_create_btn'):
|
|
216
|
-
self._create_btn.setText(self.lang('create_project'))
|
|
217
|
-
if hasattr(self, '_cancel_btn'):
|
|
218
|
-
self._cancel_btn.setText(self.lang('cancel'))
|
|
219
|
-
|
|
220
|
-
# Update config labels
|
|
221
|
-
self._lang_label.setText(self.lang('language'))
|
|
222
|
-
self._arena_label.setText(self.lang('arena'))
|
|
223
|
-
self._level_label.setText(self.lang('difficulty'))
|
|
224
|
-
|
|
225
|
-
# Update radio buttons
|
|
226
|
-
self._empty_radio.setText(self.lang('empty'))
|
|
227
|
-
self._basic_radio.setText(self.lang('basic'))
|
|
228
|
-
self._prefab_radio.setText(self.lang('prefab'))
|
|
229
|
-
self._pi_radio.setText(self.lang('pi'))
|
|
230
|
-
|
|
231
|
-
# Update language combo box items based on current language
|
|
232
|
-
if current_lang == 'en':
|
|
233
|
-
# In English mode, show only language codes
|
|
234
|
-
self._lang_combo.blockSignals(True)
|
|
235
|
-
current_index = self._lang_combo.currentIndex()
|
|
236
|
-
self._lang_combo.clear()
|
|
237
|
-
self._lang_combo.addItem("cn", "cn")
|
|
238
|
-
self._lang_combo.addItem("en", "en")
|
|
239
|
-
self._lang_combo.setCurrentIndex(current_index)
|
|
240
|
-
self._lang_combo.blockSignals(False)
|
|
241
|
-
|
|
242
|
-
# Update arena combo box items
|
|
243
|
-
self._arena_combo.blockSignals(True)
|
|
244
|
-
current_arena_index = self._arena_combo.currentIndex()
|
|
245
|
-
self._arena_combo.clear()
|
|
246
|
-
self._arena_combo.addItem("gray", "gray")
|
|
247
|
-
self._arena_combo.addItem("green", "green")
|
|
248
|
-
self._arena_combo.addItem("blue", "blue")
|
|
249
|
-
self._arena_combo.addItem("red", "red")
|
|
250
|
-
self._arena_combo.setCurrentIndex(current_arena_index)
|
|
251
|
-
self._arena_combo.blockSignals(False)
|
|
252
|
-
|
|
253
|
-
# Update level combo box items
|
|
254
|
-
self._level_combo.blockSignals(True)
|
|
255
|
-
current_level_index = self._level_combo.currentIndex()
|
|
256
|
-
self._level_combo.clear()
|
|
257
|
-
self._level_combo.addItem("basic", "basic")
|
|
258
|
-
self._level_combo.addItem("advanced", "advanced")
|
|
259
|
-
self._level_combo.setCurrentIndex(current_level_index)
|
|
260
|
-
self._level_combo.blockSignals(False)
|
|
261
|
-
else:
|
|
262
|
-
# In Chinese mode, show full language names
|
|
263
|
-
self._lang_combo.blockSignals(True)
|
|
264
|
-
current_index = self._lang_combo.currentIndex()
|
|
265
|
-
self._lang_combo.clear()
|
|
266
|
-
self._lang_combo.addItem("中文 (cn)", "cn")
|
|
267
|
-
self._lang_combo.addItem("英文 (en)", "en")
|
|
268
|
-
self._lang_combo.setCurrentIndex(current_index)
|
|
269
|
-
self._lang_combo.blockSignals(False)
|
|
270
|
-
|
|
271
|
-
# Update arena combo box items
|
|
272
|
-
self._arena_combo.blockSignals(True)
|
|
273
|
-
current_arena_index = self._arena_combo.currentIndex()
|
|
274
|
-
self._arena_combo.clear()
|
|
275
|
-
self._arena_combo.addItem("灰色 (gray)", "gray")
|
|
276
|
-
self._arena_combo.addItem("绿色 (green)", "green")
|
|
277
|
-
self._arena_combo.addItem("蓝色 (blue)", "blue")
|
|
278
|
-
self._arena_combo.addItem("红色 (red)", "red")
|
|
279
|
-
self._arena_combo.setCurrentIndex(current_arena_index)
|
|
280
|
-
self._arena_combo.blockSignals(False)
|
|
281
|
-
|
|
282
|
-
# Update level combo box items
|
|
283
|
-
self._level_combo.blockSignals(True)
|
|
284
|
-
current_level_index = self._level_combo.currentIndex()
|
|
285
|
-
self._level_combo.clear()
|
|
286
|
-
self._level_combo.addItem("基础 (basic)", "basic")
|
|
287
|
-
self._level_combo.addItem("高级 (advanced)", "advanced")
|
|
288
|
-
self._level_combo.setCurrentIndex(current_level_index)
|
|
289
|
-
self._level_combo.blockSignals(False)
|
|
290
|
-
|
|
291
|
-
# Save settings after language change
|
|
292
|
-
self._save_settings()
|
|
293
27
|
|
|
294
28
|
def _init_ui(self):
|
|
295
29
|
"""初始化UI界面"""
|
|
30
|
+
self.setWindowTitle("PyScreeps Arena - 项目创建器")
|
|
31
|
+
self.setWindowIcon(QIcon(get_pixmap()))
|
|
296
32
|
self.setFixedSize(500, 580)
|
|
297
33
|
|
|
298
|
-
#
|
|
299
|
-
|
|
34
|
+
# 主窗口部件
|
|
35
|
+
central_widget = QWidget()
|
|
36
|
+
self.setCentralWidget(central_widget)
|
|
37
|
+
layout = QVBoxLayout(central_widget)
|
|
300
38
|
layout.setSpacing(15)
|
|
301
|
-
|
|
302
|
-
layout.setContentsMargins(15, 15, 15, 30)
|
|
303
|
-
|
|
304
|
-
# 创建项目创建页面
|
|
305
|
-
self._project_page = QWidget()
|
|
306
|
-
self._project_layout = QVBoxLayout(self._project_page)
|
|
307
|
-
self._project_layout.setSpacing(15)
|
|
308
|
-
self._project_layout.setContentsMargins(0, 0, 0, 0)
|
|
309
|
-
|
|
310
|
-
# 创建堆叠容器
|
|
311
|
-
self._stacked_widget = QStackedWidget()
|
|
312
|
-
layout.addWidget(self._stacked_widget)
|
|
39
|
+
layout.setContentsMargins(30, 30, 30, 30)
|
|
313
40
|
|
|
314
41
|
# 标题
|
|
315
|
-
|
|
316
|
-
|
|
42
|
+
title = QLabel("PyScreeps Arena")
|
|
43
|
+
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
317
44
|
title_font = QFont()
|
|
318
45
|
title_font.setPointSize(18)
|
|
319
46
|
title_font.setBold(True)
|
|
320
|
-
|
|
321
|
-
|
|
47
|
+
title.setFont(title_font)
|
|
48
|
+
layout.addWidget(title)
|
|
322
49
|
|
|
323
50
|
# 项目信息
|
|
324
51
|
info_widget = QWidget()
|
|
@@ -326,27 +53,27 @@ class QProjectBody(QWidget):
|
|
|
326
53
|
info_layout.setSpacing(8)
|
|
327
54
|
|
|
328
55
|
# 版本信息
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
info_layout.addWidget(
|
|
56
|
+
version_label = QLabel(f"版本: {const.VERSION}")
|
|
57
|
+
version_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
58
|
+
info_layout.addWidget(version_label)
|
|
332
59
|
|
|
333
60
|
# 作者信息
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
info_layout.addWidget(
|
|
61
|
+
author_label = QLabel(f"作者: {const.AUTHOR}")
|
|
62
|
+
author_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
63
|
+
info_layout.addWidget(author_label)
|
|
337
64
|
|
|
338
65
|
# GitHub信息
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
info_layout.addWidget(
|
|
66
|
+
github_label = QLabel(f"GitHub: {const.GITHUB_NAME}")
|
|
67
|
+
github_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
68
|
+
info_layout.addWidget(github_label)
|
|
342
69
|
|
|
343
|
-
|
|
70
|
+
layout.addWidget(info_widget)
|
|
344
71
|
|
|
345
72
|
# 分隔线
|
|
346
73
|
separator = QLabel("─" * 50)
|
|
347
74
|
separator.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
348
75
|
separator.setStyleSheet("color: #ccc;")
|
|
349
|
-
|
|
76
|
+
layout.addWidget(separator)
|
|
350
77
|
|
|
351
78
|
# 项目输入区域
|
|
352
79
|
input_widget = QWidget()
|
|
@@ -355,20 +82,20 @@ class QProjectBody(QWidget):
|
|
|
355
82
|
|
|
356
83
|
# 项目名称输入
|
|
357
84
|
name_layout = QHBoxLayout()
|
|
358
|
-
|
|
359
|
-
|
|
85
|
+
name_label = QLabel("项目名称:")
|
|
86
|
+
name_label.setFixedWidth(80)
|
|
360
87
|
self._name_input = QLineEdit()
|
|
361
88
|
self._name_input.setPlaceholderText("输入项目名称...")
|
|
362
89
|
self._name_input.textChanged.connect(self._on_name_changed)
|
|
363
90
|
self._name_input.returnPressed.connect(self._create_project)
|
|
364
|
-
name_layout.addWidget(
|
|
91
|
+
name_layout.addWidget(name_label)
|
|
365
92
|
name_layout.addWidget(self._name_input)
|
|
366
93
|
input_layout.addLayout(name_layout)
|
|
367
94
|
|
|
368
95
|
# 项目路径输入
|
|
369
96
|
path_layout = QHBoxLayout()
|
|
370
|
-
|
|
371
|
-
|
|
97
|
+
path_label = QLabel("保存位置:")
|
|
98
|
+
path_label.setFixedWidth(80)
|
|
372
99
|
self._path_input = QLineEdit()
|
|
373
100
|
self._path_input.setPlaceholderText("选择项目保存位置...")
|
|
374
101
|
self._path_input.setReadOnly(True)
|
|
@@ -377,23 +104,23 @@ class QProjectBody(QWidget):
|
|
|
377
104
|
if os.path.exists(desktop_path):
|
|
378
105
|
self._path_input.setText(desktop_path)
|
|
379
106
|
self._proj_path = desktop_path
|
|
380
|
-
path_layout.addWidget(
|
|
107
|
+
path_layout.addWidget(path_label)
|
|
381
108
|
path_layout.addWidget(self._path_input)
|
|
382
109
|
|
|
383
110
|
# 浏览按钮
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
path_layout.addWidget(
|
|
111
|
+
browse_btn = QPushButton("浏览...")
|
|
112
|
+
browse_btn.setFixedWidth(60)
|
|
113
|
+
browse_btn.clicked.connect(self._browse_path)
|
|
114
|
+
path_layout.addWidget(browse_btn)
|
|
388
115
|
|
|
389
116
|
input_layout.addLayout(path_layout)
|
|
390
|
-
|
|
117
|
+
layout.addWidget(input_widget)
|
|
391
118
|
|
|
392
119
|
# 分隔线
|
|
393
120
|
separator2 = QLabel("─" * 50)
|
|
394
121
|
separator2.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
395
122
|
separator2.setStyleSheet("color: #ccc;")
|
|
396
|
-
|
|
123
|
+
layout.addWidget(separator2)
|
|
397
124
|
|
|
398
125
|
# 配置选项区域(左右布局)
|
|
399
126
|
config_container = QWidget()
|
|
@@ -407,43 +134,34 @@ class QProjectBody(QWidget):
|
|
|
407
134
|
|
|
408
135
|
# 语言选择
|
|
409
136
|
lang_layout = QHBoxLayout()
|
|
410
|
-
|
|
411
|
-
|
|
137
|
+
lang_label = QLabel("语 言:")
|
|
138
|
+
lang_label.setFixedWidth(80)
|
|
412
139
|
self._lang_combo = QComboBox()
|
|
413
|
-
|
|
414
|
-
self._lang_combo.addItem("中文 (cn)", "cn")
|
|
415
|
-
self._lang_combo.addItem("英文 (en)", "en")
|
|
140
|
+
self._lang_combo.addItems(["中文 (cn)", "英文 (en)"])
|
|
416
141
|
self._lang_combo.setCurrentIndex(0)
|
|
417
|
-
|
|
418
|
-
lang_layout.addWidget(self._lang_label)
|
|
142
|
+
lang_layout.addWidget(lang_label)
|
|
419
143
|
lang_layout.addWidget(self._lang_combo)
|
|
420
144
|
left_config_layout.addLayout(lang_layout)
|
|
421
145
|
|
|
422
146
|
# 竞技场选择
|
|
423
147
|
arena_layout = QHBoxLayout()
|
|
424
|
-
|
|
425
|
-
|
|
148
|
+
arena_label = QLabel("竞技场:")
|
|
149
|
+
arena_label.setFixedWidth(80)
|
|
426
150
|
self._arena_combo = QComboBox()
|
|
427
|
-
|
|
428
|
-
self._arena_combo.addItem("灰色 (gray)", "gray")
|
|
429
|
-
self._arena_combo.addItem("绿色 (green)", "green")
|
|
430
|
-
self._arena_combo.addItem("蓝色 (blue)", "blue")
|
|
431
|
-
self._arena_combo.addItem("红色 (red)", "red")
|
|
151
|
+
self._arena_combo.addItems(["灰色 (gray)", "绿色 (green)", "蓝色 (blue)", "红色 (red)"])
|
|
432
152
|
self._arena_combo.setCurrentIndex(0)
|
|
433
|
-
arena_layout.addWidget(
|
|
153
|
+
arena_layout.addWidget(arena_label)
|
|
434
154
|
arena_layout.addWidget(self._arena_combo)
|
|
435
155
|
left_config_layout.addLayout(arena_layout)
|
|
436
156
|
|
|
437
157
|
# 难度级别选择
|
|
438
158
|
level_layout = QHBoxLayout()
|
|
439
|
-
|
|
440
|
-
|
|
159
|
+
level_label = QLabel("难 度:")
|
|
160
|
+
level_label.setFixedWidth(80)
|
|
441
161
|
self._level_combo = QComboBox()
|
|
442
|
-
|
|
443
|
-
self._level_combo.addItem("基础 (basic)", "basic")
|
|
444
|
-
self._level_combo.addItem("高级 (advanced)", "advanced")
|
|
162
|
+
self._level_combo.addItems(["基础 (basic)", "高级 (advanced)"])
|
|
445
163
|
self._level_combo.setCurrentIndex(0)
|
|
446
|
-
level_layout.addWidget(
|
|
164
|
+
level_layout.addWidget(level_label)
|
|
447
165
|
level_layout.addWidget(self._level_combo)
|
|
448
166
|
left_config_layout.addLayout(level_layout)
|
|
449
167
|
|
|
@@ -476,22 +194,7 @@ class QProjectBody(QWidget):
|
|
|
476
194
|
|
|
477
195
|
config_container_layout.addWidget(right_config_widget)
|
|
478
196
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
self._project_layout.addStretch()
|
|
482
|
-
|
|
483
|
-
# 加载设置
|
|
484
|
-
self._load_settings()
|
|
485
|
-
|
|
486
|
-
# 创建预制件管理器页面
|
|
487
|
-
self._prefabs_page = QPrefabsManager()
|
|
488
|
-
|
|
489
|
-
# 添加页面到堆叠容器
|
|
490
|
-
self._stacked_widget.addWidget(self._project_page)
|
|
491
|
-
self._stacked_widget.addWidget(self._prefabs_page)
|
|
492
|
-
|
|
493
|
-
# 连接堆叠容器的页面变化信号
|
|
494
|
-
self._stacked_widget.currentChanged.connect(self._on_page_changed)
|
|
197
|
+
layout.addWidget(config_container)
|
|
495
198
|
|
|
496
199
|
# 按钮区域
|
|
497
200
|
button_layout = QHBoxLayout()
|
|
@@ -499,95 +202,36 @@ class QProjectBody(QWidget):
|
|
|
499
202
|
|
|
500
203
|
# 创建按钮
|
|
501
204
|
self._create_btn = QPushButton("创建项目")
|
|
502
|
-
self._create_btn.setFixedSize(
|
|
503
|
-
self._create_btn.clicked.connect(self.
|
|
205
|
+
self._create_btn.setFixedSize(100, 35)
|
|
206
|
+
self._create_btn.clicked.connect(self._create_project)
|
|
504
207
|
self._create_btn.setEnabled(False)
|
|
505
208
|
self._create_btn.setDefault(True)
|
|
506
209
|
button_layout.addWidget(self._create_btn)
|
|
507
210
|
|
|
508
211
|
# 取消按钮
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
button_layout.addWidget(
|
|
212
|
+
cancel_btn = QPushButton("取消")
|
|
213
|
+
cancel_btn.setFixedSize(80, 35)
|
|
214
|
+
cancel_btn.clicked.connect(self.close)
|
|
215
|
+
button_layout.addWidget(cancel_btn)
|
|
513
216
|
|
|
514
|
-
# 添加按钮区域到主布局
|
|
515
217
|
layout.addLayout(button_layout)
|
|
516
|
-
|
|
517
|
-
# 初始更新语言
|
|
518
|
-
# 延迟调用,确保所有UI元素都已创建
|
|
519
|
-
self._update_language()
|
|
520
|
-
|
|
521
|
-
# 初始更新按钮文本
|
|
522
|
-
self._update_button_texts()
|
|
218
|
+
layout.addStretch()
|
|
523
219
|
|
|
524
220
|
def _on_name_changed(self, text):
|
|
525
221
|
"""项目名称改变时的处理"""
|
|
526
222
|
self._proj_name = text.strip()
|
|
527
223
|
self._create_btn.setEnabled(bool(self._proj_name and self._proj_path))
|
|
528
|
-
|
|
529
|
-
def _on_cancel(self):
|
|
530
|
-
"""Handle cancel button click"""
|
|
531
|
-
self.projectCancelled.emit()
|
|
532
|
-
# Exit the application when cancel is clicked
|
|
533
|
-
QApplication.quit()
|
|
534
|
-
|
|
535
|
-
def _on_next_or_create(self):
|
|
536
|
-
"""Handle next or create button click"""
|
|
537
|
-
current_index = self._stacked_widget.currentIndex()
|
|
538
|
-
max_index = self._stacked_widget.count() - 1
|
|
539
|
-
|
|
540
|
-
if current_index < max_index:
|
|
541
|
-
# Go to next page
|
|
542
|
-
self._stacked_widget.setCurrentIndex(current_index + 1)
|
|
543
|
-
else:
|
|
544
|
-
# Create project
|
|
545
|
-
self._create_project()
|
|
546
|
-
|
|
547
|
-
def _on_previous_or_cancel(self):
|
|
548
|
-
"""Handle previous or cancel button click"""
|
|
549
|
-
current_index = self._stacked_widget.currentIndex()
|
|
550
|
-
|
|
551
|
-
if current_index > 0:
|
|
552
|
-
# Go to previous page
|
|
553
|
-
self._stacked_widget.setCurrentIndex(current_index - 1)
|
|
554
|
-
else:
|
|
555
|
-
# Cancel
|
|
556
|
-
self._on_cancel()
|
|
557
|
-
|
|
558
|
-
def _on_page_changed(self, index):
|
|
559
|
-
"""Handle page changed signal"""
|
|
560
|
-
self._update_button_texts()
|
|
561
|
-
|
|
562
|
-
def _update_button_texts(self):
|
|
563
|
-
"""Update button texts based on current page index"""
|
|
564
|
-
current_index = self._stacked_widget.currentIndex()
|
|
565
|
-
max_index = self._stacked_widget.count() - 1
|
|
566
|
-
|
|
567
|
-
# Update create/next button
|
|
568
|
-
if current_index < max_index:
|
|
569
|
-
self._create_btn.setText(self.lang('next_page'))
|
|
570
|
-
else:
|
|
571
|
-
self._create_btn.setText(self.lang('create_project'))
|
|
572
|
-
|
|
573
|
-
# Update cancel/previous button
|
|
574
|
-
if current_index == 0:
|
|
575
|
-
self._cancel_btn.setText(self.lang('cancel'))
|
|
576
|
-
else:
|
|
577
|
-
self._cancel_btn.setText(self.lang('previous_page'))
|
|
578
224
|
|
|
579
225
|
def _browse_path(self):
|
|
580
226
|
"""浏览选择路径"""
|
|
581
227
|
current_path = self._path_input.text() or os.path.expanduser("~")
|
|
582
228
|
path = QFileDialog.getExistingDirectory(
|
|
583
|
-
self,
|
|
229
|
+
self, "选择项目保存位置", current_path
|
|
584
230
|
)
|
|
585
231
|
if path:
|
|
586
232
|
self._path_input.setText(path)
|
|
587
233
|
self._proj_path = path
|
|
588
234
|
self._create_btn.setEnabled(bool(self._proj_name and self._proj_path))
|
|
589
|
-
# Save settings after path change
|
|
590
|
-
self._save_settings()
|
|
591
235
|
|
|
592
236
|
def _create_project(self):
|
|
593
237
|
"""创建项目"""
|
|
@@ -600,8 +244,8 @@ class QProjectBody(QWidget):
|
|
|
600
244
|
# 检查路径是否已存在
|
|
601
245
|
if os.path.exists(full_path):
|
|
602
246
|
reply = QMessageBox.question(
|
|
603
|
-
self,
|
|
604
|
-
|
|
247
|
+
self, "路径已存在",
|
|
248
|
+
f"路径 '{full_path}' 已存在。\n是否继续?",
|
|
605
249
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
|
606
250
|
)
|
|
607
251
|
if reply != QMessageBox.StandardButton.Yes:
|
|
@@ -612,22 +256,15 @@ class QProjectBody(QWidget):
|
|
|
612
256
|
self._extract_project_template(full_path)
|
|
613
257
|
|
|
614
258
|
QMessageBox.information(
|
|
615
|
-
self,
|
|
616
|
-
|
|
259
|
+
self, "成功",
|
|
260
|
+
f"项目 '{self._proj_name}' 创建成功!\n路径: {full_path}"
|
|
617
261
|
)
|
|
618
|
-
|
|
619
|
-
# 处理预制件
|
|
620
|
-
self._process_prefabs(full_path)
|
|
621
|
-
|
|
622
|
-
self.projectCreated.emit(self._proj_name, full_path)
|
|
623
|
-
|
|
624
|
-
# 成功创建项目后关闭程序
|
|
625
|
-
QApplication.quit()
|
|
262
|
+
self.close()
|
|
626
263
|
|
|
627
264
|
except Exception as e:
|
|
628
265
|
QMessageBox.critical(
|
|
629
|
-
self,
|
|
630
|
-
|
|
266
|
+
self, "错误",
|
|
267
|
+
f"项目创建失败:\n{str(e)}"
|
|
631
268
|
)
|
|
632
269
|
|
|
633
270
|
def _extract_project_template(self, target_path):
|
|
@@ -650,9 +287,14 @@ class QProjectBody(QWidget):
|
|
|
650
287
|
print(f"[DEBUG] 项目模板已解压到: {target_path}") # 调试输出
|
|
651
288
|
|
|
652
289
|
# 获取用户选择的配置
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
290
|
+
lang_text = self._lang_combo.currentText()
|
|
291
|
+
lang = lang_text.split('(')[1].strip(')')
|
|
292
|
+
|
|
293
|
+
arena_text = self._arena_combo.currentText()
|
|
294
|
+
arena = arena_text.split('(')[1].strip(')')
|
|
295
|
+
|
|
296
|
+
level_text = self._level_combo.currentText()
|
|
297
|
+
level = level_text.split('(')[1].strip(')')
|
|
656
298
|
|
|
657
299
|
# 调用配置方法
|
|
658
300
|
ToConfigAfter(path=target_path, language=lang, arena=arena, level=level)
|
|
@@ -672,268 +314,6 @@ class QProjectBody(QWidget):
|
|
|
672
314
|
print(f"[DEBUG] 执行预设继承模式配置")
|
|
673
315
|
ToPrefabAfter(path=target_path)
|
|
674
316
|
ToCustomAfter(path=target_path)
|
|
675
|
-
|
|
676
|
-
# Properties
|
|
677
|
-
@pyqtProperty(str, constant=False)
|
|
678
|
-
def projectName(self) -> str:
|
|
679
|
-
"""Get project name"""
|
|
680
|
-
return self._proj_name
|
|
681
|
-
|
|
682
|
-
@projectName.setter
|
|
683
|
-
def projectName(self, value: str):
|
|
684
|
-
"""Set project name"""
|
|
685
|
-
self._proj_name = value.strip()
|
|
686
|
-
self._name_input.setText(self._proj_name)
|
|
687
|
-
self._create_btn.setEnabled(bool(self._proj_name and self._proj_path))
|
|
688
|
-
|
|
689
|
-
@pyqtProperty(str, constant=False)
|
|
690
|
-
def projectPath(self) -> str:
|
|
691
|
-
"""Get project path"""
|
|
692
|
-
return self._proj_path
|
|
693
|
-
|
|
694
|
-
@projectPath.setter
|
|
695
|
-
def projectPath(self, value: str):
|
|
696
|
-
"""Set project path"""
|
|
697
|
-
self._proj_path = value
|
|
698
|
-
self._path_input.setText(self._proj_path)
|
|
699
|
-
self._create_btn.setEnabled(bool(self._proj_name and self._proj_path))
|
|
700
|
-
|
|
701
|
-
@pyqtProperty(str, constant=False)
|
|
702
|
-
def language(self) -> str:
|
|
703
|
-
"""Get current language code"""
|
|
704
|
-
return self._lang_combo.currentData() or 'cn'
|
|
705
|
-
|
|
706
|
-
@language.setter
|
|
707
|
-
def language(self, value: str):
|
|
708
|
-
"""Set language code"""
|
|
709
|
-
if value == 'cn':
|
|
710
|
-
self._lang_combo.setCurrentIndex(0)
|
|
711
|
-
elif value == 'en':
|
|
712
|
-
self._lang_combo.setCurrentIndex(1)
|
|
713
|
-
|
|
714
|
-
def _process_prefabs(self, project_path: str):
|
|
715
|
-
"""
|
|
716
|
-
Process selected prefabs: copy them to src directory, handle naming conflicts,
|
|
717
|
-
and add import statements to main.py
|
|
718
|
-
|
|
719
|
-
Args:
|
|
720
|
-
project_path: Path to the newly created project
|
|
721
|
-
"""
|
|
722
|
-
print(f"Processing prefabs for project: {project_path}")
|
|
723
|
-
|
|
724
|
-
# Check if QPrefabsManager exists and has selected prefabs
|
|
725
|
-
if hasattr(self, '_prefabs_page'):
|
|
726
|
-
# Get selected prefabs as list
|
|
727
|
-
selected_prefabs = self._prefabs_page.selectedPrefabs
|
|
728
|
-
print(f"Selected prefabs: {selected_prefabs}")
|
|
729
|
-
|
|
730
|
-
if selected_prefabs:
|
|
731
|
-
# Convert to dict format using list_to_dict method
|
|
732
|
-
prefabs_dict = self._prefabs_page.list_to_dict(selected_prefabs)
|
|
733
|
-
print(f"Prefabs dict: {prefabs_dict}")
|
|
734
|
-
|
|
735
|
-
# Create src directory if it doesn't exist
|
|
736
|
-
src_dir = os.path.join(project_path, 'src')
|
|
737
|
-
os.makedirs(src_dir, exist_ok=True)
|
|
738
|
-
print(f"Src directory: {src_dir}")
|
|
739
|
-
|
|
740
|
-
# Get existing directory names in src
|
|
741
|
-
existing_dirs = [d for d in os.listdir(src_dir) if os.path.isdir(os.path.join(src_dir, d))]
|
|
742
|
-
print(f"Existing dirs in src: {existing_dirs}")
|
|
743
|
-
|
|
744
|
-
# Process each prefab
|
|
745
|
-
imported_modules = []
|
|
746
|
-
|
|
747
|
-
for name, path in prefabs_dict.items():
|
|
748
|
-
# Clean directory name: replace non-alphanumeric and non-underscore characters with underscore
|
|
749
|
-
clean_name = ''.join(c if c.isalnum() or c == '_' else '_' for c in name)
|
|
750
|
-
|
|
751
|
-
# Handle naming conflicts
|
|
752
|
-
final_name = clean_name
|
|
753
|
-
i = 2
|
|
754
|
-
while final_name in existing_dirs:
|
|
755
|
-
final_name = f"{clean_name}_{i}"
|
|
756
|
-
i += 1
|
|
757
|
-
|
|
758
|
-
# Add to existing_dirs to avoid conflicts with subsequent prefabs
|
|
759
|
-
existing_dirs.append(final_name)
|
|
760
|
-
|
|
761
|
-
# Target directory path
|
|
762
|
-
target_dir = os.path.join(src_dir, final_name)
|
|
763
|
-
|
|
764
|
-
# Copy directory from path to target_dir
|
|
765
|
-
import shutil
|
|
766
|
-
try:
|
|
767
|
-
shutil.copytree(path, target_dir)
|
|
768
|
-
imported_modules.append(final_name)
|
|
769
|
-
print(f"Copied prefab {name} to {target_dir}")
|
|
770
|
-
except Exception as e:
|
|
771
|
-
print(f"Error copying prefab {name}: {e}")
|
|
772
|
-
|
|
773
|
-
# Add import statements to main.py
|
|
774
|
-
if imported_modules:
|
|
775
|
-
main_py_path = os.path.join(project_path, 'src', 'main.py')
|
|
776
|
-
print(f"Main.py path: {main_py_path}")
|
|
777
|
-
if os.path.exists(main_py_path):
|
|
778
|
-
# Read main.py content
|
|
779
|
-
with open(main_py_path, 'r', encoding='utf-8') as f:
|
|
780
|
-
content = f.read()
|
|
781
|
-
print(f"Main.py content:\n{content}")
|
|
782
|
-
|
|
783
|
-
# Find the end of import statements
|
|
784
|
-
import_end = 0
|
|
785
|
-
lines = content.split('\n')
|
|
786
|
-
# Iterate through all lines to find the last import statement
|
|
787
|
-
for i, line in enumerate(lines):
|
|
788
|
-
if line.strip().startswith('from ') or line.strip().startswith('import '):
|
|
789
|
-
import_end = i + 1
|
|
790
|
-
elif import_end > 0 and line.strip() == '':
|
|
791
|
-
# Keep moving past empty lines after imports
|
|
792
|
-
import_end = i + 1
|
|
793
|
-
elif import_end > 0 and line.strip() != '':
|
|
794
|
-
# Stop when we reach non-empty, non-import line
|
|
795
|
-
break
|
|
796
|
-
# If no imports found, add at the beginning
|
|
797
|
-
print(f"Import end position: {import_end}")
|
|
798
|
-
|
|
799
|
-
# Add import statements
|
|
800
|
-
import_lines = []
|
|
801
|
-
for module in imported_modules:
|
|
802
|
-
import_lines.append(f"from {module} import *")
|
|
803
|
-
print(f"Import lines to add: {import_lines}")
|
|
804
|
-
|
|
805
|
-
# Process the content to properly format imports
|
|
806
|
-
# First, separate the content into import section and code section
|
|
807
|
-
import_section = []
|
|
808
|
-
code_section = []
|
|
809
|
-
in_imports = True
|
|
810
|
-
|
|
811
|
-
for line in lines:
|
|
812
|
-
if in_imports:
|
|
813
|
-
if line.strip().startswith('from ') or line.strip().startswith('import '):
|
|
814
|
-
import_section.append(line.strip())
|
|
815
|
-
elif line.strip() == '':
|
|
816
|
-
# Skip empty lines in import section
|
|
817
|
-
pass
|
|
818
|
-
else:
|
|
819
|
-
# Reached end of imports
|
|
820
|
-
in_imports = False
|
|
821
|
-
code_section.append(line)
|
|
822
|
-
else:
|
|
823
|
-
# Add all other lines to code section
|
|
824
|
-
code_section.append(line)
|
|
825
|
-
|
|
826
|
-
# Add new imports to import section
|
|
827
|
-
import_section.extend(import_lines)
|
|
828
|
-
|
|
829
|
-
# Create properly formatted content
|
|
830
|
-
# Join imports without empty lines between them
|
|
831
|
-
import_text = '\n'.join(import_section)
|
|
832
|
-
# Join code section as is
|
|
833
|
-
code_text = '\n'.join(code_section)
|
|
834
|
-
# Combine with exactly two empty lines between imports and code
|
|
835
|
-
new_content = import_text + '\n\n\n' + code_text
|
|
836
|
-
print(f"New main.py content:\n{new_content}")
|
|
837
|
-
|
|
838
|
-
# Write back to main.py
|
|
839
|
-
with open(main_py_path, 'w', encoding='utf-8') as f:
|
|
840
|
-
f.write(new_content)
|
|
841
|
-
|
|
842
|
-
print(f"Added import statements for {len(imported_modules)} modules to main.py")
|
|
843
|
-
|
|
844
|
-
# Verify the changes
|
|
845
|
-
with open(main_py_path, 'r', encoding='utf-8') as f:
|
|
846
|
-
verify_content = f.read()
|
|
847
|
-
print(f"Verified main.py content:\n{verify_content}")
|
|
848
|
-
else:
|
|
849
|
-
print(f"Main.py not found at {main_py_path}")
|
|
850
|
-
else:
|
|
851
|
-
print("No modules to import")
|
|
852
|
-
else:
|
|
853
|
-
print("No selected prefabs")
|
|
854
|
-
else:
|
|
855
|
-
print("No QPrefabsManager found")
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
class ProjectCreatorUI(QMainWindow):
|
|
859
|
-
"""Project creator main window"""
|
|
860
|
-
|
|
861
|
-
def __init__(self):
|
|
862
|
-
super().__init__()
|
|
863
|
-
self._init_ui()
|
|
864
|
-
|
|
865
|
-
def _init_ui(self):
|
|
866
|
-
"""Initialize main window"""
|
|
867
|
-
self.setWindowTitle("PyScreeps Arena - Project Wizard")
|
|
868
|
-
self.setWindowIcon(QIcon(get_pixmap()))
|
|
869
|
-
|
|
870
|
-
# Create central widget
|
|
871
|
-
central_widget = QWidget()
|
|
872
|
-
self.setCentralWidget(central_widget)
|
|
873
|
-
|
|
874
|
-
# Create main layout
|
|
875
|
-
layout = QVBoxLayout(central_widget)
|
|
876
|
-
layout.setContentsMargins(10, 10, 10, 10)
|
|
877
|
-
|
|
878
|
-
# Create project body widget
|
|
879
|
-
self._project_body = QProjectBody()
|
|
880
|
-
|
|
881
|
-
# Connect signals
|
|
882
|
-
self._project_body.projectCreated.connect(self._on_project_created)
|
|
883
|
-
self._project_body.projectCancelled.connect(self._on_project_cancelled)
|
|
884
|
-
self._project_body.languageChanged.connect(self._on_language_changed)
|
|
885
|
-
|
|
886
|
-
# Add project body to layout
|
|
887
|
-
layout.addWidget(self._project_body)
|
|
888
|
-
|
|
889
|
-
# Set window size
|
|
890
|
-
self.setFixedSize(550, 650)
|
|
891
|
-
|
|
892
|
-
def _on_project_created(self, project_name: str, project_path: str):
|
|
893
|
-
"""Handle project created signal"""
|
|
894
|
-
print(f"Project created: {project_name} at {project_path}")
|
|
895
|
-
# You can add additional handling here if needed
|
|
896
|
-
|
|
897
|
-
def _on_project_cancelled(self):
|
|
898
|
-
"""Handle project cancelled signal"""
|
|
899
|
-
print("Project creation cancelled")
|
|
900
|
-
# You can add additional handling here if needed
|
|
901
|
-
|
|
902
|
-
def _on_language_changed(self, language_code: str):
|
|
903
|
-
"""Handle language changed signal"""
|
|
904
|
-
print(f"Language changed to: {language_code}")
|
|
905
|
-
# You can add additional handling here if needed
|
|
906
|
-
|
|
907
|
-
# Properties to expose project body properties
|
|
908
|
-
@pyqtProperty(str, constant=False)
|
|
909
|
-
def projectName(self) -> str:
|
|
910
|
-
"""Get project name"""
|
|
911
|
-
return self._project_body.projectName
|
|
912
|
-
|
|
913
|
-
@projectName.setter
|
|
914
|
-
def projectName(self, value: str):
|
|
915
|
-
"""Set project name"""
|
|
916
|
-
self._project_body.projectName = value
|
|
917
|
-
|
|
918
|
-
@pyqtProperty(str, constant=False)
|
|
919
|
-
def projectPath(self) -> str:
|
|
920
|
-
"""Get project path"""
|
|
921
|
-
return self._project_body.projectPath
|
|
922
|
-
|
|
923
|
-
@projectPath.setter
|
|
924
|
-
def projectPath(self, value: str):
|
|
925
|
-
"""Set project path"""
|
|
926
|
-
self._project_body.projectPath = value
|
|
927
|
-
|
|
928
|
-
@pyqtProperty(str, constant=False)
|
|
929
|
-
def language(self) -> str:
|
|
930
|
-
"""Get current language code"""
|
|
931
|
-
return self._project_body.language
|
|
932
|
-
|
|
933
|
-
@language.setter
|
|
934
|
-
def language(self, value: str):
|
|
935
|
-
"""Set language code"""
|
|
936
|
-
self._project_body.language = value
|
|
937
317
|
|
|
938
318
|
|
|
939
319
|
def run_project_creator():
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
pyscreeps_arena/__init__.py,sha256=GWrCpZnrzPwbBzhNqk8gTvV4VSwkP2DbxK8lR0E_SzY,3223
|
|
2
2
|
pyscreeps_arena/build.py,sha256=DQeGLnID2FjpsWTbnqwt2cOp28olWK68U67bfbFJSOU,177
|
|
3
|
-
pyscreeps_arena/compiler.py,sha256=
|
|
3
|
+
pyscreeps_arena/compiler.py,sha256=RdJNwEvWOzS36UhwM6wxK2_bV2JtMBaNg6dLotVKj2k,66843
|
|
4
4
|
pyscreeps_arena/localization.py,sha256=Dr0G6n8DvT6syfC0urqwjccYpEPEfcwYyJx3f9s-6a8,6031
|
|
5
|
-
pyscreeps_arena/project.7z,sha256=
|
|
5
|
+
pyscreeps_arena/project.7z,sha256=yl-JXQwkItO8EkDrITPLSBU3krG4Hz6-z6PjDPl2Xpk,581577
|
|
6
6
|
pyscreeps_arena/afters/__init__.py,sha256=Ze8NKQoMEzIJ3WlyR8Jhhdq21gSXJUtGoINzB8iaQyE,254
|
|
7
7
|
pyscreeps_arena/afters/after_config.py,sha256=Tw8Qry0KAM3hDasmXArKoS1crPuwfiAfWiEeAcKrPdo,2455
|
|
8
8
|
pyscreeps_arena/afters/after_custom.py,sha256=pgI5xkY4X7w_NHFufQ5PUGxpCrv9Ayw6VWFwVJCHLFw,3226
|
|
@@ -11,7 +11,7 @@ pyscreeps_arena/afters/after_prefab.py,sha256=H_2LJbr_7e2yCtxWStxze259-py3TXQlyP
|
|
|
11
11
|
pyscreeps_arena/core/__init__.py,sha256=qoP_rx1TpbDLJoTm5via4XPwEPaV1FXr1SYvoVoHGms,41
|
|
12
12
|
pyscreeps_arena/core/basic.py,sha256=DFvyDTsTXf2bQtnS9s254TrkshvRwajaHcvTyVvJyqw,2790
|
|
13
13
|
pyscreeps_arena/core/config.py,sha256=x_JhVHlVZqB3qA7UyACVnwZjg2gZU-BIs49UxZzwCoE,637
|
|
14
|
-
pyscreeps_arena/core/const.py,sha256=
|
|
14
|
+
pyscreeps_arena/core/const.py,sha256=VP43eR7T0YugXN9wm5WQQF7yME6Q2yA50x9gnEUcp9U,590
|
|
15
15
|
pyscreeps_arena/core/core.py,sha256=3Nty8eLKPNgwnYk_sVNBPrWuKxBXI2od8nfEezsEAZQ,5157
|
|
16
16
|
pyscreeps_arena/core/main.py,sha256=-FNSOEjksNlDfCbUqsjtPSUW8vT3qxEdfzXqT5Tdsik,170
|
|
17
17
|
pyscreeps_arena/core/utils.py,sha256=N9OOkORvrqnJakayaFp9qyS0apWhB9lBK4xyyYkhFdo,215
|
|
@@ -20,7 +20,7 @@ pyscreeps_arena/ui/__init__.py,sha256=rxSx5ZPxMNUVokvYgOmGkxHInFUVVYh9InSbbvkgo2
|
|
|
20
20
|
pyscreeps_arena/ui/creeplogic_edit.py,sha256=XlO4aoH48UyWhQQAejyLY57yukpn9BzS0w9QXMMOeeg,314
|
|
21
21
|
pyscreeps_arena/ui/map_render.py,sha256=dS7iL5ywEwlhGEOiif0G9up3bpPI4p20o07mbnDaSD8,30414
|
|
22
22
|
pyscreeps_arena/ui/mapviewer.py,sha256=AWv9loznosIJaQC5L5szRms_zDetDiHXIsY2EEpRNB8,293
|
|
23
|
-
pyscreeps_arena/ui/project_ui.py,sha256=
|
|
23
|
+
pyscreeps_arena/ui/project_ui.py,sha256=6LTwRHqfyb1R2ry13Xtq41vCANEJL96-hqXposNr81M,12580
|
|
24
24
|
pyscreeps_arena/ui/rs_icon.py,sha256=5v8YdTv2dds4iAp9x3MXpqB_5XIOYd0uPqaq83ZNySM,22496
|
|
25
25
|
pyscreeps_arena/ui/qcreeplogic/__init__.py,sha256=FVuJ6TZyDh5WnkYlHjCWjEKGPBWl27LzqIAab-4aeuI,75
|
|
26
26
|
pyscreeps_arena/ui/qcreeplogic/model.py,sha256=_Ba5jbHcFsKQ1el36UcCnXGUimvvWSRHgzo979SpVzs,2833
|
|
@@ -45,8 +45,8 @@ pyscreeps_arena/ui/qmapv/test_simple_array.py,sha256=hPnLXcFrn6I1X4JXFM3RVpBPhU_
|
|
|
45
45
|
pyscreeps_arena/ui/qrecipe/__init__.py,sha256=2Guvr9k5kGkZboiVC0aNF4u48LRbmcCm2dqOhEF52Tw,59
|
|
46
46
|
pyscreeps_arena/ui/qrecipe/model.py,sha256=s3lr_DXtsBgt8bVg1_wLz-dX88QKi77mNkqM5VJsGwE,13200
|
|
47
47
|
pyscreeps_arena/ui/qrecipe/qrecipe.py,sha256=z57VLmlpMripdpGtVCkKR0csJQhw5-WpocZK5l2xTVg,39398
|
|
48
|
-
pyscreeps_arena-0.5.
|
|
49
|
-
pyscreeps_arena-0.5.
|
|
50
|
-
pyscreeps_arena-0.5.
|
|
51
|
-
pyscreeps_arena-0.5.
|
|
52
|
-
pyscreeps_arena-0.5.
|
|
48
|
+
pyscreeps_arena-0.5.9a0.dist-info/METADATA,sha256=WO8MT9aDrX2NK1eOY2UyHPflms4IeaY0ZtUur_yhUPQ,2356
|
|
49
|
+
pyscreeps_arena-0.5.9a0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
50
|
+
pyscreeps_arena-0.5.9a0.dist-info/entry_points.txt,sha256=pnpuPPadwQsxQPaR1rXzUo0fUvhOcC7HTHlf7TYXr7M,141
|
|
51
|
+
pyscreeps_arena-0.5.9a0.dist-info/top_level.txt,sha256=l4uLyMR2NOy41ngBMh795jOHTFk3tgYKy64-9cgjVng,16
|
|
52
|
+
pyscreeps_arena-0.5.9a0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|