pyscreeps-arena 0.5.4a0__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.
- pyscreeps_arena/__init__.py +9 -3
- pyscreeps_arena/compiler.py +202 -25
- pyscreeps_arena/core/const.py +1 -1
- pyscreeps_arena/localization.py +4 -0
- pyscreeps_arena/project.7z +0 -0
- pyscreeps_arena/ui/P2PY.py +47 -44
- pyscreeps_arena/ui/creeplogic_edit.py +14 -0
- pyscreeps_arena/ui/project_ui.py +5 -3
- pyscreeps_arena/ui/qcreeplogic/__init__.py +3 -0
- pyscreeps_arena/ui/qcreeplogic/model.py +72 -0
- pyscreeps_arena/ui/qcreeplogic/qcreeplogic.py +709 -0
- pyscreeps_arena/ui/qrecipe/__init__.py +1 -0
- pyscreeps_arena/ui/qrecipe/model.py +434 -0
- pyscreeps_arena/ui/qrecipe/qrecipe.py +914 -0
- pyscreeps_arena/ui/rs_icon.py +24 -9
- {pyscreeps_arena-0.5.4a0.dist-info → pyscreeps_arena-0.5.7b0.dist-info}/METADATA +1 -1
- pyscreeps_arena-0.5.7b0.dist-info/RECORD +28 -0
- pyscreeps_arena-0.5.4a0.dist-info/RECORD +0 -21
- {pyscreeps_arena-0.5.4a0.dist-info → pyscreeps_arena-0.5.7b0.dist-info}/WHEEL +0 -0
- {pyscreeps_arena-0.5.4a0.dist-info → pyscreeps_arena-0.5.7b0.dist-info}/entry_points.txt +0 -0
- {pyscreeps_arena-0.5.4a0.dist-info → pyscreeps_arena-0.5.7b0.dist-info}/top_level.txt +0 -0
pyscreeps_arena/__init__.py
CHANGED
|
@@ -35,8 +35,13 @@ def CMD_OpenUI():
|
|
|
35
35
|
|
|
36
36
|
"""
|
|
37
37
|
try:
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
# 检查是否带 -c
|
|
39
|
+
if len(sys.argv) > 1 and sys.argv[1] == '-c':
|
|
40
|
+
from pyscreeps_arena.ui.creeplogic_edit import run_creeplogic_edit
|
|
41
|
+
run_creeplogic_edit()
|
|
42
|
+
else:
|
|
43
|
+
from pyscreeps_arena.ui.project_ui import run_project_creator
|
|
44
|
+
run_project_creator()
|
|
40
45
|
except ImportError as e:
|
|
41
46
|
print(f"错误: 无法导入UI模块 - {e}")
|
|
42
47
|
print("请确保已安装PyQt6: pip install PyQt6")
|
|
@@ -47,4 +52,5 @@ def extract_7z(file_path, output_dir):
|
|
|
47
52
|
with py7zr.SevenZipFile(file_path, mode='r') as archive:
|
|
48
53
|
archive.extractall(path=output_dir)
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
if __name__ == '__main__':
|
|
56
|
+
CMD_OpenUI()
|
pyscreeps_arena/compiler.py
CHANGED
|
@@ -9,6 +9,7 @@ import chardet
|
|
|
9
9
|
import subprocess
|
|
10
10
|
import pyperclip
|
|
11
11
|
from colorama import Fore
|
|
12
|
+
from typing import List, Optional, Tuple, Union
|
|
12
13
|
|
|
13
14
|
WAIT = Fore.YELLOW + ">>>" + Fore.RESET
|
|
14
15
|
GREEN = Fore.GREEN + "{}" + Fore.RESET
|
|
@@ -92,24 +93,25 @@ export var loop = function () {
|
|
|
92
93
|
timeLine = get.cpu_us();
|
|
93
94
|
know.handle();
|
|
94
95
|
knowCost = get.cpu_us() - timeLine;
|
|
95
|
-
|
|
96
|
-
std.show_welcome();
|
|
97
|
-
init (know);
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
96
|
+
|
|
101
97
|
timeLine = get.cpu_us();
|
|
102
98
|
monitor.handle();
|
|
103
99
|
monitorCost = get.cpu_us() - timeLine;
|
|
104
|
-
for (const creep of
|
|
100
|
+
for (const creep of know.creeps){
|
|
105
101
|
creep.handle();
|
|
106
102
|
}
|
|
103
|
+
if (know.now === 1) {
|
|
104
|
+
std.show_welcome();
|
|
105
|
+
init (know);
|
|
106
|
+
|
|
107
|
+
}
|
|
107
108
|
step (know);
|
|
108
109
|
timeLine = get.cpu_us();
|
|
109
110
|
if (get._SCH_FLAG) sch.handle();
|
|
110
111
|
stepCost = get.cpu_us() - timeLine;
|
|
111
112
|
std.show_usage ();
|
|
112
113
|
print("knowCost:", knowCost, "monitorCost:", monitorCost, "stepCost:", stepCost);
|
|
114
|
+
if (know.draw) know.draw();
|
|
113
115
|
};
|
|
114
116
|
"""
|
|
115
117
|
|
|
@@ -147,7 +149,7 @@ export var loop = function () {
|
|
|
147
149
|
|
|
148
150
|
ARENA_IMPORTS_GETTER = {
|
|
149
151
|
const.ARENA_GREEN: lambda: f"""
|
|
150
|
-
const ARENA_COLOR_TYPE = "
|
|
152
|
+
const ARENA_COLOR_TYPE = "GREEN";
|
|
151
153
|
class GameAreaEffect{{
|
|
152
154
|
constructor(){{
|
|
153
155
|
}}
|
|
@@ -199,11 +201,6 @@ class GameConstructionBoost{{
|
|
|
199
201
|
constructor(){{
|
|
200
202
|
}}
|
|
201
203
|
}};
|
|
202
|
-
let GameFlag = GameStructureSpawn;
|
|
203
|
-
import * as GAME_PROTO_MODULE from 'game/prototypes';
|
|
204
|
-
if (GAME_PROTO_MODULE.Flag){{
|
|
205
|
-
GameFlag = GAME_PROTO_MODULE.Flag;
|
|
206
|
-
}}
|
|
207
204
|
""",
|
|
208
205
|
}
|
|
209
206
|
|
|
@@ -233,12 +230,16 @@ class Compiler_Utils(Compiler_Const):
|
|
|
233
230
|
return f.read()
|
|
234
231
|
except UnicodeDecodeError:
|
|
235
232
|
# 如果使用检测到的编码读取失败,尝试使用chardet检测编码
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
233
|
+
try:
|
|
234
|
+
with open(fpath, 'rb') as f: # 以二进制模式打开文件
|
|
235
|
+
raw_data = f.read() # 读取文件的原始数据
|
|
236
|
+
result = chardet.detect(raw_data) # 使用chardet检测编码
|
|
237
|
+
encoding = result['encoding'] # 获取检测到的编码
|
|
238
|
+
with open(fpath, 'r', encoding=encoding) as f: # 使用检测到的编码打开文件
|
|
239
|
+
return f.read()
|
|
240
|
+
except UnicodeDecodeError as e:
|
|
241
|
+
core.error('Compiler_Utils.auto_read', core.lformat(LOC_FILE_READ_FAILED, [fpath, "UnicodeError", e]), end='', head='\n', ln=config.language)
|
|
242
|
+
quit(-1)
|
|
242
243
|
|
|
243
244
|
def copy_to(self) -> list:
|
|
244
245
|
"""
|
|
@@ -689,6 +690,167 @@ class Compiler_Utils(Compiler_Const):
|
|
|
689
690
|
code = re.sub(r"'''[^']*'''", '', code)
|
|
690
691
|
return code
|
|
691
692
|
|
|
693
|
+
@classmethod
|
|
694
|
+
def _collect_logical_line(cls, lines: List[str], start_idx: int) -> Tuple[str, int]:
|
|
695
|
+
"""收集从start_idx开始的逻辑行(处理多行语句,直到遇到:结尾)"""
|
|
696
|
+
if start_idx >= len(lines):
|
|
697
|
+
return "", start_idx
|
|
698
|
+
|
|
699
|
+
parts = [lines[start_idx].rstrip()]
|
|
700
|
+
i = start_idx
|
|
701
|
+
|
|
702
|
+
# 持续收集直到找到以:结尾的行
|
|
703
|
+
while i < len(lines) and not parts[-1].endswith(':'):
|
|
704
|
+
i += 1
|
|
705
|
+
if i < len(lines):
|
|
706
|
+
parts.append(lines[i].rstrip())
|
|
707
|
+
|
|
708
|
+
return " ".join(parts), i
|
|
709
|
+
|
|
710
|
+
@classmethod
|
|
711
|
+
def _convert_block(cls, lines: List[str], match_counter: Optional[List[int]] = None) -> List[str]:
|
|
712
|
+
if match_counter is None:
|
|
713
|
+
match_counter = [0]
|
|
714
|
+
|
|
715
|
+
result = []
|
|
716
|
+
i = 0
|
|
717
|
+
|
|
718
|
+
while i < len(lines):
|
|
719
|
+
line = lines[i].rstrip()
|
|
720
|
+
|
|
721
|
+
# 检测match语句(支持多行)
|
|
722
|
+
if re.match(r'^\s*match\s+', line):
|
|
723
|
+
full_match, end_idx = cls._collect_logical_line(lines, i)
|
|
724
|
+
|
|
725
|
+
match_stmt = re.match(r'^(\s*)match\s+(.+?)\s*:', full_match)
|
|
726
|
+
if not match_stmt:
|
|
727
|
+
result.append(line)
|
|
728
|
+
i += 1
|
|
729
|
+
continue
|
|
730
|
+
|
|
731
|
+
indent = match_stmt.group(1)
|
|
732
|
+
subject = match_stmt.group(2).strip()
|
|
733
|
+
i = end_idx + 1 # 跳过match语句
|
|
734
|
+
|
|
735
|
+
# 生成临时变量
|
|
736
|
+
var_name = f"__MATCH_{match_counter[0]}__"
|
|
737
|
+
match_counter[0] += 1
|
|
738
|
+
result.append(f"{indent}{var_name} = {subject}")
|
|
739
|
+
|
|
740
|
+
# 解析case语句
|
|
741
|
+
cases: List[Tuple[str, List[str]]] = []
|
|
742
|
+
case_indent = None
|
|
743
|
+
|
|
744
|
+
while i < len(lines):
|
|
745
|
+
case_line = lines[i].rstrip()
|
|
746
|
+
|
|
747
|
+
# 缩进检查 - case必须比match缩进更多
|
|
748
|
+
if not case_line.startswith(indent + ' ') and case_line.strip():
|
|
749
|
+
if re.match(r'^\s*case\s+', case_line):
|
|
750
|
+
raise MatchCaseError(f"第 {i + 1} 行: case 缩进必须大于 match")
|
|
751
|
+
break
|
|
752
|
+
|
|
753
|
+
# 检测case语句(不再使用_collect_logical_line,而是单独处理每一行)
|
|
754
|
+
case_match = re.match(r'^(\s+)case\s+(.+?)\s*:', case_line)
|
|
755
|
+
if case_match:
|
|
756
|
+
curr_case_indent = case_match.group(1)
|
|
757
|
+
case_val = case_match.group(2).strip()
|
|
758
|
+
|
|
759
|
+
# 验证缩进 - 允许不同的case缩进(用于嵌套)
|
|
760
|
+
if len(curr_case_indent) <= len(indent):
|
|
761
|
+
raise MatchCaseError(f"第 {i + 1} 行: case 缩进必须大于 match")
|
|
762
|
+
|
|
763
|
+
# 不再强制要求所有case缩进一致,允许嵌套情况下的不同缩进
|
|
764
|
+
if case_indent is None:
|
|
765
|
+
case_indent = curr_case_indent
|
|
766
|
+
|
|
767
|
+
# 提取内联代码(如果有)
|
|
768
|
+
inline_code = ""
|
|
769
|
+
if ':' in case_line:
|
|
770
|
+
after_colon = case_line.split(':', 1)[1].strip()
|
|
771
|
+
if after_colon:
|
|
772
|
+
inline_code = after_colon
|
|
773
|
+
|
|
774
|
+
i += 1
|
|
775
|
+
|
|
776
|
+
# 收集case块
|
|
777
|
+
block_lines = []
|
|
778
|
+
if inline_code:
|
|
779
|
+
block_lines.append(f"{curr_case_indent} {inline_code}")
|
|
780
|
+
|
|
781
|
+
while i < len(lines):
|
|
782
|
+
block_line = lines[i].rstrip()
|
|
783
|
+
if not block_line.strip():
|
|
784
|
+
block_lines.append(block_line)
|
|
785
|
+
i += 1
|
|
786
|
+
continue
|
|
787
|
+
|
|
788
|
+
# 检查是否是下一个case或者缩进回到当前match级别
|
|
789
|
+
if re.match(r'^\s*case\s+', block_line):
|
|
790
|
+
# 检查这个case是否属于当前match还是父级match
|
|
791
|
+
next_case_indent = re.match(r'^\s*', block_line).group(0)
|
|
792
|
+
if len(next_case_indent) <= len(indent):
|
|
793
|
+
# 属于父级match,退出当前match的处理
|
|
794
|
+
break
|
|
795
|
+
# 仍然属于当前match,继续收集
|
|
796
|
+
if block_line.startswith(curr_case_indent + ' '):
|
|
797
|
+
block_lines.append(block_line)
|
|
798
|
+
i += 1
|
|
799
|
+
continue
|
|
800
|
+
else:
|
|
801
|
+
break
|
|
802
|
+
|
|
803
|
+
if block_line.startswith(indent) and not block_line.startswith(curr_case_indent):
|
|
804
|
+
break
|
|
805
|
+
|
|
806
|
+
if block_line.startswith(curr_case_indent + ' '):
|
|
807
|
+
block_lines.append(block_line)
|
|
808
|
+
i += 1
|
|
809
|
+
continue
|
|
810
|
+
|
|
811
|
+
break
|
|
812
|
+
|
|
813
|
+
cases.append((case_val, block_lines))
|
|
814
|
+
else:
|
|
815
|
+
break
|
|
816
|
+
|
|
817
|
+
# 验证case
|
|
818
|
+
seen = set()
|
|
819
|
+
for idx, (val, _) in enumerate(cases):
|
|
820
|
+
if val == '_':
|
|
821
|
+
if idx != len(cases) - 1:
|
|
822
|
+
raise MatchCaseError(f"第 {i + 1} 行附近: case _ 必须在最后")
|
|
823
|
+
else:
|
|
824
|
+
if val in seen:
|
|
825
|
+
raise MatchCaseError(f"第 {i + 1} 行附近: 重复的 case 值 '{val}'")
|
|
826
|
+
seen.add(val)
|
|
827
|
+
|
|
828
|
+
# 生成if/elif/else
|
|
829
|
+
for idx, (case_val, blk_lines) in enumerate(cases):
|
|
830
|
+
keyword = "else" if case_val == '_' else ("if" if idx == 0 else "elif")
|
|
831
|
+
if keyword == "else":
|
|
832
|
+
result.append(f"{indent}else:")
|
|
833
|
+
else:
|
|
834
|
+
result.append(f"{indent}{keyword} {var_name} == {case_val}:")
|
|
835
|
+
|
|
836
|
+
if blk_lines:
|
|
837
|
+
# 递归处理block_lines,以支持嵌套match
|
|
838
|
+
converted_blk_lines = cls._convert_block(blk_lines, match_counter)
|
|
839
|
+
result.extend(converted_blk_lines)
|
|
840
|
+
|
|
841
|
+
continue
|
|
842
|
+
|
|
843
|
+
result.append(line)
|
|
844
|
+
i += 1
|
|
845
|
+
|
|
846
|
+
return result
|
|
847
|
+
|
|
848
|
+
@classmethod
|
|
849
|
+
def convert_match_to_if(cls, code: str) -> str:
|
|
850
|
+
lines = code.split('\n')
|
|
851
|
+
converted_lines = cls._convert_block(lines, [0])
|
|
852
|
+
return '\n'.join(converted_lines)
|
|
853
|
+
|
|
692
854
|
|
|
693
855
|
class CompilerBase(Compiler_Utils):
|
|
694
856
|
|
|
@@ -831,10 +993,11 @@ class Compiler(CompilerBase):
|
|
|
831
993
|
else:
|
|
832
994
|
_pre_sort_[fname] = 65535
|
|
833
995
|
|
|
834
|
-
# ------------------------------------
|
|
996
|
+
# ------------------------------------ 自定义:mate & match ------------------------------------ #
|
|
835
997
|
for fpath in py_fpath:
|
|
836
998
|
content = self.auto_read(fpath)
|
|
837
999
|
content = self.process_mate_code(content) # 调用process_mate_code
|
|
1000
|
+
content = self.convert_match_to_if(content) # 调用convert_match_to_if
|
|
838
1001
|
with open(fpath, 'w', encoding='utf-8') as f:
|
|
839
1002
|
f.write(content)
|
|
840
1003
|
|
|
@@ -923,9 +1086,9 @@ class Compiler(CompilerBase):
|
|
|
923
1086
|
return _imports, _js_imports, _pre_sort_, _pre_define_, _js_replace_
|
|
924
1087
|
|
|
925
1088
|
def transcrypt_cmd(self):
|
|
926
|
-
# 执行cmd命令: transcrypt -b -m -n -s -e 6 target | execute cmd: transcrypt -b -m -n -s -e 6 target
|
|
1089
|
+
# 执行cmd命令: python -m transcrypt -b -m -n -s -e 6 target | execute cmd: python -m transcrypt -b -m -n -s -e 6 target
|
|
927
1090
|
# 并获取cmd得到的输出 | and get the output of the cmd
|
|
928
|
-
cmd = 'transcrypt -b -m -n -s -e 6 %s' % self.target_py
|
|
1091
|
+
cmd = 'python -m transcrypt -b -m -n -s -e 6 %s' % self.target_py
|
|
929
1092
|
core.lprint(WAIT, core.lformat(LOC_TRANSCRYPTING, [cmd]), end="", ln=config.language)
|
|
930
1093
|
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
|
931
1094
|
stdout, stderr = p.communicate()
|
|
@@ -1220,6 +1383,20 @@ class Compiler(CompilerBase):
|
|
|
1220
1383
|
|
|
1221
1384
|
|
|
1222
1385
|
if __name__ == '__main__':
|
|
1223
|
-
compiler = Compiler('src', 'library', 'build')
|
|
1224
|
-
compiler.compile()
|
|
1225
|
-
compiler.clean()
|
|
1386
|
+
# compiler = Compiler('src', 'library', 'build')
|
|
1387
|
+
# compiler.compile()
|
|
1388
|
+
# compiler.clean()
|
|
1389
|
+
test = """
|
|
1390
|
+
|
|
1391
|
+
def patrolling(self, c: Creep):
|
|
1392
|
+
e = self.center.nearest(k.civilian.enemies, 5)
|
|
1393
|
+
if e:
|
|
1394
|
+
match c.test(e,
|
|
1395
|
+
int(c.hpPer <= 0.9) * 2,
|
|
1396
|
+
int(c.info.melee) * -3
|
|
1397
|
+
):
|
|
1398
|
+
case True: c.move(e, SWAMP_MOTION)
|
|
1399
|
+
case False: c.move(self.bpos, SWAMP_MOTION)
|
|
1400
|
+
|
|
1401
|
+
"""
|
|
1402
|
+
print(f"res=\n{Compiler.convert_match_to_if(test)}")
|
pyscreeps_arena/core/const.py
CHANGED
pyscreeps_arena/localization.py
CHANGED
|
@@ -17,6 +17,10 @@ LOC_FILE_NOT_EXISTS = {
|
|
|
17
17
|
'en': "{} File not exists: {}. You can ignore if it's a not used file.",
|
|
18
18
|
'cn': "{} 文件不存在: {}. 如果它是一个未使用的文件,您可以忽略它。",
|
|
19
19
|
}
|
|
20
|
+
LOC_FILE_READ_FAILED = {
|
|
21
|
+
'en': "Failed to read file: {}({})\nDetails:\n{}",
|
|
22
|
+
'cn': "读取文件失败: {}({})\n详细信息:\n{}",
|
|
23
|
+
}
|
|
20
24
|
LOC_PREPROCESSING = {
|
|
21
25
|
'en': "Preprocessing ...",
|
|
22
26
|
'cn': "预处理中 ...",
|
pyscreeps_arena/project.7z
CHANGED
|
Binary file
|
pyscreeps_arena/ui/P2PY.py
CHANGED
|
@@ -1,105 +1,108 @@
|
|
|
1
|
-
# png_to_py.py - 将PNG图片转换为Python
|
|
1
|
+
# png_to_py.py - 将PNG图片转换为Python模块(最终验证版)
|
|
2
2
|
import base64
|
|
3
3
|
import sys
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
import re
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def png_to_py(png_path, py_path=None, var_name=None):
|
|
8
9
|
"""
|
|
9
10
|
将PNG图片转换为Python模块文件
|
|
10
|
-
|
|
11
|
+
|
|
11
12
|
:param png_path: PNG图片路径
|
|
12
13
|
:param py_path: 输出的.py文件路径(默认为图片同名.py)
|
|
13
14
|
:param var_name: 变量名(默认为文件名的大写形式)
|
|
14
|
-
|
|
15
|
-
:raises: FileNotFoundError: 若PNG文件不存在
|
|
16
|
-
:warns: 若文件不是PNG格式,将输出警告
|
|
17
|
-
|
|
18
|
-
:usage:
|
|
19
|
-
# 基本用法
|
|
20
|
-
png_to_py('icon.png')
|
|
21
|
-
# 指定输出路径和变量名
|
|
22
|
-
png_to_py('logo.png', 'resources/logo.py', 'LOGO_ICON')
|
|
23
15
|
"""
|
|
24
16
|
png_file = Path(png_path)
|
|
25
|
-
|
|
17
|
+
|
|
26
18
|
if not png_file.exists():
|
|
27
19
|
raise FileNotFoundError(f"文件 '{png_path}' 不存在")
|
|
28
|
-
|
|
20
|
+
|
|
29
21
|
if png_file.suffix.lower() != '.png':
|
|
30
22
|
print(f"[警告] 文件 '{png_path}' 不是PNG格式")
|
|
31
23
|
return False
|
|
32
|
-
|
|
24
|
+
|
|
33
25
|
# 自动生成输出路径
|
|
34
26
|
if py_path is None:
|
|
35
27
|
py_path = png_file.with_suffix('.py')
|
|
36
28
|
else:
|
|
37
29
|
py_path = Path(py_path)
|
|
38
|
-
|
|
30
|
+
|
|
39
31
|
# 自动生成变量名
|
|
40
32
|
if var_name is None:
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
base_name = png_file.stem.replace('-', '_').replace(' ', '_')
|
|
34
|
+
base_name = re.sub(r'[^0-9a-zA-Z_]', '', base_name)
|
|
35
|
+
if base_name and base_name[0].isdigit():
|
|
36
|
+
base_name = f'ICON_{base_name}'
|
|
37
|
+
var_name = base_name.upper()
|
|
38
|
+
|
|
43
39
|
try:
|
|
44
40
|
# 读取PNG文件并转换为base64
|
|
45
41
|
with open(png_file, 'rb') as f:
|
|
46
42
|
png_data = f.read()
|
|
47
43
|
base64_data = base64.b64encode(png_data).decode('utf-8')
|
|
48
|
-
|
|
49
|
-
print(f"[DEBUG] 读取PNG文件: {len(png_data)} 字节")
|
|
50
|
-
print(f"[DEBUG] Base64编码长度: {len(base64_data)} 字符")
|
|
51
|
-
|
|
52
|
-
# 生成Python
|
|
44
|
+
|
|
45
|
+
print(f"[DEBUG] 读取PNG文件: {len(png_data)} 字节")
|
|
46
|
+
print(f"[DEBUG] Base64编码长度: {len(base64_data)} 字符")
|
|
47
|
+
|
|
48
|
+
# 生成Python代码(关键修复:使用 len({var_name}))
|
|
53
49
|
py_content = f'''# -*- coding: utf-8 -*-
|
|
54
50
|
"""
|
|
55
51
|
PyQt6资源模块: {png_file.name}
|
|
56
52
|
由 png_to_py.py 自动生成
|
|
57
53
|
"""
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
{base64_data}
|
|
61
|
-
"""
|
|
55
|
+
# Base64编码的PNG数据(单行,无换行符)
|
|
56
|
+
{var_name} = b"{base64_data}"
|
|
62
57
|
|
|
63
58
|
def get_pixmap():
|
|
64
|
-
"""返回QPixmap
|
|
59
|
+
\"\"\"返回QPixmap对象\"\"\"
|
|
65
60
|
from PyQt6.QtGui import QPixmap
|
|
66
61
|
from PyQt6.QtCore import QByteArray
|
|
62
|
+
|
|
63
|
+
byte_array = QByteArray.fromBase64({var_name})
|
|
67
64
|
pixmap = QPixmap()
|
|
68
|
-
pixmap.loadFromData(
|
|
65
|
+
pixmap.loadFromData(byte_array)
|
|
69
66
|
return pixmap
|
|
70
67
|
|
|
71
68
|
def get_icon():
|
|
72
|
-
"""返回QIcon
|
|
69
|
+
\"\"\"返回QIcon对象\"\"\"
|
|
73
70
|
from PyQt6.QtGui import QIcon
|
|
74
|
-
|
|
75
|
-
return icon
|
|
71
|
+
return QIcon(get_pixmap())
|
|
76
72
|
|
|
77
73
|
if __name__ == '__main__':
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
import sys
|
|
75
|
+
from PyQt6.QtWidgets import QApplication
|
|
76
|
+
|
|
77
|
+
app = QApplication.instance() or QApplication(sys.argv)
|
|
78
|
+
|
|
79
|
+
print(f"资源模块 {var_name} 已加载")
|
|
80
|
+
print(f"字节数据长度: {{len({var_name})}} 字节") # ✅ 修复:使用 {var_name}
|
|
81
|
+
|
|
82
|
+
# 验证能否正确加载
|
|
83
|
+
pixmap = get_pixmap()
|
|
84
|
+
print(f"QPixmap加载成功: 尺寸 {{pixmap.width()}}x{{pixmap.height()}}")
|
|
81
85
|
'''
|
|
82
|
-
|
|
86
|
+
|
|
83
87
|
# 写入Python文件
|
|
84
88
|
with open(py_path, 'w', encoding='utf-8') as f:
|
|
85
89
|
f.write(py_content)
|
|
86
|
-
|
|
90
|
+
|
|
87
91
|
print(f"✓ 成功: {png_path} -> {py_path}")
|
|
88
92
|
print(f" 变量名: {var_name}")
|
|
89
93
|
return True
|
|
90
|
-
|
|
94
|
+
|
|
91
95
|
except Exception as e:
|
|
92
96
|
print(f"错误: 转换失败 - {e}")
|
|
97
|
+
import traceback
|
|
98
|
+
traceback.print_exc()
|
|
93
99
|
return False
|
|
94
100
|
|
|
95
101
|
|
|
96
102
|
if __name__ == '__main__':
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
print("用法: python png_to_py.py <图片路径> [输出路径]")
|
|
100
|
-
print("示例: python png_to_py.py logo.png")
|
|
101
|
-
print(" python png_to_py.py icon.png resources/icon.py")
|
|
103
|
+
if len(sys.argv) > 1:
|
|
104
|
+
target = sys.argv[1]
|
|
102
105
|
else:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
target = "icon.png"
|
|
107
|
+
|
|
108
|
+
png_to_py(target, "rs_icon.py")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from PyQt6.QtWidgets import QApplication
|
|
3
|
+
from pyscreeps_arena.ui.qcreeplogic import QPSACreepLogic
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def run_creeplogic_edit():
|
|
8
|
+
app = QApplication(sys.argv)
|
|
9
|
+
window = QPSACreepLogic()
|
|
10
|
+
window.show()
|
|
11
|
+
sys.exit(app.exec())
|
|
12
|
+
|
|
13
|
+
if __name__ == '__main__':
|
|
14
|
+
run_creeplogic_edit()
|
pyscreeps_arena/ui/project_ui.py
CHANGED
|
@@ -10,8 +10,10 @@ from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
|
|
10
10
|
QFileDialog, QMessageBox)
|
|
11
11
|
from PyQt6.QtCore import Qt
|
|
12
12
|
from PyQt6.QtGui import QFont
|
|
13
|
+
from PyQt6.QtGui import QIcon
|
|
13
14
|
from pyscreeps_arena.core import const
|
|
14
|
-
from pyscreeps_arena.ui.rs_icon import
|
|
15
|
+
from pyscreeps_arena.ui.rs_icon import get_pixmap
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class ProjectCreatorUI(QMainWindow):
|
|
@@ -24,7 +26,7 @@ class ProjectCreatorUI(QMainWindow):
|
|
|
24
26
|
def _init_ui(self):
|
|
25
27
|
"""初始化UI界面"""
|
|
26
28
|
self.setWindowTitle("PyScreeps Arena - 项目创建器")
|
|
27
|
-
self.setWindowIcon(
|
|
29
|
+
self.setWindowIcon(QIcon(get_pixmap()))
|
|
28
30
|
self.setFixedSize(500, 350)
|
|
29
31
|
|
|
30
32
|
# 主窗口部件
|
|
@@ -210,4 +212,4 @@ def run_project_creator():
|
|
|
210
212
|
|
|
211
213
|
|
|
212
214
|
if __name__ == '__main__':
|
|
213
|
-
run_project_creator()
|
|
215
|
+
run_project_creator()
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
from pyscreeps_arena.ui.qrecipe.model import PartsVector
|
|
3
|
+
|
|
4
|
+
class NamedRecipe:
|
|
5
|
+
def __init__(self, name: str, recipe: List[str]):
|
|
6
|
+
self.name = name
|
|
7
|
+
self.recipe = recipe
|
|
8
|
+
|
|
9
|
+
class CreepLogicSettings:
|
|
10
|
+
"""
|
|
11
|
+
爬虫逻辑设置类,用于定义爬虫的基本属性和行为。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
# ---------------------- 爬虫本体 | Creep --------------------------- #
|
|
16
|
+
self.draw: bool = False # 是否绘制爬虫信息
|
|
17
|
+
self.layer: int = 10 # 爬虫绘制图层,默认为10
|
|
18
|
+
|
|
19
|
+
# ---------------------- 流程与结构 | Flow & Struct --------------------------- #
|
|
20
|
+
self.link: Union[List[str], str, None] = None # 爬虫逻辑链接
|
|
21
|
+
self.once: bool = True # 是否禁用死亡后重生
|
|
22
|
+
|
|
23
|
+
# ---------------------- 孵化选项 | Spawning --------------------------- #
|
|
24
|
+
self.spawnable = True # 是否可以孵化标志
|
|
25
|
+
self.recipe: Union[List[str], NamedRecipe] = ["MOVE"] # 爬虫配方,默认为["MOVE"]
|
|
26
|
+
self.optimise: bool = True # 自动优化配方标志
|
|
27
|
+
self.extension: bool = True # 使用extension标志
|
|
28
|
+
self.direction: Optional[int] = None # 出生方向
|
|
29
|
+
|
|
30
|
+
# ---------------------- 基本信息 | Basic Info --------------------------- #
|
|
31
|
+
self.name: str = "" # 爬虫名称
|
|
32
|
+
|
|
33
|
+
def to_dict(self) -> dict:
|
|
34
|
+
"""
|
|
35
|
+
将设置转换为字典格式
|
|
36
|
+
"""
|
|
37
|
+
return {
|
|
38
|
+
"name": self.name,
|
|
39
|
+
"draw": self.draw,
|
|
40
|
+
"layer": self.layer,
|
|
41
|
+
"link": self.link,
|
|
42
|
+
"once": self.once,
|
|
43
|
+
"spawnable": self.spawnable,
|
|
44
|
+
"recipe": self.recipe if isinstance(self.recipe, list) else self.recipe.name,
|
|
45
|
+
"optimise": self.optimise,
|
|
46
|
+
"extension": self.extension,
|
|
47
|
+
"direction": self.direction
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def from_dict(cls, data: dict) -> "CreepLogicSettings":
|
|
52
|
+
"""
|
|
53
|
+
从字典格式创建设置对象
|
|
54
|
+
"""
|
|
55
|
+
settings = cls()
|
|
56
|
+
settings.name = data.get("name", "")
|
|
57
|
+
settings.draw = data.get("draw", False)
|
|
58
|
+
settings.layer = data.get("layer", 10)
|
|
59
|
+
settings.link = data.get("link", None)
|
|
60
|
+
settings.once = data.get("once", True)
|
|
61
|
+
settings.spawnable = data.get("spawnable", True)
|
|
62
|
+
settings.recipe = data.get("recipe", ["MOVE"])
|
|
63
|
+
settings.optimise = data.get("optimise", True)
|
|
64
|
+
settings.extension = data.get("extension", True)
|
|
65
|
+
settings.direction = data.get("direction", None)
|
|
66
|
+
return settings
|
|
67
|
+
|
|
68
|
+
def reset(self):
|
|
69
|
+
"""
|
|
70
|
+
重置所有设置为默认值
|
|
71
|
+
"""
|
|
72
|
+
self.__init__()
|