pyscreeps-arena 0.5.7.2__tar.gz → 0.5.7.4__tar.gz
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-0.5.7.2 → pyscreeps_arena-0.5.7.4}/PKG-INFO +1 -1
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/__init__.py +4 -4
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/compiler.py +2 -1
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/core/const.py +1 -1
- pyscreeps_arena-0.5.7.4/pyscreeps_arena/project.7z +0 -0
- pyscreeps_arena-0.5.7.4/pyscreeps_arena/ui/qmapker/__init__.py +1 -0
- pyscreeps_arena-0.5.7.4/pyscreeps_arena/ui/qmapker/qmapmarker.py +339 -0
- pyscreeps_arena-0.5.7.4/pyscreeps_arena/ui/qmapker/qvariable.py +303 -0
- pyscreeps_arena-0.5.7.4/pyscreeps_arena/ui/qmapker/test_compact_variable.py +61 -0
- pyscreeps_arena-0.5.7.4/pyscreeps_arena/ui/qmapker/test_qmapmarker.py +71 -0
- pyscreeps_arena-0.5.7.4/pyscreeps_arena/ui/qmapker/test_qvariable.py +49 -0
- pyscreeps_arena-0.5.7.4/pyscreeps_arena/ui/qmapker/to_code.py +100 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena.egg-info/PKG-INFO +1 -1
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena.egg-info/SOURCES.txt +7 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/setup.py +1 -1
- pyscreeps_arena-0.5.7.2/pyscreeps_arena/project.7z +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/build.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/core/__init__.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/core/basic.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/core/config.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/core/core.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/core/main.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/core/utils.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/localization.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/P2PY.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/__init__.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/creeplogic_edit.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/map_render.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/mapviewer.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/project_ui.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qcreeplogic/__init__.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qcreeplogic/model.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qcreeplogic/qcreeplogic.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/__init__.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/qcinfo.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/qco.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/qmapv.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_array_drag.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_drag.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_qcinfo.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_qco_drag.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_qmapv.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_simple_array.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qrecipe/__init__.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qrecipe/model.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qrecipe/qrecipe.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/rs_icon.py +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena.egg-info/dependency_links.txt +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena.egg-info/entry_points.txt +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena.egg-info/requires.txt +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena.egg-info/top_level.txt +0 -0
- {pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/setup.cfg +0 -0
|
@@ -3,6 +3,10 @@ import sys
|
|
|
3
3
|
import shutil
|
|
4
4
|
import py7zr
|
|
5
5
|
from pyscreeps_arena.core import const, config
|
|
6
|
+
from pyscreeps_arena.ui.mapviewer import run_mapviewer
|
|
7
|
+
from pyscreeps_arena.ui.project_ui import run_project_creator
|
|
8
|
+
from pyscreeps_arena.ui.creeplogic_edit import run_creeplogic_edit
|
|
9
|
+
|
|
6
10
|
def CMD_NewProject():
|
|
7
11
|
"""
|
|
8
12
|
cmd:
|
|
@@ -55,7 +59,6 @@ def CMD_OpenUI():
|
|
|
55
59
|
|
|
56
60
|
# 检查是否使用mapviewer
|
|
57
61
|
if len(sys.argv) > 1 and sys.argv[1] == '-m':
|
|
58
|
-
from pyscreeps_arena.ui.mapviewer import run_mapviewer
|
|
59
62
|
# 检查语言参数
|
|
60
63
|
if len(sys.argv) > 2 and sys.argv[2] == '-e':
|
|
61
64
|
from pyscreeps_arena.core import config
|
|
@@ -63,16 +66,13 @@ def CMD_OpenUI():
|
|
|
63
66
|
run_mapviewer()
|
|
64
67
|
# 检查是否使用creeplogic edit
|
|
65
68
|
elif len(sys.argv) > 1 and sys.argv[1] == '-c':
|
|
66
|
-
from pyscreeps_arena.ui.creeplogic_edit import run_creeplogic_edit
|
|
67
69
|
run_creeplogic_edit()
|
|
68
70
|
elif len(sys.argv) > 1 and sys.argv[1] == '-e':
|
|
69
|
-
from pyscreeps_arena.ui.creeplogic_edit import run_creeplogic_edit
|
|
70
71
|
from pyscreeps_arena.core import config
|
|
71
72
|
config.language = 'en'
|
|
72
73
|
run_creeplogic_edit()
|
|
73
74
|
# 默认启用project ui
|
|
74
75
|
else:
|
|
75
|
-
from pyscreeps_arena.ui.project_ui import run_project_creator
|
|
76
76
|
run_project_creator()
|
|
77
77
|
except ImportError as e:
|
|
78
78
|
print(f"错误: 无法导入UI模块 - {e}")
|
|
@@ -159,8 +159,9 @@ class GameConstructionBoost{{
|
|
|
159
159
|
}}
|
|
160
160
|
}};
|
|
161
161
|
import {{ Portal as GamePortal}} from 'arena/season_{config.season}/{const.ARENA_GREEN}/{"advanced" if config.level in ["advance", "advanced"] else "basic"}/prototypes';
|
|
162
|
-
|
|
163
162
|
""",
|
|
163
|
+
# import {Portal} from 'arena/season_1/portal_exploration/basic/prototypes';
|
|
164
|
+
|
|
164
165
|
const.ARENA_BLUE: lambda: f"""
|
|
165
166
|
const ARENA_COLOR_TYPE = "BLUE";
|
|
166
167
|
const GameScoreCollector = GameStructureSpawn;
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from pyscreeps_arena.ui.qmapker.qmapmarker import QPSAMapMarker
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
QPSA Map Marker Component - 地图标记组件
|
|
4
|
+
左右大布局,右边是QPSAMapViewer,左边是信息栏
|
|
5
|
+
"""
|
|
6
|
+
from typing import List, Dict, Any, Optional
|
|
7
|
+
import json
|
|
8
|
+
from PyQt6.QtWidgets import (QWidget, QHBoxLayout, QVBoxLayout, QPushButton,
|
|
9
|
+
QListWidget, QListWidgetItem, QFrame, QSpacerItem,
|
|
10
|
+
QSizePolicy, QApplication)
|
|
11
|
+
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
|
|
12
|
+
from PyQt6.QtGui import QPixmap
|
|
13
|
+
|
|
14
|
+
from pyscreeps_arena.ui.qmapv.qmapv import QPSAMapViewer
|
|
15
|
+
from pyscreeps_arena.ui.qmapv.qcinfo import QPSACellInfo
|
|
16
|
+
from pyscreeps_arena.ui.qmapker.qvariable import QPSACoVariable
|
|
17
|
+
from pyscreeps_arena.ui.qmapker.to_code import json2code
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AddVariableWidget(QWidget):
|
|
21
|
+
"""Special widget with centered + button to add new variable."""
|
|
22
|
+
|
|
23
|
+
# Signal emitted when add button is clicked
|
|
24
|
+
addRequested = pyqtSignal()
|
|
25
|
+
|
|
26
|
+
def __init__(self, parent=None):
|
|
27
|
+
super().__init__(parent)
|
|
28
|
+
self._init_ui()
|
|
29
|
+
|
|
30
|
+
def _init_ui(self):
|
|
31
|
+
"""Initialize UI components."""
|
|
32
|
+
# Main layout with center alignment
|
|
33
|
+
layout = QVBoxLayout()
|
|
34
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
|
35
|
+
layout.setSpacing(0)
|
|
36
|
+
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
37
|
+
|
|
38
|
+
# Add button with + symbol
|
|
39
|
+
self._add_button = QPushButton("➕")
|
|
40
|
+
self._add_button.setFixedSize(40, 40)
|
|
41
|
+
self._add_button.setStyleSheet("""
|
|
42
|
+
QPushButton {
|
|
43
|
+
border: 2px dashed #ccc;
|
|
44
|
+
border-radius: 20px;
|
|
45
|
+
background-color: #f9f9f9;
|
|
46
|
+
font-size: 20px;
|
|
47
|
+
color: #666;
|
|
48
|
+
}
|
|
49
|
+
QPushButton:hover {
|
|
50
|
+
border-color: #2196f3;
|
|
51
|
+
background-color: #e3f2fd;
|
|
52
|
+
color: #2196f3;
|
|
53
|
+
}
|
|
54
|
+
QPushButton:pressed {
|
|
55
|
+
background-color: #bbdefb;
|
|
56
|
+
}
|
|
57
|
+
""")
|
|
58
|
+
self._add_button.clicked.connect(self.addRequested.emit)
|
|
59
|
+
layout.addWidget(self._add_button)
|
|
60
|
+
|
|
61
|
+
self.setLayout(layout)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class QPSAMapMarker(QWidget):
|
|
65
|
+
"""Map marker component with left info panel and right map viewer."""
|
|
66
|
+
|
|
67
|
+
# Signals
|
|
68
|
+
onVariableChanged = pyqtSignal() # Emitted when any variable is changed
|
|
69
|
+
|
|
70
|
+
def __init__(self, parent=None):
|
|
71
|
+
super().__init__(parent)
|
|
72
|
+
self._variables: List[QPSACoVariable] = []
|
|
73
|
+
self._selected_cell_info: Optional[Dict[str, Any]] = None
|
|
74
|
+
self._init_ui()
|
|
75
|
+
|
|
76
|
+
def _init_ui(self):
|
|
77
|
+
"""Initialize UI components."""
|
|
78
|
+
# Main horizontal layout (left-right)
|
|
79
|
+
main_layout = QHBoxLayout()
|
|
80
|
+
main_layout.setContentsMargins(6, 6, 6, 6)
|
|
81
|
+
main_layout.setSpacing(8)
|
|
82
|
+
|
|
83
|
+
# Left panel (info栏)
|
|
84
|
+
left_panel = QWidget()
|
|
85
|
+
left_layout = QVBoxLayout(left_panel)
|
|
86
|
+
left_layout.setContentsMargins(0, 0, 0, 0)
|
|
87
|
+
left_layout.setSpacing(8)
|
|
88
|
+
|
|
89
|
+
# First row - selected cell info
|
|
90
|
+
self._cell_info = QPSACellInfo()
|
|
91
|
+
self._cell_info.setMinimumWidth(200)
|
|
92
|
+
# self._cell_info.setMaximumHeight(200)
|
|
93
|
+
left_layout.addWidget(self._cell_info)
|
|
94
|
+
|
|
95
|
+
# Second row - variables list
|
|
96
|
+
self._variables_list = QListWidget()
|
|
97
|
+
self._variables_list.setFrameStyle(QFrame.Shape.Box)
|
|
98
|
+
self._variables_list.setStyleSheet("""
|
|
99
|
+
QListWidget {
|
|
100
|
+
border: 1px solid #ccc;
|
|
101
|
+
border-radius: 4px;
|
|
102
|
+
background-color: white;
|
|
103
|
+
}
|
|
104
|
+
QListWidget::item {
|
|
105
|
+
border-bottom: 1px solid #eee;
|
|
106
|
+
}
|
|
107
|
+
QListWidget::item:selected {
|
|
108
|
+
background-color: #e3f2fd;
|
|
109
|
+
}
|
|
110
|
+
""")
|
|
111
|
+
self._variables_list.setSpacing(4)
|
|
112
|
+
self._variables_list.setMinimumWidth(200)
|
|
113
|
+
# self._variables_list.setMaximumHeight(200)
|
|
114
|
+
left_layout.addWidget(self._variables_list)
|
|
115
|
+
|
|
116
|
+
# Third row - control buttons
|
|
117
|
+
button_layout = QHBoxLayout()
|
|
118
|
+
button_layout.setContentsMargins(0, 0, 0, 0)
|
|
119
|
+
button_layout.setSpacing(8)
|
|
120
|
+
|
|
121
|
+
# Reset button
|
|
122
|
+
self._reset_button = QPushButton("Reset")
|
|
123
|
+
self._reset_button.setFixedHeight(30)
|
|
124
|
+
self._reset_button.setFixedWidth(80)
|
|
125
|
+
self._reset_button.setStyleSheet("""
|
|
126
|
+
QPushButton {
|
|
127
|
+
border: 1px solid #ccc;
|
|
128
|
+
border-radius: 4px;
|
|
129
|
+
background-color: #f44336;
|
|
130
|
+
color: white;
|
|
131
|
+
font-size: 12px;
|
|
132
|
+
font-weight: bold;
|
|
133
|
+
}
|
|
134
|
+
QPushButton:hover {
|
|
135
|
+
background-color: #d32f2f;
|
|
136
|
+
}
|
|
137
|
+
QPushButton:pressed {
|
|
138
|
+
background-color: #b71c1c;
|
|
139
|
+
}
|
|
140
|
+
""")
|
|
141
|
+
self._reset_button.clicked.connect(self._on_reset_clicked)
|
|
142
|
+
button_layout.addWidget(self._reset_button)
|
|
143
|
+
|
|
144
|
+
# Copy button
|
|
145
|
+
self._copy_button = QPushButton("Copy")
|
|
146
|
+
self._copy_button.setFixedHeight(30)
|
|
147
|
+
self._copy_button.setFixedWidth(80)
|
|
148
|
+
self._copy_button.setStyleSheet("""
|
|
149
|
+
QPushButton {
|
|
150
|
+
border: 1px solid #ccc;
|
|
151
|
+
border-radius: 4px;
|
|
152
|
+
background-color: #2196f3;
|
|
153
|
+
color: white;
|
|
154
|
+
font-size: 12px;
|
|
155
|
+
font-weight: bold;
|
|
156
|
+
}
|
|
157
|
+
QPushButton:hover {
|
|
158
|
+
background-color: #1976d2;
|
|
159
|
+
}
|
|
160
|
+
QPushButton:pressed {
|
|
161
|
+
background-color: #1565c0;
|
|
162
|
+
}
|
|
163
|
+
""")
|
|
164
|
+
self._copy_button.clicked.connect(self._on_copy_clicked)
|
|
165
|
+
button_layout.addWidget(self._copy_button)
|
|
166
|
+
|
|
167
|
+
button_layout.addStretch()
|
|
168
|
+
left_layout.addLayout(button_layout)
|
|
169
|
+
|
|
170
|
+
# Add initial add widget
|
|
171
|
+
self._add_add_widget()
|
|
172
|
+
|
|
173
|
+
main_layout.addWidget(left_panel, 1) # Left panel takes 1/4 space
|
|
174
|
+
|
|
175
|
+
# Right panel - map viewer
|
|
176
|
+
self._map_viewer = QPSAMapViewer()
|
|
177
|
+
main_layout.addWidget(self._map_viewer, 3) # Map viewer takes 3/4 space
|
|
178
|
+
|
|
179
|
+
self.setLayout(main_layout)
|
|
180
|
+
|
|
181
|
+
# Connect map viewer signals
|
|
182
|
+
self._map_viewer.selectChanged.connect(self._on_cell_selected)
|
|
183
|
+
|
|
184
|
+
def _add_add_widget(self):
|
|
185
|
+
"""Add the special add widget to the list."""
|
|
186
|
+
add_widget = AddVariableWidget()
|
|
187
|
+
add_widget.addRequested.connect(self._on_add_variable)
|
|
188
|
+
|
|
189
|
+
# Create list item
|
|
190
|
+
list_item = QListWidgetItem()
|
|
191
|
+
list_item.setSizeHint(add_widget.sizeHint())
|
|
192
|
+
|
|
193
|
+
self._variables_list.addItem(list_item)
|
|
194
|
+
self._variables_list.setItemWidget(list_item, add_widget)
|
|
195
|
+
|
|
196
|
+
def _add_variable_widget(self, variable: QPSACoVariable):
|
|
197
|
+
"""Add a variable widget to the list."""
|
|
198
|
+
# Create list item first
|
|
199
|
+
list_item = QListWidgetItem()
|
|
200
|
+
list_item.setSizeHint(variable.sizeHint())
|
|
201
|
+
|
|
202
|
+
# Connect variable signals
|
|
203
|
+
variable.onItemChanged.connect(self._on_variable_changed)
|
|
204
|
+
variable.removeRequested.connect(lambda: self._on_remove_variable(variable))
|
|
205
|
+
|
|
206
|
+
# Connect to dual button clicks to update size
|
|
207
|
+
variable._dual_button.clicked.connect(lambda: self._on_variable_dual_changed(variable, list_item))
|
|
208
|
+
|
|
209
|
+
self._variables_list.insertItem(self._variables_list.count() - 1, list_item)
|
|
210
|
+
self._variables_list.setItemWidget(list_item, variable)
|
|
211
|
+
|
|
212
|
+
self._variables.append(variable)
|
|
213
|
+
|
|
214
|
+
@pyqtSlot()
|
|
215
|
+
def _on_add_variable(self):
|
|
216
|
+
"""Handle add variable button click."""
|
|
217
|
+
print("[DEBUG] Adding new variable")
|
|
218
|
+
new_variable = QPSACoVariable()
|
|
219
|
+
new_variable.showRemoveButton = True # Set remove button to be visible
|
|
220
|
+
self._add_variable_widget(new_variable)
|
|
221
|
+
|
|
222
|
+
def _on_remove_variable(self, variable: QPSACoVariable):
|
|
223
|
+
"""Handle remove variable request."""
|
|
224
|
+
print("[DEBUG] Removing variable")
|
|
225
|
+
|
|
226
|
+
# Find the variable in the list
|
|
227
|
+
if variable in self._variables:
|
|
228
|
+
# Find the corresponding list item
|
|
229
|
+
for i in range(self._variables_list.count()):
|
|
230
|
+
item = self._variables_list.item(i)
|
|
231
|
+
if item:
|
|
232
|
+
widget = self._variables_list.itemWidget(item)
|
|
233
|
+
if widget == variable:
|
|
234
|
+
# Remove from list
|
|
235
|
+
self._variables_list.takeItem(i)
|
|
236
|
+
# Remove from variables list
|
|
237
|
+
self._variables.remove(variable)
|
|
238
|
+
# Delete the widget
|
|
239
|
+
variable.deleteLater()
|
|
240
|
+
print(f"[DEBUG] Variable removed at index {i}")
|
|
241
|
+
break
|
|
242
|
+
|
|
243
|
+
self.onVariableChanged.emit()
|
|
244
|
+
|
|
245
|
+
def _on_variable_dual_changed(self, variable: QPSACoVariable, list_item: QListWidgetItem):
|
|
246
|
+
"""Handle dual mode change for a variable."""
|
|
247
|
+
# Update the list item size hint when dual mode changes
|
|
248
|
+
new_size = variable.sizeHint()
|
|
249
|
+
list_item.setSizeHint(new_size)
|
|
250
|
+
print(f"[DEBUG] Dual mode changed, updated item size: {new_size}")
|
|
251
|
+
|
|
252
|
+
@pyqtSlot()
|
|
253
|
+
def _on_copy_clicked(self):
|
|
254
|
+
"""Handle copy button click to copy variables to clipboard as Python code."""
|
|
255
|
+
print("[DEBUG] Copying variables to clipboard as Python code")
|
|
256
|
+
|
|
257
|
+
# Get variables data
|
|
258
|
+
variables_data = self.variables
|
|
259
|
+
print(f"[DEBUG] Variables data: {variables_data}")
|
|
260
|
+
|
|
261
|
+
# Convert to Python code using json2code function
|
|
262
|
+
try:
|
|
263
|
+
code = json2code(variables_data)
|
|
264
|
+
print(f"[DEBUG] Generated code: {code}")
|
|
265
|
+
|
|
266
|
+
# Copy to clipboard
|
|
267
|
+
clipboard = QApplication.clipboard()
|
|
268
|
+
clipboard.setText(code)
|
|
269
|
+
print("[DEBUG] Python code copied to clipboard")
|
|
270
|
+
except Exception as e:
|
|
271
|
+
print(f"[DEBUG] Error generating code: {e}")
|
|
272
|
+
# Fallback to JSON if code generation fails
|
|
273
|
+
json_str = json.dumps(variables_data, ensure_ascii=False, indent=2)
|
|
274
|
+
clipboard = QApplication.clipboard()
|
|
275
|
+
clipboard.setText(json_str)
|
|
276
|
+
print("[DEBUG] JSON fallback copied to clipboard")
|
|
277
|
+
|
|
278
|
+
@pyqtSlot()
|
|
279
|
+
def _on_reset_clicked(self):
|
|
280
|
+
"""Handle reset button click."""
|
|
281
|
+
print("[DEBUG] Resetting info panel")
|
|
282
|
+
|
|
283
|
+
# Clear cell info
|
|
284
|
+
self._cell_info.data = None
|
|
285
|
+
self._selected_cell_info = None
|
|
286
|
+
|
|
287
|
+
# Clear all variables
|
|
288
|
+
for variable in self._variables:
|
|
289
|
+
variable.reset()
|
|
290
|
+
|
|
291
|
+
# Remove all variable widgets from list (keep only add widget)
|
|
292
|
+
while self._variables_list.count() > 1:
|
|
293
|
+
item = self._variables_list.takeItem(0)
|
|
294
|
+
if item:
|
|
295
|
+
widget = self._variables_list.itemWidget(item)
|
|
296
|
+
if widget and widget != self._variables_list.itemWidget(self._variables_list.item(self._variables_list.count() - 1)):
|
|
297
|
+
widget.deleteLater()
|
|
298
|
+
|
|
299
|
+
self._variables.clear()
|
|
300
|
+
|
|
301
|
+
@pyqtSlot(object)
|
|
302
|
+
def _on_cell_selected(self, cell_info):
|
|
303
|
+
"""Handle cell selection from map viewer."""
|
|
304
|
+
print(f"[DEBUG] Cell selected: {cell_info}")
|
|
305
|
+
if cell_info:
|
|
306
|
+
self._cell_info.data = cell_info
|
|
307
|
+
# Transfer screenshot to QPSACellInfo
|
|
308
|
+
self._cell_info.image = self._map_viewer.image
|
|
309
|
+
self._selected_cell_info = {
|
|
310
|
+
'x': cell_info.x,
|
|
311
|
+
'y': cell_info.y,
|
|
312
|
+
'terrain': cell_info.terrain,
|
|
313
|
+
'objects': cell_info.objects.copy() if cell_info.objects else []
|
|
314
|
+
}
|
|
315
|
+
else:
|
|
316
|
+
self._cell_info.data = None
|
|
317
|
+
self._selected_cell_info = None
|
|
318
|
+
|
|
319
|
+
@pyqtSlot()
|
|
320
|
+
def _on_variable_changed(self):
|
|
321
|
+
"""Handle variable changes."""
|
|
322
|
+
print("[DEBUG] Variable changed")
|
|
323
|
+
self.onVariableChanged.emit()
|
|
324
|
+
|
|
325
|
+
# Properties
|
|
326
|
+
@property
|
|
327
|
+
def variables(self) -> List[Dict[str, Any]]:
|
|
328
|
+
"""Get all variables as list of dictionaries."""
|
|
329
|
+
return [var.data for var in self._variables]
|
|
330
|
+
|
|
331
|
+
@property
|
|
332
|
+
def selectedCellInfo(self) -> Optional[Dict[str, Any]]:
|
|
333
|
+
"""Get selected cell information."""
|
|
334
|
+
return self._selected_cell_info.copy() if self._selected_cell_info else None
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def mapViewer(self) -> QPSAMapViewer:
|
|
338
|
+
"""Get the map viewer component."""
|
|
339
|
+
return self._map_viewer
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
QPSA Co Variable Component - 对称变量组件
|
|
4
|
+
"""
|
|
5
|
+
from typing import List, Dict, Any
|
|
6
|
+
from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
7
|
+
QLineEdit, QPushButton, QFrame, QSpacerItem,
|
|
8
|
+
QSizePolicy)
|
|
9
|
+
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot, QSize
|
|
10
|
+
from PyQt6.QtGui import QPalette, QColor
|
|
11
|
+
|
|
12
|
+
from pyscreeps_arena.ui.qmapv import QPSACellObject
|
|
13
|
+
|
|
14
|
+
# Import configuration from build.py
|
|
15
|
+
import sys
|
|
16
|
+
import os
|
|
17
|
+
# Add the project root directory to Python path
|
|
18
|
+
from pyscreeps_arena import config
|
|
19
|
+
|
|
20
|
+
# Language mapping
|
|
21
|
+
LANG = {
|
|
22
|
+
'cn': {
|
|
23
|
+
'symmetric': '对称',
|
|
24
|
+
},
|
|
25
|
+
'en': {
|
|
26
|
+
'symmetric': 'Symmetric',
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def lang(key: str) -> str:
|
|
31
|
+
"""Helper function to get translated text"""
|
|
32
|
+
return LANG[config.language if hasattr(config, 'language') and config.language in LANG else 'cn'][key]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class QPSACoVariable(QWidget):
|
|
36
|
+
"""Co-variable component with symmetric option."""
|
|
37
|
+
|
|
38
|
+
# Class variable to track instance count
|
|
39
|
+
_instance_count = 0
|
|
40
|
+
|
|
41
|
+
# Signals
|
|
42
|
+
onItemChanged = pyqtSignal() # Emitted when any item is added or removed
|
|
43
|
+
removeRequested = pyqtSignal() # Emitted when remove button is clicked
|
|
44
|
+
|
|
45
|
+
def __init__(self, parent=None):
|
|
46
|
+
super().__init__(parent)
|
|
47
|
+
self._dual = False
|
|
48
|
+
self._true_objects = []
|
|
49
|
+
self._false_objects = []
|
|
50
|
+
self._show_remove_btn = False # New property to control remove button
|
|
51
|
+
|
|
52
|
+
# Increment instance count and get the current number
|
|
53
|
+
QPSACoVariable._instance_count += 1
|
|
54
|
+
self._instance_number = QPSACoVariable._instance_count
|
|
55
|
+
|
|
56
|
+
self._init_ui()
|
|
57
|
+
|
|
58
|
+
def _init_ui(self):
|
|
59
|
+
"""Initialize UI components."""
|
|
60
|
+
# Main vertical layout
|
|
61
|
+
main_layout = QVBoxLayout()
|
|
62
|
+
main_layout.setContentsMargins(4, 4, 4, 4) # Reduced margins
|
|
63
|
+
main_layout.setSpacing(3) # Reduced spacing
|
|
64
|
+
|
|
65
|
+
# First row - input, checkbox, and remove button in 7:3:2 ratio
|
|
66
|
+
top_row = QHBoxLayout()
|
|
67
|
+
top_row.setContentsMargins(0, 0, 0, 0)
|
|
68
|
+
top_row.setSpacing(2) # Reduced spacing
|
|
69
|
+
|
|
70
|
+
# Input box with placeholder (70%)
|
|
71
|
+
self._input = QLineEdit()
|
|
72
|
+
self._input.setPlaceholderText(f"VAR{self._instance_number}")
|
|
73
|
+
self._input.setStyleSheet("font-size: 11px;") # Reduced font size
|
|
74
|
+
self._input.setFixedHeight(24) # Reduced height
|
|
75
|
+
top_row.addWidget(self._input, 7)
|
|
76
|
+
|
|
77
|
+
# Button-style checkbox for dual mode (30%)
|
|
78
|
+
self._dual_button = QPushButton(lang('symmetric'))
|
|
79
|
+
self._dual_button.setCheckable(True)
|
|
80
|
+
self._dual_button.setFixedHeight(24) # Reduced from 22 to 18
|
|
81
|
+
self._dual_button.setStyleSheet("""
|
|
82
|
+
QPushButton {
|
|
83
|
+
border: 1px solid #ccc;
|
|
84
|
+
border-radius: 2px;
|
|
85
|
+
background-color: #f0f0f0;
|
|
86
|
+
font-size: 9px;
|
|
87
|
+
padding: 1px;
|
|
88
|
+
}
|
|
89
|
+
QPushButton:checked {
|
|
90
|
+
background-color: #e3f2fd;
|
|
91
|
+
border-color: #2196f3;
|
|
92
|
+
}
|
|
93
|
+
""")
|
|
94
|
+
self._dual_button.clicked.connect(self._on_dual_changed)
|
|
95
|
+
top_row.addWidget(self._dual_button, 3)
|
|
96
|
+
|
|
97
|
+
# Remove button (20% - shown based on property)
|
|
98
|
+
self._remove_btn = QPushButton("⛔")
|
|
99
|
+
self._remove_btn.setFixedHeight(24)
|
|
100
|
+
self._remove_btn.setFixedWidth(24) # Make it square
|
|
101
|
+
self._remove_btn.setStyleSheet("""
|
|
102
|
+
QPushButton {
|
|
103
|
+
border: 1px solid #ff6b6b;
|
|
104
|
+
border-radius: 12px;
|
|
105
|
+
background-color: #ffe6e6;
|
|
106
|
+
color: #ff6b6b;
|
|
107
|
+
font-size: 10px;
|
|
108
|
+
font-weight: bold;
|
|
109
|
+
}
|
|
110
|
+
QPushButton:hover {
|
|
111
|
+
background-color: #ff6b6b;
|
|
112
|
+
color: white;
|
|
113
|
+
}
|
|
114
|
+
QPushButton:pressed {
|
|
115
|
+
background-color: #ff5252;
|
|
116
|
+
}
|
|
117
|
+
""")
|
|
118
|
+
self._remove_btn.clicked.connect(self._on_remove_clicked)
|
|
119
|
+
self._remove_btn.setVisible(self._show_remove_btn)
|
|
120
|
+
top_row.addWidget(self._remove_btn, 2)
|
|
121
|
+
|
|
122
|
+
main_layout.addLayout(top_row)
|
|
123
|
+
|
|
124
|
+
# Second row - QPSACellObject components
|
|
125
|
+
objects_widget = QWidget()
|
|
126
|
+
self._objects_layout = QVBoxLayout(objects_widget)
|
|
127
|
+
self._objects_layout.setContentsMargins(0, 0, 0, 0)
|
|
128
|
+
self._objects_layout.setSpacing(2) # Reduced spacing
|
|
129
|
+
|
|
130
|
+
# True objects section
|
|
131
|
+
self._true_objects_widget = QPSACellObject()
|
|
132
|
+
self._true_objects_widget.objectAdded.connect(self._on_item_changed)
|
|
133
|
+
self._true_objects_widget.objectRemoved.connect(self._on_item_changed)
|
|
134
|
+
self._true_objects_widget.itemChanged.connect(self._on_item_changed)
|
|
135
|
+
self._objects_layout.addWidget(self._true_objects_widget)
|
|
136
|
+
|
|
137
|
+
# Separator for dual mode
|
|
138
|
+
self._mid_separator = QFrame()
|
|
139
|
+
self._mid_separator.setFrameShape(QFrame.Shape.HLine)
|
|
140
|
+
self._mid_separator.setFrameShadow(QFrame.Shadow.Sunken)
|
|
141
|
+
self._mid_separator.setVisible(False)
|
|
142
|
+
self._objects_layout.addWidget(self._mid_separator)
|
|
143
|
+
|
|
144
|
+
# False objects section (for dual mode)
|
|
145
|
+
self._false_objects_widget = QPSACellObject()
|
|
146
|
+
self._false_objects_widget.objectAdded.connect(self._on_item_changed)
|
|
147
|
+
self._false_objects_widget.objectRemoved.connect(self._on_item_changed)
|
|
148
|
+
self._false_objects_widget.itemChanged.connect(self._on_item_changed)
|
|
149
|
+
# Set background color to #FFF0F5 (light red) for the list content
|
|
150
|
+
self._false_objects_widget.set_list_background_color("#FFF0F5")
|
|
151
|
+
self._false_objects_widget.setVisible(False)
|
|
152
|
+
self._objects_layout.addWidget(self._false_objects_widget)
|
|
153
|
+
|
|
154
|
+
main_layout.addWidget(objects_widget)
|
|
155
|
+
|
|
156
|
+
self.setLayout(main_layout)
|
|
157
|
+
self.setMinimumWidth(182)
|
|
158
|
+
self.setMaximumWidth(182)
|
|
159
|
+
|
|
160
|
+
@pyqtSlot(bool)
|
|
161
|
+
def _on_dual_changed(self, checked: bool):
|
|
162
|
+
"""Handle dual mode toggle."""
|
|
163
|
+
self._dual = checked
|
|
164
|
+
self._false_objects_widget.setVisible(checked)
|
|
165
|
+
self._mid_separator.setVisible(checked)
|
|
166
|
+
self._on_item_changed()
|
|
167
|
+
|
|
168
|
+
@pyqtSlot()
|
|
169
|
+
def _on_remove_clicked(self):
|
|
170
|
+
"""Handle remove button click."""
|
|
171
|
+
self.removeRequested.emit() # Request removal from parent
|
|
172
|
+
|
|
173
|
+
@pyqtSlot()
|
|
174
|
+
def _on_item_changed(self):
|
|
175
|
+
"""Handle item changes in QPSACellObject components."""
|
|
176
|
+
# Update internal lists
|
|
177
|
+
self._true_objects = self._true_objects_widget.objects
|
|
178
|
+
if self._dual:
|
|
179
|
+
self._false_objects = self._false_objects_widget.objects
|
|
180
|
+
else:
|
|
181
|
+
self._false_objects = []
|
|
182
|
+
|
|
183
|
+
# Emit callback
|
|
184
|
+
self.onItemChanged.emit()
|
|
185
|
+
|
|
186
|
+
# Properties
|
|
187
|
+
@property
|
|
188
|
+
def dual(self) -> bool:
|
|
189
|
+
"""Get/set dual mode status."""
|
|
190
|
+
return self._dual
|
|
191
|
+
|
|
192
|
+
@dual.setter
|
|
193
|
+
def dual(self, value: bool):
|
|
194
|
+
"""Set dual mode status."""
|
|
195
|
+
self._dual = value
|
|
196
|
+
self._dual_button.setChecked(value)
|
|
197
|
+
self._false_objects_widget.setVisible(value)
|
|
198
|
+
self._mid_separator.setVisible(value)
|
|
199
|
+
self._on_item_changed()
|
|
200
|
+
|
|
201
|
+
# Recalculate height by updating size hint and notifying parent
|
|
202
|
+
self.updateGeometry()
|
|
203
|
+
if self.parent():
|
|
204
|
+
# Notify parent layout to recalculate
|
|
205
|
+
self.parent().updateGeometry()
|
|
206
|
+
|
|
207
|
+
# If this is in a QListWidget, update the item size
|
|
208
|
+
if hasattr(self.parent(), 'parent') and self.parent().parent():
|
|
209
|
+
list_widget = self.parent().parent()
|
|
210
|
+
if hasattr(list_widget, 'itemWidget'):
|
|
211
|
+
# Find the corresponding item and update its size hint
|
|
212
|
+
for i in range(list_widget.count()):
|
|
213
|
+
item = list_widget.item(i)
|
|
214
|
+
if item and list_widget.itemWidget(item) == self:
|
|
215
|
+
item.setSizeHint(self.sizeHint())
|
|
216
|
+
print(f"[DEBUG] Updated item size hint for dual mode: {self.sizeHint()}")
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def trueObjects(self) -> List[Dict[str, Any]]:
|
|
221
|
+
"""Get true objects list."""
|
|
222
|
+
return self._true_objects.copy()
|
|
223
|
+
|
|
224
|
+
@trueObjects.setter
|
|
225
|
+
def trueObjects(self, value: List[Dict[str, Any]]):
|
|
226
|
+
"""Set true objects list."""
|
|
227
|
+
# Clear current objects
|
|
228
|
+
self._true_objects_widget.clear_objects()
|
|
229
|
+
# Add new objects
|
|
230
|
+
for obj in value:
|
|
231
|
+
self._true_objects_widget.add_object(obj)
|
|
232
|
+
self._on_item_changed()
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def falseObjects(self) -> List[Dict[str, Any]]:
|
|
236
|
+
"""Get false objects list."""
|
|
237
|
+
return self._false_objects.copy()
|
|
238
|
+
|
|
239
|
+
@falseObjects.setter
|
|
240
|
+
def falseObjects(self, value: List[Dict[str, Any]]):
|
|
241
|
+
"""Set false objects list."""
|
|
242
|
+
# Clear current objects
|
|
243
|
+
self._false_objects_widget.clear_objects()
|
|
244
|
+
# Add new objects
|
|
245
|
+
for obj in value:
|
|
246
|
+
self._false_objects_widget.add_object(obj)
|
|
247
|
+
self._on_item_changed()
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def showRemoveButton(self) -> bool:
|
|
251
|
+
"""Get/set whether to show the remove button."""
|
|
252
|
+
return self._show_remove_btn
|
|
253
|
+
|
|
254
|
+
@showRemoveButton.setter
|
|
255
|
+
def showRemoveButton(self, value: bool):
|
|
256
|
+
"""Set whether to show the remove button."""
|
|
257
|
+
self._show_remove_btn = value
|
|
258
|
+
self._remove_btn.setVisible(value)
|
|
259
|
+
|
|
260
|
+
def reset(self):
|
|
261
|
+
"""Reset component to default state."""
|
|
262
|
+
self._input.clear()
|
|
263
|
+
self.dual = False
|
|
264
|
+
self._true_objects_widget.clear_objects()
|
|
265
|
+
self._false_objects_widget.clear_objects()
|
|
266
|
+
|
|
267
|
+
def sizeHint(self):
|
|
268
|
+
"""Calculate preferred size based on dual mode."""
|
|
269
|
+
# Base height for single mode
|
|
270
|
+
base_height = 120
|
|
271
|
+
|
|
272
|
+
# Add height for dual mode (additional objects widget)
|
|
273
|
+
if self._dual:
|
|
274
|
+
base_height += 80 # Additional space for false objects
|
|
275
|
+
|
|
276
|
+
# Add height based on content
|
|
277
|
+
true_count = len(self._true_objects_widget.objects)
|
|
278
|
+
false_count = len(self._false_objects_widget.objects) if self._dual else 0
|
|
279
|
+
|
|
280
|
+
# Estimate height based on object count (roughly 25px per object)
|
|
281
|
+
content_height = max(true_count, false_count) * 25
|
|
282
|
+
total_height = base_height + content_height
|
|
283
|
+
|
|
284
|
+
# Keep width fixed
|
|
285
|
+
return QSize(182, min(total_height, 400)) # Cap at 400px max height
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def data(self) -> Dict[str, Any]:
|
|
289
|
+
"""Get component data as a dictionary."""
|
|
290
|
+
# Use placeholder text as key if input is empty
|
|
291
|
+
key = self._input.text() if self._input.text() else self._input.placeholderText()
|
|
292
|
+
return {
|
|
293
|
+
'key': key,
|
|
294
|
+
'dual': self._dual,
|
|
295
|
+
'pos': self._true_objects.copy(),
|
|
296
|
+
'neg': self._false_objects.copy()
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
# Static method to get current instance count
|
|
300
|
+
@staticmethod
|
|
301
|
+
def get_instance_count() -> int:
|
|
302
|
+
"""Get current instance count."""
|
|
303
|
+
return QPSACoVariable._instance_count
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Test script for compact QPSACoVariable component
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
|
9
|
+
|
|
10
|
+
from PyQt6.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton, QLabel
|
|
11
|
+
from PyQt6.QtCore import Qt
|
|
12
|
+
from pyscreeps_arena.ui.qmapker.qvariable import QPSACoVariable
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_compact_variable():
|
|
16
|
+
"""Test the compact QPSACoVariable component."""
|
|
17
|
+
app = QApplication(sys.argv)
|
|
18
|
+
|
|
19
|
+
# Create main window
|
|
20
|
+
window = QWidget()
|
|
21
|
+
window.setWindowTitle("Compact QPSACoVariable Test")
|
|
22
|
+
window.resize(400, 300)
|
|
23
|
+
|
|
24
|
+
# Create layout
|
|
25
|
+
layout = QVBoxLayout()
|
|
26
|
+
layout.setContentsMargins(10, 10, 10, 10)
|
|
27
|
+
layout.setSpacing(5)
|
|
28
|
+
|
|
29
|
+
# Add title
|
|
30
|
+
title = QLabel("Compact QPSACoVariable Components")
|
|
31
|
+
title.setStyleSheet("font-weight: bold; font-size: 14px;")
|
|
32
|
+
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
33
|
+
layout.addWidget(title)
|
|
34
|
+
|
|
35
|
+
# Create multiple compact QPSACoVariable components
|
|
36
|
+
for i in range(3):
|
|
37
|
+
variable = QPSACoVariable()
|
|
38
|
+
variable._input.setText(f"Test{i+1}")
|
|
39
|
+
layout.addWidget(variable)
|
|
40
|
+
|
|
41
|
+
# Add button to test dual mode
|
|
42
|
+
def toggle_dual():
|
|
43
|
+
for widget in window.findChildren(QPSACoVariable):
|
|
44
|
+
widget.dual = not widget.dual
|
|
45
|
+
|
|
46
|
+
test_button = QPushButton("Toggle Dual Mode")
|
|
47
|
+
test_button.clicked.connect(toggle_dual)
|
|
48
|
+
layout.addWidget(test_button)
|
|
49
|
+
|
|
50
|
+
# Add stretch to push everything up
|
|
51
|
+
layout.addStretch()
|
|
52
|
+
|
|
53
|
+
window.setLayout(layout)
|
|
54
|
+
window.show()
|
|
55
|
+
|
|
56
|
+
print("[DEBUG] Compact QPSACoVariable test window initialized")
|
|
57
|
+
sys.exit(app.exec())
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
test_compact_variable()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Test script for QPSAMapMarker component
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
# Add the parent directory to the path
|
|
10
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
|
|
11
|
+
|
|
12
|
+
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QLabel
|
|
13
|
+
from PyQt6.QtCore import Qt
|
|
14
|
+
from pyscreeps_arena.ui.qmapker.qmapmarker import QPSAMapMarker
|
|
15
|
+
from pyscreeps_arena.core import config
|
|
16
|
+
config.language = 'en'
|
|
17
|
+
|
|
18
|
+
class TestWindow(QMainWindow):
|
|
19
|
+
def __init__(self):
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.setWindowTitle("QPSAMapMarker Test")
|
|
22
|
+
self.setGeometry(100, 100, 1200, 800)
|
|
23
|
+
|
|
24
|
+
# Central widget
|
|
25
|
+
central_widget = QWidget()
|
|
26
|
+
self.setCentralWidget(central_widget)
|
|
27
|
+
|
|
28
|
+
# Main layout
|
|
29
|
+
layout = QVBoxLayout()
|
|
30
|
+
|
|
31
|
+
# Title
|
|
32
|
+
title_label = QLabel("QPSAMapMarker Component Test")
|
|
33
|
+
title_label.setStyleSheet("font-size: 18px; font-weight: bold; padding: 10px;")
|
|
34
|
+
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
35
|
+
layout.addWidget(title_label)
|
|
36
|
+
|
|
37
|
+
# Create map marker component
|
|
38
|
+
self.map_marker = QPSAMapMarker()
|
|
39
|
+
layout.addWidget(self.map_marker)
|
|
40
|
+
|
|
41
|
+
# Status label
|
|
42
|
+
self.status_label = QLabel("Select a cell on the map to see its info")
|
|
43
|
+
self.status_label.setStyleSheet("font-size: 12px; color: #666; padding: 10px;")
|
|
44
|
+
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
45
|
+
layout.addWidget(self.status_label)
|
|
46
|
+
|
|
47
|
+
# Connect signals
|
|
48
|
+
self.map_marker.onVariableChanged.connect(self.on_variable_changed)
|
|
49
|
+
|
|
50
|
+
central_widget.setLayout(layout)
|
|
51
|
+
|
|
52
|
+
print("[DEBUG] QPSAMapMarker test window initialized")
|
|
53
|
+
|
|
54
|
+
def on_variable_changed(self):
|
|
55
|
+
"""Handle variable changes."""
|
|
56
|
+
variables = self.map_marker.variables
|
|
57
|
+
self.status_label.setText(f"Variables updated: {len(variables)} variables")
|
|
58
|
+
print(f"[DEBUG] Variables changed: {len(variables)} variables")
|
|
59
|
+
|
|
60
|
+
# Print variable details
|
|
61
|
+
for i, variable in enumerate(variables):
|
|
62
|
+
print(f" Variable {i+1}: {len(variable['pos'])} true objects, {len(variable['neg'])} false objects")
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
app = QApplication(sys.argv)
|
|
66
|
+
window = TestWindow()
|
|
67
|
+
window.show()
|
|
68
|
+
sys.exit(app.exec())
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Test script for QPSACoVariable component
|
|
4
|
+
"""
|
|
5
|
+
import sys
|
|
6
|
+
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
|
|
7
|
+
from pyscreeps_arena.ui.qmapker.qvariable import QPSACoVariable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestWindow(QMainWindow):
|
|
11
|
+
"""Test window to display QPSACoVariable component."""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self._init_ui()
|
|
16
|
+
|
|
17
|
+
def _init_ui(self):
|
|
18
|
+
"""Initialize UI."""
|
|
19
|
+
self.setWindowTitle("QPSACoVariable Test")
|
|
20
|
+
self.setGeometry(100, 100, 800, 400)
|
|
21
|
+
|
|
22
|
+
# Central widget
|
|
23
|
+
central_widget = QWidget()
|
|
24
|
+
self.setCentralWidget(central_widget)
|
|
25
|
+
|
|
26
|
+
# Main layout
|
|
27
|
+
main_layout = QVBoxLayout(central_widget)
|
|
28
|
+
|
|
29
|
+
# Add multiple QPSACoVariable components to test instance counting
|
|
30
|
+
for i in range(3):
|
|
31
|
+
co_var = QPSACoVariable()
|
|
32
|
+
co_var.onItemChanged.connect(self._on_item_changed)
|
|
33
|
+
main_layout.addWidget(co_var)
|
|
34
|
+
|
|
35
|
+
def _on_item_changed(self):
|
|
36
|
+
"""Handle item changed signal."""
|
|
37
|
+
print("Item changed!")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main():
|
|
41
|
+
"""Main function."""
|
|
42
|
+
app = QApplication(sys.argv)
|
|
43
|
+
window = TestWindow()
|
|
44
|
+
window.show()
|
|
45
|
+
sys.exit(app.exec())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == '__main__':
|
|
49
|
+
main()
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def json2code(data: list) -> str:
|
|
7
|
+
"""
|
|
8
|
+
Convert data to Python code according to the specified rules.
|
|
9
|
+
Args:
|
|
10
|
+
data: List of variable dictionaries to convert
|
|
11
|
+
Returns: Generated Python code as a string
|
|
12
|
+
"""
|
|
13
|
+
# Ensure the data is a list
|
|
14
|
+
if not isinstance(data, list):
|
|
15
|
+
raise ValueError("Data must be a list")
|
|
16
|
+
|
|
17
|
+
generated_code = []
|
|
18
|
+
|
|
19
|
+
for element in data:
|
|
20
|
+
key = element.get('key', '')
|
|
21
|
+
dual = element.get('dual', False)
|
|
22
|
+
pos = element.get('pos', [])
|
|
23
|
+
neg = element.get('neg', [])
|
|
24
|
+
|
|
25
|
+
# Check if pos is empty
|
|
26
|
+
if not pos:
|
|
27
|
+
print(f"Warning: pos is empty for key '{key}', skipping this element.")
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
# Check if neg is empty when dual is enabled
|
|
31
|
+
if dual and not neg:
|
|
32
|
+
print(f"Warning: neg is empty for key '{key}' with dual=True, skipping this element.")
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
# Step 2: Generate pk and epk if dual is enabled
|
|
36
|
+
pk = key
|
|
37
|
+
epk = None
|
|
38
|
+
if dual:
|
|
39
|
+
if pk.startswith('E'):
|
|
40
|
+
epk = f'E_{pk}'
|
|
41
|
+
else:
|
|
42
|
+
epk = f'E{pk}'
|
|
43
|
+
|
|
44
|
+
# Step 3: Process pos list
|
|
45
|
+
pos_translations = []
|
|
46
|
+
for item in pos:
|
|
47
|
+
method = item.get('method', '')
|
|
48
|
+
x = item.get('x', 0)
|
|
49
|
+
y = item.get('y', 0)
|
|
50
|
+
pos_translations.append(f"{method}({x}, {y})")
|
|
51
|
+
|
|
52
|
+
# Format posv based on length
|
|
53
|
+
if len(pos_translations) > 1:
|
|
54
|
+
posv = f"[{', '.join(pos_translations)}]"
|
|
55
|
+
else:
|
|
56
|
+
posv = pos_translations[0]
|
|
57
|
+
|
|
58
|
+
# Step 4: Process neg list if dual is enabled
|
|
59
|
+
negv = None
|
|
60
|
+
if dual:
|
|
61
|
+
neg_translations = []
|
|
62
|
+
for item in neg:
|
|
63
|
+
method = item.get('method', '')
|
|
64
|
+
x = item.get('x', 0)
|
|
65
|
+
y = item.get('y', 0)
|
|
66
|
+
neg_translations.append(f"{method}({x}, {y})")
|
|
67
|
+
|
|
68
|
+
# Format negv based on length
|
|
69
|
+
if len(neg_translations) > 1:
|
|
70
|
+
negv = f"[{', '.join(neg_translations)}]"
|
|
71
|
+
else:
|
|
72
|
+
negv = neg_translations[0]
|
|
73
|
+
|
|
74
|
+
# Step 6: Generate code lines for this element
|
|
75
|
+
element_code = []
|
|
76
|
+
|
|
77
|
+
# Base line for pos
|
|
78
|
+
element_code.append(f"{pk} = {posv}")
|
|
79
|
+
|
|
80
|
+
# Additional lines if dual is enabled
|
|
81
|
+
if dual and epk and negv:
|
|
82
|
+
element_code.append(f"{epk} = {negv}")
|
|
83
|
+
element_code.append(f"if SIDE == 1: {pk}, {epk} = {epk}, {pk}")
|
|
84
|
+
|
|
85
|
+
element_code.append('') # empty
|
|
86
|
+
|
|
87
|
+
# Add to generated code
|
|
88
|
+
generated_code.extend(element_code)
|
|
89
|
+
|
|
90
|
+
# Step 7: Join all lines and return
|
|
91
|
+
return '\n'.join(generated_code)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# Test the function
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
# Read the test.json file for testing
|
|
97
|
+
with open('test.json', 'r', encoding='utf-8') as f:
|
|
98
|
+
data = json.load(f)
|
|
99
|
+
code = json2code(data)
|
|
100
|
+
print(code)
|
|
@@ -27,6 +27,13 @@ pyscreeps_arena/ui/rs_icon.py
|
|
|
27
27
|
pyscreeps_arena/ui/qcreeplogic/__init__.py
|
|
28
28
|
pyscreeps_arena/ui/qcreeplogic/model.py
|
|
29
29
|
pyscreeps_arena/ui/qcreeplogic/qcreeplogic.py
|
|
30
|
+
pyscreeps_arena/ui/qmapker/__init__.py
|
|
31
|
+
pyscreeps_arena/ui/qmapker/qmapmarker.py
|
|
32
|
+
pyscreeps_arena/ui/qmapker/qvariable.py
|
|
33
|
+
pyscreeps_arena/ui/qmapker/test_compact_variable.py
|
|
34
|
+
pyscreeps_arena/ui/qmapker/test_qmapmarker.py
|
|
35
|
+
pyscreeps_arena/ui/qmapker/test_qvariable.py
|
|
36
|
+
pyscreeps_arena/ui/qmapker/to_code.py
|
|
30
37
|
pyscreeps_arena/ui/qmapv/__init__.py
|
|
31
38
|
pyscreeps_arena/ui/qmapv/qcinfo.py
|
|
32
39
|
pyscreeps_arena/ui/qmapv/qco.py
|
|
@@ -7,7 +7,7 @@ with open(r"T:\New_PC\Import_Project\uploads\pyscreeps-arena_upload\pyscreeps-ar
|
|
|
7
7
|
long_description = f.read()
|
|
8
8
|
setup(
|
|
9
9
|
name='pyscreeps-arena',
|
|
10
|
-
version='0.5.7.
|
|
10
|
+
version='0.5.7.4',
|
|
11
11
|
description='Python api|interface to play game: Screeps: Arena.',
|
|
12
12
|
long_description=long_description,
|
|
13
13
|
long_description_content_type='text/markdown',
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qcreeplogic/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qcreeplogic/qcreeplogic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_array_drag.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_qco_drag.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena/ui/qmapv/test_simple_array.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{pyscreeps_arena-0.5.7.2 → pyscreeps_arena-0.5.7.4}/pyscreeps_arena.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|