pyscreeps-arena 0.3.0__py3-none-any.whl → 0.5.7b0__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.
@@ -0,0 +1,709 @@
1
+ from PyQt6.QtWidgets import (
2
+ QWidget, QHBoxLayout, QVBoxLayout, QGridLayout, QLabel, QLineEdit,
3
+ QCheckBox, QSpinBox, QPushButton, QFrame, QGroupBox,
4
+ QComboBox, QApplication
5
+ )
6
+ from PyQt6.QtCore import (
7
+ pyqtProperty, pyqtSignal, Qt
8
+ )
9
+ from pyscreeps_arena.ui.qrecipe.qrecipe import QPSARecipe
10
+ from pyscreeps_arena.ui.qcreeplogic.model import CreepLogicSettings
11
+ from typing import List, Optional, Union
12
+
13
+ # 全局样式常量
14
+ CHECKBOX_STYLE = "QCheckBox::indicator { width: 16px; height: 16px; border: 3px solid #555; border-radius: 5px; background-color: white; } QCheckBox::indicator:checked { background-color: #4CAF50; image: url('data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'18\' height=\'18\' viewBox=\'0 0 24 24\'><path fill=\'white\' d=\'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\'/></svg>'); }"
15
+
16
+ class QPSACreepLogic(QWidget):
17
+ """
18
+ 爬虫逻辑设置组件,用于配置爬虫的基本属性和行为。
19
+ """
20
+
21
+ # 定义信号
22
+ onChanged = pyqtSignal()
23
+ onReset = pyqtSignal()
24
+ onCopy = pyqtSignal()
25
+
26
+ def __init__(self, parent: Optional[QWidget] = None):
27
+ super().__init__(parent)
28
+
29
+ # 模型
30
+ self._settings = CreepLogicSettings()
31
+
32
+ # QPSARecipe组件
33
+ self._qrecipe = QPSARecipe()
34
+ self._qrecipe.onChanged.connect(self._on_recipe_changed)
35
+
36
+ # 初始化UI
37
+ self.init_ui()
38
+
39
+ def init_ui(self):
40
+ main_layout = QVBoxLayout(self)
41
+ main_layout.setSpacing(15)
42
+ main_layout.setContentsMargins(10, 10, 10, 10)
43
+
44
+ # 1. 类名和继承类名行
45
+ class_row = QHBoxLayout()
46
+ class_row.setSpacing(10)
47
+
48
+ # 左边:类名
49
+ class_name_group = QWidget()
50
+ class_name_layout = QVBoxLayout(class_name_group)
51
+ class_name_layout.setContentsMargins(0, 0, 0, 0)
52
+ class_name_label = QLabel("类名")
53
+ self._class_name_edit = QLineEdit()
54
+ self._class_name_edit.setPlaceholderText("MyCreep") # 类名默认为MyCreep的placeholder
55
+ class_name_layout.addWidget(class_name_label)
56
+ class_name_layout.addWidget(self._class_name_edit)
57
+ class_name_group.setFixedWidth(240) # 3:2比例,总宽度600的话
58
+
59
+ # 右边:继承类名
60
+ inherit_name_group = QWidget()
61
+ inherit_name_layout = QVBoxLayout(inherit_name_group)
62
+ inherit_name_layout.setContentsMargins(0, 0, 0, 0)
63
+ inherit_name_label = QLabel("继承类名")
64
+ self._inherit_name_edit = QLineEdit("CreepLogic")
65
+ inherit_name_layout.addWidget(inherit_name_label)
66
+ inherit_name_layout.addWidget(self._inherit_name_edit)
67
+ inherit_name_group.setFixedWidth(160) # 3:2比例
68
+
69
+ class_row.addWidget(class_name_group)
70
+ class_row.addWidget(inherit_name_group)
71
+ class_row.addStretch()
72
+ main_layout.addLayout(class_row)
73
+
74
+ # 2. 属性字段行(参考engine.py设计,除了RECIPE)
75
+ properties_group = QGroupBox("属性设置")
76
+ properties_layout = QGridLayout(properties_group)
77
+ properties_layout.setSpacing(15)
78
+
79
+ # 设置列宽比例为1:1
80
+ properties_layout.setColumnStretch(0, 1) # 标签列
81
+ properties_layout.setColumnStretch(1, 1) # 控件列
82
+ properties_layout.setColumnStretch(2, 1) # 标签列
83
+ properties_layout.setColumnStretch(3, 1) # 控件列
84
+
85
+ # 设置固定宽度
86
+ label_width = 80
87
+
88
+ # 第一列
89
+ # NAME属性
90
+ name_label = QLabel("NAME")
91
+ name_label.setFixedWidth(label_width)
92
+ self._name_edit = QLineEdit()
93
+ # 设置初始PlaceHolder与默认类名一致
94
+ default_class_name = self._class_name_edit.text().strip() or "MyCreep"
95
+ self._name_edit.setPlaceholderText(default_class_name)
96
+ properties_layout.addWidget(name_label, 0, 0)
97
+ properties_layout.addWidget(self._name_edit, 0, 1)
98
+
99
+ # Draw属性
100
+ draw_label = QLabel("DRAW")
101
+ draw_label.setFixedWidth(label_width)
102
+ self._draw_checkbox = QCheckBox()
103
+ # 增加复选框大小
104
+ self._draw_checkbox.setStyleSheet(CHECKBOX_STYLE)
105
+ properties_layout.addWidget(draw_label, 1, 0)
106
+ properties_layout.addWidget(self._draw_checkbox, 1, 1)
107
+
108
+ # Layer属性
109
+ layer_label = QLabel("LAYER")
110
+ layer_label.setFixedWidth(label_width)
111
+ self._layer_spinbox = QSpinBox()
112
+ self._layer_spinbox.setRange(0, 100)
113
+ self._layer_spinbox.setValue(10)
114
+ properties_layout.addWidget(layer_label, 2, 0)
115
+ properties_layout.addWidget(self._layer_spinbox, 2, 1)
116
+
117
+ # Once属性
118
+ once_label = QLabel("ONCE")
119
+ once_label.setFixedWidth(label_width)
120
+ self._once_checkbox = QCheckBox()
121
+ self._once_checkbox.setChecked(True)
122
+ # 增加复选框大小
123
+ self._once_checkbox.setStyleSheet(CHECKBOX_STYLE)
124
+ properties_layout.addWidget(once_label, 3, 0)
125
+ properties_layout.addWidget(self._once_checkbox, 3, 1)
126
+
127
+ # 第二列
128
+ # Spawnable属性
129
+ spawnable_label = QLabel("SPAWNABLE")
130
+ spawnable_label.setFixedWidth(label_width)
131
+ self._spawnable_checkbox = QCheckBox()
132
+ self._spawnable_checkbox.setChecked(True)
133
+ # 增加复选框大小
134
+ self._spawnable_checkbox.setStyleSheet(CHECKBOX_STYLE)
135
+ properties_layout.addWidget(spawnable_label, 0, 2)
136
+ properties_layout.addWidget(self._spawnable_checkbox, 0, 3)
137
+
138
+ # Optimise属性
139
+ optimise_label = QLabel("OPTIMISE")
140
+ optimise_label.setFixedWidth(label_width)
141
+ self._optimise_checkbox = QCheckBox()
142
+ self._optimise_checkbox.setChecked(True)
143
+ # 增加复选框大小
144
+ self._optimise_checkbox.setStyleSheet(CHECKBOX_STYLE)
145
+ # 连接信号,实现与QPSARecipe的'自动优化'同步
146
+ self._optimise_checkbox.stateChanged.connect(self._on_optimise_changed)
147
+ properties_layout.addWidget(optimise_label, 1, 2)
148
+ properties_layout.addWidget(self._optimise_checkbox, 1, 3)
149
+
150
+ # Extension属性
151
+ extension_label = QLabel("EXTENSION")
152
+ extension_label.setFixedWidth(label_width)
153
+ self._extension_checkbox = QCheckBox()
154
+ self._extension_checkbox.setChecked(True)
155
+ # 增加复选框大小
156
+ self._extension_checkbox.setStyleSheet(CHECKBOX_STYLE)
157
+ properties_layout.addWidget(extension_label, 2, 2)
158
+ properties_layout.addWidget(self._extension_checkbox, 2, 3)
159
+
160
+ # Direction属性
161
+ direction_label = QLabel("DIRECTION")
162
+ direction_label.setFixedWidth(label_width)
163
+ # 使用QComboBox显示方向符号
164
+ self._direction_combo = QComboBox()
165
+ self._direction_combo.addItem("None", None)
166
+ self._direction_combo.addItem("IN", 9) # IN_DIRECT
167
+ self._direction_combo.addItem("OUT", 10) # OUT_DIRECT
168
+ self._direction_combo.addItem("↑", 1) # TOP
169
+ self._direction_combo.addItem("↗", 2) # TOP_RIGHT
170
+ self._direction_combo.addItem("→", 3) # RIGHT
171
+ self._direction_combo.addItem("↘", 4) # BOTTOM_RIGHT
172
+ self._direction_combo.addItem("↓", 5) # BOTTOM
173
+ self._direction_combo.addItem("↙", 6) # BOTTOM_LEFT
174
+ self._direction_combo.addItem("←", 7) # LEFT
175
+ self._direction_combo.addItem("↖", 8) # TOP_LEFT
176
+ self._direction_combo.currentIndexChanged.connect(self._on_settings_changed)
177
+ properties_layout.addWidget(direction_label, 3, 2)
178
+ properties_layout.addWidget(self._direction_combo, 3, 3)
179
+
180
+ main_layout.addWidget(properties_group)
181
+
182
+ # 3.5 函数控制行
183
+ functions_group = QGroupBox("函数控制")
184
+ functions_layout = QGridLayout(functions_group)
185
+ functions_layout.setHorizontalSpacing(20) # 水平间距20px
186
+ functions_layout.setVerticalSpacing(10) # 垂直间距10px
187
+
188
+ # onLoading复选框
189
+ self._on_loading_checkbox = QCheckBox("onLoading")
190
+ self._on_loading_checkbox.setChecked(True) # 默认开启
191
+ self._on_loading_checkbox.setStyleSheet(CHECKBOX_STYLE)
192
+ functions_layout.addWidget(self._on_loading_checkbox, 0, 0)
193
+
194
+ # onStart复选框
195
+ self._on_start_checkbox = QCheckBox("onStart")
196
+ self._on_start_checkbox.setChecked(False) # 默认关闭
197
+ self._on_start_checkbox.setStyleSheet(CHECKBOX_STYLE)
198
+ functions_layout.addWidget(self._on_start_checkbox, 0, 1)
199
+
200
+ # onStop复选框
201
+ self._on_stop_checkbox = QCheckBox("onStop")
202
+ self._on_stop_checkbox.setChecked(False) # 默认关闭
203
+ self._on_stop_checkbox.setStyleSheet(CHECKBOX_STYLE)
204
+ functions_layout.addWidget(self._on_stop_checkbox, 0, 2)
205
+
206
+ # onChanged复选框
207
+ self._on_changed_checkbox = QCheckBox("onChanged")
208
+ self._on_changed_checkbox.setChecked(False) # 默认关闭
209
+ self._on_changed_checkbox.setStyleSheet(CHECKBOX_STYLE)
210
+ functions_layout.addWidget(self._on_changed_checkbox, 1, 0)
211
+
212
+ # onKilled复选框
213
+ self._on_killed_checkbox = QCheckBox("onKilled")
214
+ self._on_killed_checkbox.setChecked(False) # 默认关闭
215
+ self._on_killed_checkbox.setStyleSheet(CHECKBOX_STYLE)
216
+ functions_layout.addWidget(self._on_killed_checkbox, 1, 1)
217
+
218
+ # onDraw复选框
219
+ self._on_draw_checkbox = QCheckBox("onDraw")
220
+ self._on_draw_checkbox.setChecked(False) # 默认关闭
221
+ self._on_draw_checkbox.setStyleSheet(CHECKBOX_STYLE)
222
+ functions_layout.addWidget(self._on_draw_checkbox, 1, 2)
223
+ main_layout.addWidget(functions_group)
224
+
225
+ # 4. QPSARecipe组件
226
+ recipe_group = QGroupBox("爬虫配方")
227
+ recipe_layout = QVBoxLayout(recipe_group)
228
+ recipe_layout.addWidget(self._qrecipe)
229
+ main_layout.addWidget(recipe_group)
230
+
231
+ # 5. 按钮行
232
+ buttons_row = QHBoxLayout()
233
+ buttons_row.setSpacing(10)
234
+
235
+ # Reset按钮
236
+ self._reset_button = QPushButton("Reset")
237
+ self._reset_button.clicked.connect(self._on_reset_clicked)
238
+
239
+ # Copy按钮
240
+ self._copy_button = QPushButton("Copy")
241
+ self._copy_button.clicked.connect(self._on_copy_clicked)
242
+
243
+ buttons_row.addWidget(self._reset_button)
244
+ buttons_row.addWidget(self._copy_button)
245
+ buttons_row.addStretch()
246
+ main_layout.addLayout(buttons_row)
247
+
248
+ # 连接信号
249
+ self._connect_signals()
250
+
251
+ def _connect_signals(self):
252
+ """
253
+ 连接所有信号
254
+ """
255
+ # 类名和继承类名信号
256
+ self._class_name_edit.textChanged.connect(self._on_settings_changed)
257
+ self._class_name_edit.textChanged.connect(self._on_class_name_changed)
258
+ self._inherit_name_edit.textChanged.connect(self._on_settings_changed)
259
+
260
+ # NAME字段信号
261
+ self._name_edit.textChanged.connect(self._on_settings_changed)
262
+
263
+ # 属性字段信号
264
+ self._draw_checkbox.stateChanged.connect(self._on_settings_changed)
265
+ self._layer_spinbox.valueChanged.connect(self._on_settings_changed)
266
+ self._once_checkbox.stateChanged.connect(self._on_settings_changed)
267
+ self._spawnable_checkbox.stateChanged.connect(self._on_settings_changed)
268
+ self._extension_checkbox.stateChanged.connect(self._on_settings_changed)
269
+
270
+ # 连接QPSARecipe的onChanged信号,用于同步optimise设置
271
+ self._qrecipe.onChanged.connect(self._on_qrecipe_changed)
272
+
273
+ def _on_settings_changed(self):
274
+ """
275
+ 设置改变时更新模型并发出信号
276
+ """
277
+ self._update_settings()
278
+ self.onChanged.emit()
279
+
280
+ def _on_class_name_changed(self):
281
+ """
282
+ 类名改变时更新NAME的PlaceHolder
283
+ """
284
+ # 获取当前类名或默认值
285
+ class_name = self._class_name_edit.text().strip() or "MyCreep"
286
+ # 更新NAME的PlaceHolder与类名一致
287
+ self._name_edit.setPlaceholderText(class_name)
288
+
289
+ def _on_recipe_changed(self):
290
+ """
291
+ 配方改变时更新模型并发出信号
292
+ """
293
+ self._settings.recipe = self._qrecipe.recipe
294
+ self.onChanged.emit()
295
+
296
+ def _on_optimise_changed(self, state):
297
+ """
298
+ OPTIMISE复选框改变时同步QPSARecipe的'自动优化'设置
299
+ """
300
+ optimise = state == Qt.CheckState.Checked.value
301
+ self._settings.optimise = optimise
302
+ # 同步QPSARecipe的optimise设置
303
+ self._qrecipe.optimise = optimise
304
+ self.onChanged.emit()
305
+
306
+ def _on_qrecipe_changed(self):
307
+ """
308
+ QPSARecipe改变时同步optimise设置
309
+ """
310
+ # 只有当QPSARecipe的optimise设置与当前不同时才更新
311
+ if self._qrecipe.optimise != self._settings.optimise:
312
+ self._settings.optimise = self._qrecipe.optimise
313
+ self._optimise_checkbox.setChecked(self._qrecipe.optimise)
314
+ self.onChanged.emit()
315
+
316
+ def _on_reset_clicked(self):
317
+ """
318
+ Reset按钮点击事件
319
+ """
320
+ self.reset()
321
+ self.onReset.emit()
322
+
323
+ def _on_copy_clicked(self):
324
+ """
325
+ Copy按钮点击事件
326
+ """
327
+ # 生成Python代码
328
+ python_code = self.generate_python_code()
329
+ # 将代码复制到剪贴板
330
+ clipboard = QApplication.clipboard()
331
+ clipboard.setText(python_code)
332
+ self.onCopy.emit()
333
+
334
+ def generate_python_code(self) -> str:
335
+ """
336
+ 生成Python代码,以class <classname>(inherit)开始
337
+ """
338
+ self._update_settings()
339
+
340
+ # 获取类名和继承类名
341
+ class_name = self._class_name_edit.text().strip() or "MyCreep"
342
+ inherit_name = self._inherit_name_edit.text().strip() or "CreepLogic"
343
+
344
+ # 构建类定义
345
+ code_lines = []
346
+ code_lines.append(f"class {class_name}({inherit_name}):")
347
+ name_value = self._settings.name if self._settings.name else class_name
348
+ code_lines.append(f" NAME = \"{name_value}\"")
349
+
350
+ # 添加属性
351
+ if self._settings.draw:
352
+ code_lines.append(f" DRAW = {self._settings.draw}")
353
+ if self._settings.layer != 10:
354
+ code_lines.append(f" LAYER = {self._settings.layer}")
355
+ if self._settings.link is not None:
356
+ if isinstance(self._settings.link, list):
357
+ code_lines.append(f" LINK = {self._settings.link}")
358
+ else:
359
+ code_lines.append(f" LINK = \"{self._settings.link}\"")
360
+ if not self._settings.once:
361
+ code_lines.append(f" ONCE = {self._settings.once}")
362
+ if not self._settings.spawnable:
363
+ code_lines.append(f" SPAWNABLE = {self._settings.spawnable}")
364
+
365
+ # 添加配方,使用qrecipe的.string属性
366
+ recipe_string = self._qrecipe.string
367
+ if recipe_string:
368
+ code_lines.append(f" RECIPE = \"{recipe_string}\"")
369
+
370
+ if not self._settings.optimise:
371
+ code_lines.append(f" OPTIMISE = {self._settings.optimise}")
372
+ if not self._settings.extension:
373
+ code_lines.append(f" EXTENSION = {self._settings.extension}")
374
+ if self._settings.direction is not None:
375
+ # 方向映射:数字 -> 常量名称
376
+ direction_map = {
377
+ 1: "TOP",
378
+ 2: "TOP_RIGHT",
379
+ 3: "RIGHT",
380
+ 4: "BOTTOM_RIGHT",
381
+ 5: "BOTTOM",
382
+ 6: "BOTTOM_LEFT",
383
+ 7: "LEFT",
384
+ 8: "TOP_LEFT",
385
+ 9: "IN_DIRECT",
386
+ 10: "OUT_DIRECT"
387
+ }
388
+ direction_name = direction_map.get(self._settings.direction, self._settings.direction)
389
+ code_lines.append(f" DIRECTION = {direction_name}")
390
+
391
+ # 添加空行和基本方法框架
392
+ # 根据复选框状态决定是否生成onLoading方法
393
+ if self._on_loading_checkbox.isChecked():
394
+ code_lines.append("")
395
+ code_lines.append(" def onLoading(self, c: Creep | None, k: GlobalKnowledge, *refs: \"CreepLogic\") -> None:")
396
+ code_lines.append(" # 初始化逻辑")
397
+ code_lines.append(" pass")
398
+
399
+ # 根据复选框状态决定是否生成onStart方法
400
+ if self._on_start_checkbox.isChecked():
401
+ code_lines.append("")
402
+ code_lines.append(" def onStart(self, c: Creep, k: GlobalKnowledge, *refs: \"CreepLogic\") -> None:")
403
+ code_lines.append(" # 启动逻辑")
404
+ code_lines.append(" pass")
405
+
406
+ # 必生成onStep方法
407
+ code_lines.append("")
408
+ code_lines.append(" def onStep(self, c: Creep, k: GlobalKnowledge, *refs):")
409
+ code_lines.append(" # 主逻辑")
410
+ code_lines.append(" pass")
411
+
412
+ # 根据复选框状态决定是否生成onStop方法
413
+ if self._on_stop_checkbox.isChecked():
414
+ code_lines.append("")
415
+ code_lines.append(" def onStop(self, c: Creep | None, k: GlobalKnowledge, *refs: \"CreepLogic\") -> None:")
416
+ code_lines.append(" # 停止逻辑")
417
+ code_lines.append(" pass")
418
+
419
+ # 根据复选框状态决定是否生成onChanged方法
420
+ if self._on_changed_checkbox.isChecked():
421
+ code_lines.append("")
422
+ code_lines.append(" def onChanged(self, src: str, dst: str, inst: any, k: GlobalKnowledge):")
423
+ code_lines.append(" # 状态变化逻辑")
424
+ code_lines.append(" pass")
425
+
426
+ # 根据复选框状态决定是否生成onKilled方法
427
+ if self._on_killed_checkbox.isChecked():
428
+ code_lines.append("")
429
+ code_lines.append(" def onKilled(self, c: Creep | None, k: GlobalKnowledge, *refs: \"CreepLogic\") -> None:")
430
+ code_lines.append(" # 被杀死逻辑")
431
+ code_lines.append(" pass")
432
+
433
+ # 根据复选框状态决定是否生成onDraw方法
434
+ if self._on_draw_checkbox.isChecked():
435
+ code_lines.append("")
436
+ code_lines.append(" def onDraw(self, c: Creep, v: View, k: GlobalKnowledge, *refs: \"CreepLogic\") -> None:")
437
+ code_lines.append(" # 绘制逻辑")
438
+ code_lines.append(" pass")
439
+
440
+ # 合并所有行
441
+ return "\n".join(code_lines)
442
+
443
+ def _update_settings(self):
444
+ """
445
+ 从UI更新模型
446
+ """
447
+ # 更新NAME
448
+ self._settings.name = self._name_edit.text()
449
+
450
+ # 更新属性
451
+ self._settings.draw = self._draw_checkbox.isChecked()
452
+ self._settings.layer = self._layer_spinbox.value()
453
+
454
+ # 移除LINK的设定
455
+ self._settings.link = None
456
+
457
+ self._settings.once = self._once_checkbox.isChecked()
458
+ self._settings.spawnable = self._spawnable_checkbox.isChecked()
459
+ self._settings.optimise = self._optimise_checkbox.isChecked()
460
+ self._settings.extension = self._extension_checkbox.isChecked()
461
+
462
+ # 处理DIRECTION
463
+ direction_val = self._direction_combo.currentData()
464
+ self._settings.direction = direction_val
465
+
466
+ def reset(self):
467
+ """
468
+ 重置所有设置为默认值
469
+ """
470
+ # 重置模型
471
+ self._settings = CreepLogicSettings()
472
+
473
+ # 重置UI
474
+ self._class_name_edit.clear()
475
+ self._inherit_name_edit.setText("CreepLogic")
476
+ self._name_edit.clear()
477
+
478
+ # 重置属性
479
+ self._draw_checkbox.setChecked(False)
480
+ self._layer_spinbox.setValue(10)
481
+ # 移除LINK的设定,无需重置
482
+ self._once_checkbox.setChecked(True)
483
+ self._spawnable_checkbox.setChecked(True)
484
+ self._qrecipe.recipe = ["MOVE"]
485
+ self._optimise_checkbox.setChecked(True)
486
+ # 同步QPSARecipe的optimise设置
487
+ self._qrecipe.optimise = True
488
+ self._extension_checkbox.setChecked(True)
489
+ self._direction_combo.setCurrentIndex(0)
490
+
491
+ # 重置函数控制复选框
492
+ self._on_loading_checkbox.setChecked(True)
493
+ self._on_start_checkbox.setChecked(False)
494
+ self._on_stop_checkbox.setChecked(False)
495
+ self._on_changed_checkbox.setChecked(False)
496
+ self._on_killed_checkbox.setChecked(False)
497
+ self._on_draw_checkbox.setChecked(False)
498
+
499
+ self.onChanged.emit()
500
+
501
+ def get_settings(self) -> CreepLogicSettings:
502
+ """
503
+ 获取当前设置
504
+ """
505
+ self._update_settings()
506
+ return self._settings
507
+
508
+ def set_settings(self, settings: CreepLogicSettings):
509
+ """
510
+ 设置当前配置
511
+ """
512
+ self._settings = settings
513
+
514
+ # 更新UI
515
+ self._name_edit.setText(settings.name)
516
+
517
+ # 更新属性
518
+ self._draw_checkbox.setChecked(settings.draw)
519
+ self._layer_spinbox.setValue(settings.layer)
520
+
521
+ # 移除LINK的设定,无需更新
522
+
523
+ self._once_checkbox.setChecked(settings.once)
524
+ self._spawnable_checkbox.setChecked(settings.spawnable)
525
+ self._qrecipe.recipe = settings.recipe if isinstance(settings.recipe, list) else ["MOVE"]
526
+ self._optimise_checkbox.setChecked(settings.optimise)
527
+ # 同步QPSARecipe的optimise设置
528
+ self._qrecipe.optimise = settings.optimise
529
+ self._extension_checkbox.setChecked(settings.extension)
530
+
531
+ # 更新DIRECTION
532
+ for i in range(self._direction_combo.count()):
533
+ if self._direction_combo.itemData(i) == settings.direction:
534
+ self._direction_combo.setCurrentIndex(i)
535
+ break
536
+
537
+ self.onChanged.emit()
538
+
539
+ # Properties
540
+ @pyqtProperty(str)
541
+ def name(self) -> str:
542
+ return self._settings.name
543
+
544
+ @name.setter
545
+ def name(self, value: str):
546
+ self._settings.name = value
547
+ self._name_edit.setText(value)
548
+ self.onChanged.emit()
549
+
550
+ @pyqtProperty(bool)
551
+ def draw(self) -> bool:
552
+ return self._settings.draw
553
+
554
+ @draw.setter
555
+ def draw(self, value: bool):
556
+ self._settings.draw = value
557
+ self._draw_checkbox.setChecked(value)
558
+ self.onChanged.emit()
559
+
560
+ @pyqtProperty(int)
561
+ def layer(self) -> int:
562
+ return self._settings.layer
563
+
564
+ @layer.setter
565
+ def layer(self, value: int):
566
+ self._settings.layer = value
567
+ self._layer_spinbox.setValue(value)
568
+ self.onChanged.emit()
569
+
570
+ @pyqtProperty(object)
571
+ def link(self) -> Union[List[str], str, None]:
572
+ return self._settings.link
573
+
574
+ @link.setter
575
+ def link(self, value: Union[List[str], str, None]):
576
+ self._settings.link = value
577
+ if value is None:
578
+ self._link_edit.clear()
579
+ elif isinstance(value, list):
580
+ self._link_edit.setText(", ".join(value))
581
+ else:
582
+ self._link_edit.setText(value)
583
+ self.onChanged.emit()
584
+
585
+ @pyqtProperty(bool)
586
+ def once(self) -> bool:
587
+ return self._settings.once
588
+
589
+ @once.setter
590
+ def once(self, value: bool):
591
+ self._settings.once = value
592
+ self._once_checkbox.setChecked(value)
593
+ self.onChanged.emit()
594
+
595
+ @pyqtProperty(bool)
596
+ def spawnable(self) -> bool:
597
+ return self._settings.spawnable
598
+
599
+ @spawnable.setter
600
+ def spawnable(self, value: bool):
601
+ self._settings.spawnable = value
602
+ self._spawnable_checkbox.setChecked(value)
603
+ self.onChanged.emit()
604
+
605
+ @pyqtProperty(list)
606
+ def recipe(self) -> List[str]:
607
+ return self._settings.recipe if isinstance(self._settings.recipe, list) else ["MOVE"]
608
+
609
+ @recipe.setter
610
+ def recipe(self, value: List[str]):
611
+ self._settings.recipe = value
612
+ self._qrecipe.recipe = value
613
+ self.onChanged.emit()
614
+
615
+ @pyqtProperty(bool)
616
+ def optimise(self) -> bool:
617
+ return self._settings.optimise
618
+
619
+ @optimise.setter
620
+ def optimise(self, value: bool):
621
+ self._settings.optimise = value
622
+ self._optimise_checkbox.setChecked(value)
623
+ self.onChanged.emit()
624
+
625
+ @pyqtProperty(bool)
626
+ def extension(self) -> bool:
627
+ return self._settings.extension
628
+
629
+ @extension.setter
630
+ def extension(self, value: bool):
631
+ self._settings.extension = value
632
+ self._extension_checkbox.setChecked(value)
633
+ self.onChanged.emit()
634
+
635
+ @pyqtProperty(object)
636
+ def direction(self) -> Optional[int]:
637
+ return self._settings.direction
638
+
639
+ @direction.setter
640
+ def direction(self, value: Optional[int]):
641
+ self._settings.direction = value
642
+ for i in range(self._direction_combo.count()):
643
+ if self._direction_combo.itemData(i) == value:
644
+ self._direction_combo.setCurrentIndex(i)
645
+ break
646
+ self.onChanged.emit()
647
+
648
+ @pyqtProperty(object)
649
+ def qrecipe(self) -> QPSARecipe:
650
+ return self._qrecipe
651
+
652
+ def to_dict(self) -> dict:
653
+ """
654
+ 将设置转换为字典格式
655
+ """
656
+ self._update_settings()
657
+ return {
658
+ "class_name": self._class_name_edit.text(),
659
+ "inherit_name": self._inherit_name_edit.text(),
660
+ **self._settings.to_dict()
661
+ }
662
+
663
+ def from_dict(self, data: dict):
664
+ """
665
+ 从字典格式加载设置
666
+ """
667
+ # 加载类名和继承类名
668
+ self._class_name_edit.setText(data.get("class_name", ""))
669
+ self._inherit_name_edit.setText(data.get("inherit_name", "CreepLogic"))
670
+
671
+ # 加载设置
672
+ self._settings = CreepLogicSettings.from_dict(data)
673
+ self.set_settings(self._settings)
674
+
675
+
676
+ if __name__ == "__main__":
677
+ """
678
+ 直接运行组件的主程序入口点
679
+ """
680
+ import sys
681
+ from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
682
+
683
+ class MainWindow(QMainWindow):
684
+ def __init__(self):
685
+ super().__init__()
686
+ self.setWindowTitle("QPSACreepLogic Demo")
687
+ # 使用resize方法替换setWidth和setHeight
688
+ self.resize(380, 700)
689
+
690
+ # 中央部件
691
+ central_widget = QWidget()
692
+ main_layout = QVBoxLayout(central_widget)
693
+
694
+ # 添加QPSACreepLogic组件
695
+ self.creep_logic_widget = QPSACreepLogic()
696
+ main_layout.addWidget(self.creep_logic_widget)
697
+
698
+ self.setCentralWidget(central_widget)
699
+
700
+ # 创建应用程序和窗口
701
+ app = QApplication(sys.argv)
702
+ window = MainWindow()
703
+ window.show()
704
+
705
+ # 运行应用程序
706
+ sys.exit(app.exec())
707
+
708
+
709
+