pyscreeps-arena 0.5.9b2__py3-none-any.whl → 0.5.9b4__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.
@@ -18,6 +18,7 @@ python_version_info = sys.version_info
18
18
  python_version_info = f"{python_version_info.major}.{python_version_info.minor}.{python_version_info.micro}"
19
19
 
20
20
 
21
+
21
22
  class MatchCaseError(Exception):
22
23
  pass
23
24
 
@@ -53,6 +54,7 @@ def replace_src_prefix(file_list):
53
54
  # """
54
55
  # return PYFILE_PRAGMA_INSERTS + "\n" + content
55
56
  class Compiler_Const:
57
+ CALLED_FUNCTIONS = ['behavior', 'sequence', 'selector', 'parallel', 'listen']
56
58
  PROTO_DEFINES_DIRS = ["builtin", "library"]
57
59
  FILE_STRONG_REPLACE = {
58
60
  "std": {
@@ -1159,7 +1161,7 @@ class Compiler(CompilerBase):
1159
1161
  content = self.auto_read(fpath)
1160
1162
  content = self.stage_recursive_replace(content) # 调用stage_recursive_replace
1161
1163
  # 调用stage_called_replace处理四个装饰器
1162
- for caller in ['behavior', 'sequence', 'selector', 'parallel']:
1164
+ for caller in self.CALLED_FUNCTIONS:
1163
1165
  content = self.stage_called_replace(caller, content)
1164
1166
  with open(fpath, 'w', encoding='utf-8') as f:
1165
1167
  f.write(content)
Binary file
@@ -4,11 +4,13 @@
4
4
  """
5
5
  import sys
6
6
  import os
7
+ import json
7
8
  from pathlib import Path
8
9
  from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
9
10
  QHBoxLayout, QLabel, QLineEdit, QPushButton,
10
- QFileDialog, QMessageBox, QComboBox)
11
- from PyQt6.QtCore import Qt
11
+ QFileDialog, QMessageBox, QComboBox, QStackedWidget)
12
+ from pyscreeps_arena.ui.qprefabs.qprefabs import QPrefabsManager
13
+ from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty
12
14
  from PyQt6.QtGui import QFont
13
15
  from PyQt6.QtGui import QIcon
14
16
  from pyscreeps_arena.core import const
@@ -16,36 +18,307 @@ from pyscreeps_arena.ui.rs_icon import get_pixmap
16
18
  from pyscreeps_arena.afters import ToConfigAfter, ToEmptyAfter, ToPrefabAfter, ToCustomAfter
17
19
  from PyQt6.QtWidgets import QRadioButton, QGroupBox
18
20
 
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
+ }
19
84
 
20
85
 
21
- class ProjectCreatorUI(QMainWindow):
22
- def __init__(self):
23
- super().__init__()
86
+
87
+ class QProjectBody(QWidget):
88
+ """Project creation body widget"""
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)
24
97
  self._proj_name = ""
25
98
  self._proj_path = ""
26
99
  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()
27
293
 
28
294
  def _init_ui(self):
29
295
  """初始化UI界面"""
30
- self.setWindowTitle("PyScreeps Arena - 项目创建器")
31
- self.setWindowIcon(QIcon(get_pixmap()))
32
296
  self.setFixedSize(500, 580)
33
297
 
34
- # 主窗口部件
35
- central_widget = QWidget()
36
- self.setCentralWidget(central_widget)
37
- layout = QVBoxLayout(central_widget)
298
+ # 主布局
299
+ layout = QVBoxLayout(self)
38
300
  layout.setSpacing(15)
39
- layout.setContentsMargins(30, 30, 30, 30)
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)
40
313
 
41
314
  # 标题
42
- title = QLabel("PyScreeps Arena")
43
- title.setAlignment(Qt.AlignmentFlag.AlignCenter)
315
+ self._title_label = QLabel("PyScreeps Arena")
316
+ self._title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
44
317
  title_font = QFont()
45
318
  title_font.setPointSize(18)
46
319
  title_font.setBold(True)
47
- title.setFont(title_font)
48
- layout.addWidget(title)
320
+ self._title_label.setFont(title_font)
321
+ self._project_layout.addWidget(self._title_label)
49
322
 
50
323
  # 项目信息
51
324
  info_widget = QWidget()
@@ -53,27 +326,27 @@ class ProjectCreatorUI(QMainWindow):
53
326
  info_layout.setSpacing(8)
54
327
 
55
328
  # 版本信息
56
- version_label = QLabel(f"版本: {const.VERSION}")
57
- version_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
58
- info_layout.addWidget(version_label)
329
+ self._version_label = QLabel(f"版本: {const.VERSION}")
330
+ self._version_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
331
+ info_layout.addWidget(self._version_label)
59
332
 
60
333
  # 作者信息
61
- author_label = QLabel(f"作者: {const.AUTHOR}")
62
- author_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
63
- info_layout.addWidget(author_label)
334
+ self._author_label = QLabel(f"作者: {const.AUTHOR}")
335
+ self._author_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
336
+ info_layout.addWidget(self._author_label)
64
337
 
65
338
  # GitHub信息
66
- github_label = QLabel(f"GitHub: {const.GITHUB_NAME}")
67
- github_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
68
- info_layout.addWidget(github_label)
339
+ self._github_label = QLabel(f"GitHub: {const.GITHUB_NAME}")
340
+ self._github_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
341
+ info_layout.addWidget(self._github_label)
69
342
 
70
- layout.addWidget(info_widget)
343
+ self._project_layout.addWidget(info_widget)
71
344
 
72
345
  # 分隔线
73
346
  separator = QLabel("─" * 50)
74
347
  separator.setAlignment(Qt.AlignmentFlag.AlignCenter)
75
348
  separator.setStyleSheet("color: #ccc;")
76
- layout.addWidget(separator)
349
+ self._project_layout.addWidget(separator)
77
350
 
78
351
  # 项目输入区域
79
352
  input_widget = QWidget()
@@ -82,20 +355,20 @@ class ProjectCreatorUI(QMainWindow):
82
355
 
83
356
  # 项目名称输入
84
357
  name_layout = QHBoxLayout()
85
- name_label = QLabel("项目名称:")
86
- name_label.setFixedWidth(80)
358
+ self._name_label = QLabel("项目名称:")
359
+ self._name_label.setFixedWidth(80)
87
360
  self._name_input = QLineEdit()
88
361
  self._name_input.setPlaceholderText("输入项目名称...")
89
362
  self._name_input.textChanged.connect(self._on_name_changed)
90
363
  self._name_input.returnPressed.connect(self._create_project)
91
- name_layout.addWidget(name_label)
364
+ name_layout.addWidget(self._name_label)
92
365
  name_layout.addWidget(self._name_input)
93
366
  input_layout.addLayout(name_layout)
94
367
 
95
368
  # 项目路径输入
96
369
  path_layout = QHBoxLayout()
97
- path_label = QLabel("保存位置:")
98
- path_label.setFixedWidth(80)
370
+ self._path_label = QLabel("保存位置:")
371
+ self._path_label.setFixedWidth(80)
99
372
  self._path_input = QLineEdit()
100
373
  self._path_input.setPlaceholderText("选择项目保存位置...")
101
374
  self._path_input.setReadOnly(True)
@@ -104,23 +377,23 @@ class ProjectCreatorUI(QMainWindow):
104
377
  if os.path.exists(desktop_path):
105
378
  self._path_input.setText(desktop_path)
106
379
  self._proj_path = desktop_path
107
- path_layout.addWidget(path_label)
380
+ path_layout.addWidget(self._path_label)
108
381
  path_layout.addWidget(self._path_input)
109
382
 
110
383
  # 浏览按钮
111
- browse_btn = QPushButton("浏览...")
112
- browse_btn.setFixedWidth(60)
113
- browse_btn.clicked.connect(self._browse_path)
114
- path_layout.addWidget(browse_btn)
384
+ self._browse_btn = QPushButton("浏览...")
385
+ self._browse_btn.setFixedWidth(80)
386
+ self._browse_btn.clicked.connect(self._browse_path)
387
+ path_layout.addWidget(self._browse_btn)
115
388
 
116
389
  input_layout.addLayout(path_layout)
117
- layout.addWidget(input_widget)
390
+ self._project_layout.addWidget(input_widget)
118
391
 
119
392
  # 分隔线
120
393
  separator2 = QLabel("─" * 50)
121
394
  separator2.setAlignment(Qt.AlignmentFlag.AlignCenter)
122
395
  separator2.setStyleSheet("color: #ccc;")
123
- layout.addWidget(separator2)
396
+ self._project_layout.addWidget(separator2)
124
397
 
125
398
  # 配置选项区域(左右布局)
126
399
  config_container = QWidget()
@@ -134,34 +407,43 @@ class ProjectCreatorUI(QMainWindow):
134
407
 
135
408
  # 语言选择
136
409
  lang_layout = QHBoxLayout()
137
- lang_label = QLabel("语 言:")
138
- lang_label.setFixedWidth(80)
410
+ self._lang_label = QLabel("语 言:")
411
+ self._lang_label.setFixedWidth(80)
139
412
  self._lang_combo = QComboBox()
140
- self._lang_combo.addItems(["中文 (cn)", "英文 (en)"])
413
+ # 语言选项
414
+ self._lang_combo.addItem("中文 (cn)", "cn")
415
+ self._lang_combo.addItem("英文 (en)", "en")
141
416
  self._lang_combo.setCurrentIndex(0)
142
- lang_layout.addWidget(lang_label)
417
+ self._lang_combo.currentIndexChanged.connect(self._update_language)
418
+ lang_layout.addWidget(self._lang_label)
143
419
  lang_layout.addWidget(self._lang_combo)
144
420
  left_config_layout.addLayout(lang_layout)
145
421
 
146
422
  # 竞技场选择
147
423
  arena_layout = QHBoxLayout()
148
- arena_label = QLabel("竞技场:")
149
- arena_label.setFixedWidth(80)
424
+ self._arena_label = QLabel("竞技场:")
425
+ self._arena_label.setFixedWidth(80)
150
426
  self._arena_combo = QComboBox()
151
- self._arena_combo.addItems(["灰色 (gray)", "绿色 (green)", "蓝色 (blue)", "红色 (red)"])
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")
152
432
  self._arena_combo.setCurrentIndex(0)
153
- arena_layout.addWidget(arena_label)
433
+ arena_layout.addWidget(self._arena_label)
154
434
  arena_layout.addWidget(self._arena_combo)
155
435
  left_config_layout.addLayout(arena_layout)
156
436
 
157
437
  # 难度级别选择
158
438
  level_layout = QHBoxLayout()
159
- level_label = QLabel("难 度:")
160
- level_label.setFixedWidth(80)
439
+ self._level_label = QLabel("难 度:")
440
+ self._level_label.setFixedWidth(80)
161
441
  self._level_combo = QComboBox()
162
- self._level_combo.addItems(["基础 (basic)", "高级 (advanced)"])
442
+ # 难度选项
443
+ self._level_combo.addItem("基础 (basic)", "basic")
444
+ self._level_combo.addItem("高级 (advanced)", "advanced")
163
445
  self._level_combo.setCurrentIndex(0)
164
- level_layout.addWidget(level_label)
446
+ level_layout.addWidget(self._level_label)
165
447
  level_layout.addWidget(self._level_combo)
166
448
  left_config_layout.addLayout(level_layout)
167
449
 
@@ -194,7 +476,22 @@ class ProjectCreatorUI(QMainWindow):
194
476
 
195
477
  config_container_layout.addWidget(right_config_widget)
196
478
 
197
- layout.addWidget(config_container)
479
+ self._project_layout.addWidget(config_container)
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)
198
495
 
199
496
  # 按钮区域
200
497
  button_layout = QHBoxLayout()
@@ -202,36 +499,95 @@ class ProjectCreatorUI(QMainWindow):
202
499
 
203
500
  # 创建按钮
204
501
  self._create_btn = QPushButton("创建项目")
205
- self._create_btn.setFixedSize(100, 35)
206
- self._create_btn.clicked.connect(self._create_project)
502
+ self._create_btn.setFixedSize(120, 35)
503
+ self._create_btn.clicked.connect(self._on_next_or_create)
207
504
  self._create_btn.setEnabled(False)
208
505
  self._create_btn.setDefault(True)
209
506
  button_layout.addWidget(self._create_btn)
210
507
 
211
508
  # 取消按钮
212
- cancel_btn = QPushButton("取消")
213
- cancel_btn.setFixedSize(80, 35)
214
- cancel_btn.clicked.connect(self.close)
215
- button_layout.addWidget(cancel_btn)
509
+ self._cancel_btn = QPushButton("取消")
510
+ self._cancel_btn.setFixedSize(80, 35)
511
+ self._cancel_btn.clicked.connect(self._on_previous_or_cancel)
512
+ button_layout.addWidget(self._cancel_btn)
216
513
 
514
+ # 添加按钮区域到主布局
217
515
  layout.addLayout(button_layout)
218
- layout.addStretch()
516
+
517
+ # 初始更新语言
518
+ # 延迟调用,确保所有UI元素都已创建
519
+ self._update_language()
520
+
521
+ # 初始更新按钮文本
522
+ self._update_button_texts()
219
523
 
220
524
  def _on_name_changed(self, text):
221
525
  """项目名称改变时的处理"""
222
526
  self._proj_name = text.strip()
223
527
  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'))
224
578
 
225
579
  def _browse_path(self):
226
580
  """浏览选择路径"""
227
581
  current_path = self._path_input.text() or os.path.expanduser("~")
228
582
  path = QFileDialog.getExistingDirectory(
229
- self, "选择项目保存位置", current_path
583
+ self, self.lang('select_location'), current_path
230
584
  )
231
585
  if path:
232
586
  self._path_input.setText(path)
233
587
  self._proj_path = path
234
588
  self._create_btn.setEnabled(bool(self._proj_name and self._proj_path))
589
+ # Save settings after path change
590
+ self._save_settings()
235
591
 
236
592
  def _create_project(self):
237
593
  """创建项目"""
@@ -244,8 +600,8 @@ class ProjectCreatorUI(QMainWindow):
244
600
  # 检查路径是否已存在
245
601
  if os.path.exists(full_path):
246
602
  reply = QMessageBox.question(
247
- self, "路径已存在",
248
- f"路径 '{full_path}' 已存在。\n是否继续?",
603
+ self, self.lang('path_exists'),
604
+ self.lang('path_exists_message', full_path),
249
605
  QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
250
606
  )
251
607
  if reply != QMessageBox.StandardButton.Yes:
@@ -256,15 +612,22 @@ class ProjectCreatorUI(QMainWindow):
256
612
  self._extract_project_template(full_path)
257
613
 
258
614
  QMessageBox.information(
259
- self, "成功",
260
- f"项目 '{self._proj_name}' 创建成功!\n路径: {full_path}"
615
+ self, self.lang('success'),
616
+ self.lang('success_message', self._proj_name, full_path)
261
617
  )
262
- self.close()
618
+
619
+ # 处理预制件
620
+ self._process_prefabs(full_path)
621
+
622
+ self.projectCreated.emit(self._proj_name, full_path)
623
+
624
+ # 成功创建项目后关闭程序
625
+ QApplication.quit()
263
626
 
264
627
  except Exception as e:
265
628
  QMessageBox.critical(
266
- self, "错误",
267
- f"项目创建失败:\n{str(e)}"
629
+ self, self.lang('error'),
630
+ self.lang('error_message', str(e))
268
631
  )
269
632
 
270
633
  def _extract_project_template(self, target_path):
@@ -287,14 +650,9 @@ class ProjectCreatorUI(QMainWindow):
287
650
  print(f"[DEBUG] 项目模板已解压到: {target_path}") # 调试输出
288
651
 
289
652
  # 获取用户选择的配置
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(')')
653
+ lang = self._lang_combo.currentData() or 'cn'
654
+ arena = self._arena_combo.currentData() or 'gray'
655
+ level = self._level_combo.currentData() or 'basic'
298
656
 
299
657
  # 调用配置方法
300
658
  ToConfigAfter(path=target_path, language=lang, arena=arena, level=level)
@@ -314,6 +672,268 @@ class ProjectCreatorUI(QMainWindow):
314
672
  print(f"[DEBUG] 执行预设继承模式配置")
315
673
  ToPrefabAfter(path=target_path)
316
674
  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 - 项目创建器")
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
317
937
 
318
938
 
319
939
  def run_project_creator():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyscreeps-arena
3
- Version: 0.5.9b2
3
+ Version: 0.5.9b4
4
4
  Summary: Python api|interface to play game: Screeps: Arena.
5
5
  Author-email: 2229066748@qq.com
6
6
  Maintainer: Eagle'sBaby
@@ -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=HOiSpe4im7s9EvBbue0mgeiqB99AEXd6i4oAhPvr0X0,70505
3
+ pyscreeps_arena/compiler.py,sha256=tV9Yy3qFAcihLIIFCL5KiFW0IMIzpHZxGzR2uH6Np5I,70563
4
4
  pyscreeps_arena/localization.py,sha256=Dr0G6n8DvT6syfC0urqwjccYpEPEfcwYyJx3f9s-6a8,6031
5
- pyscreeps_arena/project.7z,sha256=odbJd-BLEHtwZTddVBDeWC0YlpCrvIZtW-rq0KT0nt0,537145
5
+ pyscreeps_arena/project.7z,sha256=mQmjO7zZmTMkQKBeaNh2AQhJW7bK9UucTS3BkaWK8pI,540775
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
@@ -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=6LTwRHqfyb1R2ry13Xtq41vCANEJL96-hqXposNr81M,12580
23
+ pyscreeps_arena/ui/project_ui.py,sha256=VusL7WBC5LSUd9XS7CYlI4D1VIpX-vihWU6bZacQ6eE,39189
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.9b2.dist-info/METADATA,sha256=TfNLZMWuRFkiLcCFPq6oYWKJAkRXez2nynQVRfPYXbQ,2356
49
- pyscreeps_arena-0.5.9b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
- pyscreeps_arena-0.5.9b2.dist-info/entry_points.txt,sha256=pnpuPPadwQsxQPaR1rXzUo0fUvhOcC7HTHlf7TYXr7M,141
51
- pyscreeps_arena-0.5.9b2.dist-info/top_level.txt,sha256=l4uLyMR2NOy41ngBMh795jOHTFk3tgYKy64-9cgjVng,16
52
- pyscreeps_arena-0.5.9b2.dist-info/RECORD,,
48
+ pyscreeps_arena-0.5.9b4.dist-info/METADATA,sha256=aV44OugxmrP9H-k50tCnxaoL3NkHD8CDHgo8YQ4YC2k,2356
49
+ pyscreeps_arena-0.5.9b4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
+ pyscreeps_arena-0.5.9b4.dist-info/entry_points.txt,sha256=pnpuPPadwQsxQPaR1rXzUo0fUvhOcC7HTHlf7TYXr7M,141
51
+ pyscreeps_arena-0.5.9b4.dist-info/top_level.txt,sha256=l4uLyMR2NOy41ngBMh795jOHTFk3tgYKy64-9cgjVng,16
52
+ pyscreeps_arena-0.5.9b4.dist-info/RECORD,,