pymud 0.20.3__py3-none-any.whl → 0.21.0a1__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.
- pymud/__init__.py +6 -2
- pymud/dialogs.py +21 -21
- pymud/extras.py +0 -104
- pymud/i18n.py +42 -0
- pymud/lang/i18n_chs.py +205 -0
- pymud/lang/i18n_eng.py +43 -0
- pymud/main.py +107 -42
- pymud/modules.py +198 -14
- pymud/objects.py +6 -6
- pymud/pkuxkx.py +44 -133
- pymud/pymud.py +75 -54
- pymud/session.py +185 -176
- pymud/settings.py +11 -3
- pymud-0.21.0a1.dist-info/METADATA +370 -0
- pymud-0.21.0a1.dist-info/RECORD +22 -0
- {pymud-0.20.3.dist-info → pymud-0.21.0a1.dist-info}/WHEEL +1 -1
- pymud-0.20.3.dist-info/METADATA +0 -1029
- pymud-0.20.3.dist-info/RECORD +0 -19
- {pymud-0.20.3.dist-info → pymud-0.21.0a1.dist-info}/entry_points.txt +0 -0
- {pymud-0.20.3.dist-info → pymud-0.21.0a1.dist-info/licenses}/LICENSE.txt +0 -0
- {pymud-0.20.3.dist-info → pymud-0.21.0a1.dist-info}/top_level.txt +0 -0
pymud/pkuxkx.py
CHANGED
@@ -1,19 +1,16 @@
|
|
1
1
|
# 示例脚本:如何在PyMud中玩PKUXKX
|
2
2
|
|
3
3
|
import webbrowser
|
4
|
-
from pymud import Alias, Trigger,
|
4
|
+
from pymud import Session, IConfig, alias, trigger, timer, gmcp, Alias, Trigger, Timer, SimpleTrigger, SimpleAlias
|
5
5
|
|
6
6
|
# 在PyMud中,使用#load {filename}可以加载对应的配置作为脚本文件以提供支撑。支持多脚本加载
|
7
|
-
# 本示例脚本对PyMud支持的变量(Variable)、触发器(Trigger,包含单行与多行触发)、别名(Alias)、定时器(Timer)
|
8
|
-
# 使用#load {filename}
|
7
|
+
# 本示例脚本对PyMud支持的变量(Variable)、触发器(Trigger,包含单行与多行触发)、别名(Alias)、定时器(Timer)进行了代码示例
|
8
|
+
# 使用#load {filename}加载的配置文件中,若有一个类型继承自IConfig,则在#load操作时,会自动创建此类型;若没有继承自IConfig的类,则仅将文件引入
|
9
9
|
# 例如,加载本文件指定的配置,则使用 #load pymud.pkuxkx即可
|
10
10
|
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
class Configuration:
|
11
|
+
# 定义一个自定义配置类,并继承自IConfig。
|
12
|
+
# 目前不在推荐使用Configuration类,而是使用IConfig接口。因为只有使用IConfig接口,才能在类型函数中自动管理由装饰器创建的对象
|
13
|
+
class MyConfig(IConfig):
|
17
14
|
|
18
15
|
# hpbrief long情况下的含义
|
19
16
|
HP_KEYS = (
|
@@ -22,135 +19,49 @@ class Configuration:
|
|
22
19
|
"vigour/qi", "vigour/yuan", "food", "water", "fighting", "busy"
|
23
20
|
)
|
24
21
|
|
25
|
-
# 类的构造函数,传递参数session
|
26
|
-
def __init__(self, session) -> None:
|
22
|
+
# 类的构造函数,传递参数session,是会话本身。另外请保留*args和**kwargs,以便后续扩展
|
23
|
+
def __init__(self, session: Session, *args, **kwargs) -> None:
|
27
24
|
self.session = session
|
28
|
-
self.
|
29
|
-
|
30
|
-
|
31
|
-
self.
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
# 1. fullme的链接对应的触发器,匹配URL
|
56
|
-
# 当匹配成功后,调用ontri_webpage
|
57
|
-
self._triggers["tri_webpage"] = self.tri_webpage = Trigger(self.session, id = 'tri_webpage', patterns = r'^http://fullme.pkuxkx.net/robot.php.+$', group = "sys", onSuccess = lambda id, line, wildcards: webbrowser.open(line))
|
58
|
-
# 2. fullme的链接对应的触发器,因为要进行多行匹配(3行),因此匹配模式pattern为3个正则表达式模式构成的元组(所有列表类型均可识别),无需像MushClient一样要指定multiline标识和linesToMatch数量
|
59
|
-
# 当匹配成功后,调用ontri_hpbrief
|
60
|
-
# 特别说明:此处的hpbrief触发匹配,需要set hpbrief long后才可以支持
|
61
|
-
self._triggers["tri_hp"] = self.tri_hp = Trigger(self.session, id = 'tri_hpbrief', patterns = (r'^[> ]*#(\d+.?\d*[KM]?),(\d+),(\d+),(\d+),(\d+),(\d+)$', r'^[> ]*#(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)$', r'^[> ]*#(\d+),(\d+),(-?\d+),(-?\d+),(\d+),(\d+)$',), group = "sys", onSuccess = self.ontri_hpbrief)
|
62
|
-
|
63
|
-
# 3. 现在支持简单Trigger了,例如
|
64
|
-
self._triggers["tri_gem"] = SimpleTrigger(self.session ,r'^[> ]*从.+身上.+[◎☆★].+', "pack gem", group = "sys")
|
65
|
-
|
66
|
-
self.session.addTriggers(self._triggers)
|
67
|
-
|
68
|
-
|
69
|
-
def _initCommands(self):
|
70
|
-
'''初始化命令,本示例中创建了1个命令,支持hpbrief命令'''
|
71
|
-
|
72
|
-
# Command是异步执行的命令,可以理解为Alias+Trigger+Timer的组合。在MUD中发出一条命令后,会有成功、失败、超时的不同状态,在这三种状态下,会分别调用onSuccess、onFailure、onTimeout方法
|
73
|
-
# 举个Command的应用例子说明:加入把移动(s/e/n/w等等)实现为一个Command。
|
74
|
-
# 1. 当向某个方向移动时,成功的时候会移动到下一个房间;
|
75
|
-
# 2. 不成功的时候,会出现“这个方向没有出路”等描述;
|
76
|
-
# 3. 而当角色处于晕倒状态时,移动命令是不会有任何反应的,超出设定超时时间之后,会调用onTimeout
|
77
|
-
# 在上述实现情况下,我们在执行命令时,可以明确的根据命令的执行结果再判断下一步该做什么
|
78
|
-
# 本示例使用了简单命令SimpleCommand,其在MatchObject的基础上,增加了以下参数:
|
79
|
-
# 1. succ_tri: 命令执行成功时的触发器,不能为空
|
80
|
-
# 2. fail_tri: 命令执行失败时的触发器,可以为空
|
81
|
-
# 3. retry_tri: 需要重新尝试命令的触发器,可以为空(仍以移动为例,当向某个方向移动,出现“你现在正忙着呢”之后,可以在等待2s之后,再次尝试该命令,知道到达最大尝试次数
|
82
|
-
|
83
|
-
# 命令可以同步调用,也可以在异步函数(async)中使用await语法异步调用
|
84
|
-
# 例如,下面的hpbrief可以在这样使用:
|
85
|
-
# self.session.exec_command("hpbrief")
|
86
|
-
# self.session.exec_command_after(2, "hpbrief")
|
87
|
-
# await self.cmd_hpbrief.execute("hpbrief")
|
88
|
-
|
89
|
-
# 异步实现意味着,在函数实现过程中可以以循环实现,而不是以回调实现,有利于代码的可读性
|
90
|
-
# 假设已经实现了一个 cmd_move 的 Command,现在要从ct 执行"s;s;w"行走指令到达春来茶馆,然后根据当前的hpbrief结果判断是否需要drink,然后走回中央广场,可以在函数中这样实现:
|
91
|
-
# async def gotodrink(self):
|
92
|
-
# for step in "s;s;w".split(";"):
|
93
|
-
# await self.cmd_move.execute(step)
|
94
|
-
# await self.cmd_hpbrief.execute("hpbrief")
|
95
|
-
# await asyncio.sleep(1)
|
96
|
-
# water = self.session.getVariable("water")
|
97
|
-
# if int(water) < 300:
|
98
|
-
# self.session.writeline("drink")
|
99
|
-
# await asyncio.sleep(1)
|
100
|
-
# for step in "e;n;n".split(";"):
|
101
|
-
# await self.cmd_move.execute(step)
|
102
|
-
|
103
|
-
|
104
|
-
self._commands['cmd_hpbrief'] = self.cmd_hpbrief = SimpleCommand(self.session, id = "cmd_hpbrief", patterns = "^hpbrief$", succ_tri = self.tri_hp, group = "status", onSuccess = self.oncmd_hpbrief)
|
105
|
-
self.session.addCommands(self._commands)
|
106
|
-
|
107
|
-
def _initAliases(self):
|
108
|
-
'''初始化别名,本示例中创建了1个别名,是get xxx from corpse'''
|
109
|
-
|
110
|
-
# get xxx from corpse的别名操作,匹配成功后会自动调用getfromcorpse函数
|
111
|
-
# 例如, gp silver 相当于 get silver from corpse
|
112
|
-
self._aliases['ali_get'] = Alias(self.session, r"^gp\s(.+)$", id = "ali_get", onSuccess = self.getfromcorpse)
|
113
|
-
|
114
|
-
# 3. 现在支持简单Alias了,在其中也可以支持#wait(缩写为#wa操作)等待,当然,Trigger也支持
|
115
|
-
# 从扬州中心广场到西门的行走,每步中间插入100ms等待
|
116
|
-
self._aliases["ali_yz_xm"] = SimpleAlias(self.session ,'^yz_xm$', "w;#wa 100;w;#wa 100;w;#wa 100;w", group = "sys")
|
117
|
-
|
118
|
-
self.session.addAliases(self._aliases)
|
119
|
-
|
120
|
-
def _initTimers(self):
|
121
|
-
'''初始化定时器,本示例中创建了1个定时器,每隔2秒打印信息'''
|
122
|
-
|
123
|
-
self._timers["tm_test"] = self.tm_test = Timer(self.session, timeout = 2, id = "tm_test", onSuccess = self.onTimer)
|
124
|
-
self.session.addTimers(self._timers)
|
125
|
-
|
126
|
-
def getfromcorpse(self, name, line, wildcards):
|
25
|
+
# 所有自行构建的对象, 统一放到self._objs中,方便管理和卸载。
|
26
|
+
# 目前加载卸载可以支持字典、列表、单个对象均可。此处使用字典,是为了方便后续处理其中某个单个对象。
|
27
|
+
# 对象创建时将自动增加到会话中,不需要手动调用session.addObject操作了
|
28
|
+
self._objs = {
|
29
|
+
# 别名,触发器可以通过创建一个对应类型的实例来生成
|
30
|
+
"tri_gem": SimpleTrigger(self.session ,r'^[> ]*从.+身上.+[◎☆★].+', "pack gem", group = "sys"),
|
31
|
+
"ali_yz_xm": SimpleAlias(self.session ,'^yz_xm$', "w;#wa 100;w;#wa 100;w;#wa 100;w", group = "sys")
|
32
|
+
}
|
33
|
+
|
34
|
+
# 不要遗漏 super().__init__(),否则会导致@alias等函数装饰器不能正常创建对象
|
35
|
+
super().__init__(session, *args, **kwargs)
|
36
|
+
|
37
|
+
def __unload__(self):
|
38
|
+
# 在__unload__方法中定义卸载时需要从会话中清除的对象。
|
39
|
+
# 目前加载卸载可以支持字典、列表、单个对象均可。
|
40
|
+
self.session.delObjects(self._objs)
|
41
|
+
|
42
|
+
# 不要遗漏 super().__unload__(),否则会导致@alias等函数装饰器生成的对象不能被正常卸载
|
43
|
+
super().__unload__()
|
44
|
+
|
45
|
+
@trigger('^http://fullme.pkuxkx.net/robot.php.+$', group = "sys")
|
46
|
+
def ontri_webpage(self, id, line, wildcards):
|
47
|
+
webbrowser.open(line)
|
48
|
+
|
49
|
+
@alias(r"^gp\s(.+)$", id = "ali_get", group = "sys")
|
50
|
+
def getfromcorpse(self, id, line, wildcards):
|
127
51
|
cmd = f"get {wildcards[0]} from corpse"
|
128
52
|
self.session.writeline(cmd)
|
129
53
|
|
130
|
-
|
54
|
+
@timer(2)
|
55
|
+
def onTimer(self, id, *args, **kwargs):
|
131
56
|
self.session.info("每2秒都会打印本信息", "定时器测试")
|
132
57
|
|
133
|
-
|
58
|
+
@trigger(id = 'tri_hpbrief', patterns = (r'^[> ]*#(\d+.?\d*[KM]?),(\d+),(\d+),(\d+),(\d+),(\d+)$', r'^[> ]*#(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)$', r'^[> ]*#(\d+),(\d+),(-?\d+),(-?\d+),(\d+),(\d+)$',), group = "sys")
|
59
|
+
def ontri_hpbrief(self, id, line, wildcards):
|
134
60
|
self.session.setVariables(self.HP_KEYS, wildcards)
|
135
61
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
# │【真气】 0 / 0 [ 0%] │【禅定】 101% [正常] │
|
143
|
-
# │【食物】 222 / 400 [缺食] │【潜能】 36,955 │
|
144
|
-
# │【饮水】 247 / 400 [缺水] │【经验】 2,341,005 │
|
145
|
-
# ├─────────────────────────────┴─────────────────────────────┤
|
146
|
-
# │【状态】 健康、怒 │
|
147
|
-
# └────────────────────────────────────────────北大侠客行────────┘
|
148
|
-
var1 = self.session.getVariables(("jing", "effjing", "maxjing", "jingli", "maxjingli"))
|
149
|
-
line1 = "【精神】 {0:<8} [{5:3.0f}%] / {1:<8} [{2:3.0f}%] |【精力】 {3:<8} / {4:<8} [{6:3.0f}%]".format(var1[0], var1[1], 100 * float(var1[1]) / float(var1[2]), var1[3], var1[4], 100 * float(var1[0]) / float(var1[2]), 100 * float(var1[3]) / float(var1[4]))
|
150
|
-
var2 = self.session.getVariables(("qi", "effqi", "maxqi", "neili", "maxneili"))
|
151
|
-
line2 = "【气血】 {0:<8} [{5:3.0f}%] / {1:<8} [{2:3.0f}%] |【内力】 {3:<8} / {4:<8} [{6:3.0f}%]".format(var2[0], var2[1], 100 * float(var2[1]) / float(var2[2]), var2[3], var2[4], 100 * float(var2[0]) / float(var2[2]), 100 * float(var2[3]) / float(var2[4]))
|
152
|
-
var3 = self.session.getVariables(("food", "water", "exp", "pot", "fighting", "busy"))
|
153
|
-
line3 = "【食物】 {0:<4} 【饮水】{1:<4} 【经验】{2:<9} 【潜能】{3:<10}【{4}】【{5}】".format(var3[0], var3[1], var3[2], var3[3], "未战斗" if var3[4] == "0" else "战斗中", "不忙" if var3[5] == "0" else "忙")
|
154
|
-
self.session.info(line1, "状态")
|
155
|
-
self.session.info(line2, "状态")
|
156
|
-
self.session.info(line3, "状态")
|
62
|
+
|
63
|
+
# 若多个对象共用同一个处理函数,也可以同时使用多个装饰器实现
|
64
|
+
@trigger(r"^\s+你可以获取(.+)")
|
65
|
+
@trigger(r"^\s+这里位于(.+)和(.+)的.+")
|
66
|
+
def ontri_multideco(self, id, line, wildcards):
|
67
|
+
self.session.info("触发器触发,ID: {0}, 内容: {1}, 匹配项: {2}".format(id, line, wildcards), "测试")
|
pymud/pymud.py
CHANGED
@@ -34,6 +34,7 @@ from prompt_toolkit.layout.processors import (
|
|
34
34
|
HighlightSelectionProcessor,
|
35
35
|
)
|
36
36
|
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
37
|
+
from wcwidth import wcwidth, wcswidth
|
37
38
|
|
38
39
|
from .objects import CodeBlock
|
39
40
|
from .extras import MudFormatProcessor, SessionBuffer, EasternMenuContainer, VSplitWindow, SessionBufferControl, DotDict
|
@@ -75,7 +76,18 @@ class PyMudApp:
|
|
75
76
|
构造PyMudApp对象实例,并加载替代配置。
|
76
77
|
"""
|
77
78
|
|
79
|
+
from .i18n import i18n_LoadLanguage, i18n_ListAvailableLanguages
|
80
|
+
# 加载默认chs语言内容,以防翻译不完整时,默认使用中文替代
|
81
|
+
i18n_LoadLanguage("chs")
|
82
|
+
|
78
83
|
if cfg_data and isinstance(cfg_data, dict):
|
84
|
+
# load language from
|
85
|
+
language = Settings.language
|
86
|
+
if "language" in cfg_data.keys():
|
87
|
+
if cfg_data["language"] in i18n_ListAvailableLanguages() and cfg_data["language"] != "chs":
|
88
|
+
language = cfg_data["language"]
|
89
|
+
i18n_LoadLanguage(language)
|
90
|
+
|
79
91
|
for key in cfg_data.keys():
|
80
92
|
if key == "sessions":
|
81
93
|
Settings.sessions = cfg_data[key]
|
@@ -89,6 +101,8 @@ class PyMudApp:
|
|
89
101
|
Settings.styles.update(cfg_data[key])
|
90
102
|
elif key == "keys":
|
91
103
|
Settings.keys.update(cfg_data[key])
|
104
|
+
elif key == "language":
|
105
|
+
Settings.language = cfg_data[key]
|
92
106
|
|
93
107
|
self._mouse_support = True
|
94
108
|
self._plugins = DotDict() # 增加 插件 字典
|
@@ -303,26 +317,26 @@ class PyMudApp:
|
|
303
317
|
body = self.body,
|
304
318
|
menu_items=[
|
305
319
|
MenuItem(
|
306
|
-
Settings.
|
320
|
+
Settings.gettext("world"),
|
307
321
|
children=self.create_world_menus(),
|
308
322
|
),
|
309
323
|
MenuItem(
|
310
|
-
Settings.
|
324
|
+
Settings.gettext("session"),
|
311
325
|
children=[
|
312
|
-
MenuItem(Settings.
|
313
|
-
MenuItem(Settings.
|
314
|
-
MenuItem(Settings.
|
315
|
-
MenuItem(Settings.
|
326
|
+
MenuItem(Settings.gettext("disconnect"), handler = self.act_discon),
|
327
|
+
MenuItem(Settings.gettext("connect"), handler = self.act_connect),
|
328
|
+
MenuItem(Settings.gettext("closesession"), handler = self.act_close_session),
|
329
|
+
MenuItem(Settings.gettext("autoreconnect"), handler = self.act_autoreconnect),
|
316
330
|
MenuItem("-", disabled=True),
|
317
|
-
MenuItem(Settings.
|
318
|
-
MenuItem(Settings.
|
319
|
-
MenuItem(Settings.
|
320
|
-
MenuItem(Settings.
|
321
|
-
MenuItem(Settings.
|
322
|
-
MenuItem(Settings.
|
331
|
+
MenuItem(Settings.gettext("nosplit"), handler = self.act_nosplit),
|
332
|
+
MenuItem(Settings.gettext("echoinput"), handler = self.act_echoinput),
|
333
|
+
MenuItem(Settings.gettext("beautify"), handler = self.act_beautify),
|
334
|
+
MenuItem(Settings.gettext("copy"), handler = self.act_copy),
|
335
|
+
MenuItem(Settings.gettext("copyraw"), handler = self.act_copyraw),
|
336
|
+
MenuItem(Settings.gettext("clearsession"), handler = self.act_clearsession),
|
323
337
|
MenuItem("-", disabled=True),
|
324
338
|
|
325
|
-
MenuItem(Settings.
|
339
|
+
MenuItem(Settings.gettext("reloadconfig"), handler = self.act_reload),
|
326
340
|
]
|
327
341
|
),
|
328
342
|
|
@@ -336,9 +350,9 @@ class PyMudApp:
|
|
336
350
|
# ),
|
337
351
|
|
338
352
|
MenuItem(
|
339
|
-
Settings.
|
353
|
+
Settings.gettext("help"),
|
340
354
|
children=[
|
341
|
-
MenuItem(Settings.
|
355
|
+
MenuItem(Settings.gettext("about"), handler = self.act_about)
|
342
356
|
]
|
343
357
|
),
|
344
358
|
|
@@ -359,7 +373,7 @@ class PyMudApp:
|
|
359
373
|
def create_world_menus(self):
|
360
374
|
"创建世界子菜单,其中根据本地pymud.cfg中的有关配置创建会话有关子菜单"
|
361
375
|
menus = []
|
362
|
-
menus.append(MenuItem(Settings.
|
376
|
+
menus.append(MenuItem(f'{Settings.gettext("new_session")}...', handler = self.act_new))
|
363
377
|
menus.append(MenuItem("-", disabled=True))
|
364
378
|
|
365
379
|
ss = Settings.sessions
|
@@ -372,9 +386,9 @@ class PyMudApp:
|
|
372
386
|
menus.append(menu)
|
373
387
|
|
374
388
|
menus.append(MenuItem("-", disabled=True))
|
375
|
-
menus.append(MenuItem(Settings.
|
389
|
+
menus.append(MenuItem(Settings.gettext("show_log"), handler = self.show_logSelectDialog))
|
376
390
|
menus.append(MenuItem("-", disabled=True))
|
377
|
-
menus.append(MenuItem(Settings.
|
391
|
+
menus.append(MenuItem(Settings.gettext("exit"), handler=self.act_exit))
|
378
392
|
|
379
393
|
return menus
|
380
394
|
|
@@ -507,12 +521,10 @@ class PyMudApp:
|
|
507
521
|
line = self.mudFormatProc.line_correction(b.document.current_line)
|
508
522
|
start = max(0, scol)
|
509
523
|
end = min(ecol, len(line))
|
510
|
-
#line_plain = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", line, flags = re.IGNORECASE).replace("\r", "").replace("\x00", "")
|
511
524
|
line_plain = Session.PLAIN_TEXT_REGX.sub("", line).replace("\r", "").replace("\x00", "")
|
512
|
-
#line_plain = re.sub("\x1b\\[[^mz]+[mz]", "", line).replace("\r", "").replace("\x00", "")
|
513
525
|
selection = line_plain[start:end]
|
514
526
|
self.app.clipboard.set_text(selection)
|
515
|
-
self.set_status("
|
527
|
+
self.set_status(Settings.gettext("msg_copy", selection))
|
516
528
|
if self.current_session:
|
517
529
|
self.current_session.setVariable("%copy", selection)
|
518
530
|
else:
|
@@ -520,12 +532,11 @@ class PyMudApp:
|
|
520
532
|
lines = []
|
521
533
|
for row in range(srow, erow + 1):
|
522
534
|
line = b.document.lines[row]
|
523
|
-
#line_plain = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", line, flags = re.IGNORECASE).replace("\r", "").replace("\x00", "")
|
524
535
|
line_plain = Session.PLAIN_TEXT_REGX.sub("", line).replace("\r", "").replace("\x00", "")
|
525
536
|
lines.append(line_plain)
|
526
537
|
|
527
538
|
self.app.clipboard.set_text("\n".join(lines))
|
528
|
-
self.set_status("
|
539
|
+
self.set_status(Settings.gettext("msg_copylines", 1 + erow - srow))
|
529
540
|
|
530
541
|
if self.current_session:
|
531
542
|
self.current_session.setVariable("%copy", "\n".join(lines))
|
@@ -535,7 +546,7 @@ class PyMudApp:
|
|
535
546
|
if srow == erow:
|
536
547
|
line = b.document.current_line
|
537
548
|
self.app.clipboard.set_text(line)
|
538
|
-
self.set_status("
|
549
|
+
self.set_status(Settings.gettext("msg_copy", line))
|
539
550
|
|
540
551
|
if self.current_session:
|
541
552
|
self.current_session.setVariable("%copy", line)
|
@@ -544,18 +555,13 @@ class PyMudApp:
|
|
544
555
|
lines = b.document.lines[srow:erow+1]
|
545
556
|
copy_raw_text = "".join(lines)
|
546
557
|
self.app.clipboard.set_text(copy_raw_text)
|
547
|
-
self.set_status("
|
558
|
+
self.set_status(Settings.gettext("msg_copylines", 1 + erow - srow))
|
548
559
|
|
549
560
|
if self.current_session:
|
550
561
|
self.current_session.setVariable("%copy", copy_raw_text)
|
551
562
|
|
552
|
-
# data = self.consoleView.buffer.copy_selection()
|
553
|
-
# self.app.clipboard.set_data(data)
|
554
|
-
# self.set_status("已复制:{}".format(data.text))
|
555
|
-
|
556
|
-
# self.current_session.setVariable("%copy", data.text)
|
557
563
|
else:
|
558
|
-
self.set_status("
|
564
|
+
self.set_status(Settings.gettext("msg_no_selection"))
|
559
565
|
|
560
566
|
def create_session(self, name, host, port, encoding = None, after_connect = None, scripts = None, userid = None):
|
561
567
|
"""
|
@@ -584,13 +590,19 @@ class PyMudApp:
|
|
584
590
|
|
585
591
|
result = True
|
586
592
|
else:
|
587
|
-
self.set_status(
|
593
|
+
self.set_status(Settings.gettext("msg_session_exists", name))
|
588
594
|
|
589
595
|
return result
|
590
596
|
|
591
597
|
def show_logSelectDialog(self):
|
598
|
+
def correction_align_width(text, width):
|
599
|
+
"修正文本对齐宽度,防止ljust和rjust方法产生的中文宽度不对齐问题"
|
600
|
+
return width - wcswidth(text) + len(text)
|
592
601
|
async def coroutine():
|
593
|
-
|
602
|
+
title_filename = Settings.gettext("logfile_name").ljust(correction_align_width(Settings.gettext("logfile_name"), 20))
|
603
|
+
title_filesize = Settings.gettext("logfile_size").rjust(correction_align_width(Settings.gettext("logfile_size"), 20))
|
604
|
+
title_modified = Settings.gettext("logfile_modified").center(correction_align_width(Settings.gettext("logfile_modified"), 23))
|
605
|
+
head_line = " {}{}{}".format(title_filename, title_filesize, title_modified)
|
594
606
|
|
595
607
|
log_list = list()
|
596
608
|
files = [f for f in os.listdir('.') if os.path.isfile(f) and f.endswith('.log')]
|
@@ -663,7 +675,7 @@ class PyMudApp:
|
|
663
675
|
async def coroutine():
|
664
676
|
if self.current_session:
|
665
677
|
if self.current_session.connected:
|
666
|
-
dlgQuery = QueryDialog(HTML('<b fg="red"
|
678
|
+
dlgQuery = QueryDialog(HTML(f'<b fg="red">{Settings.gettext("warning")}</b>'), HTML(f'<style fg="red">{Settings.gettext("session_close_prompt", self.current_session.name)}</style>'))
|
667
679
|
result = await self.show_dialog_as_float(dlgQuery)
|
668
680
|
if result:
|
669
681
|
self.current_session.disconnect()
|
@@ -751,21 +763,21 @@ class PyMudApp:
|
|
751
763
|
val = not Settings.client["beautify"]
|
752
764
|
Settings.client["beautify"] = val
|
753
765
|
if self.current_session:
|
754
|
-
self.current_session.info(f"
|
766
|
+
self.current_session.info(f'{Settings.gettext("msg_beautify")}{Settings.gettext("open") if val else Settings.gettext("close")}!')
|
755
767
|
|
756
768
|
def act_echoinput(self):
|
757
769
|
"菜单: 显示/隐藏输入指令"
|
758
770
|
val = not Settings.client["echo_input"]
|
759
771
|
Settings.client["echo_input"] = val
|
760
772
|
if self.current_session:
|
761
|
-
self.current_session.info(f"
|
773
|
+
self.current_session.info(f'{Settings.gettext("msg_echoinput")}{Settings.gettext("open") if val else Settings.gettext("close")}!')
|
762
774
|
|
763
775
|
def act_autoreconnect(self):
|
764
776
|
"菜单: 打开/关闭自动重连"
|
765
777
|
val = not Settings.client["auto_reconnect"]
|
766
778
|
Settings.client["auto_reconnect"] = val
|
767
779
|
if self.current_session:
|
768
|
-
self.current_session.info(f"
|
780
|
+
self.current_session.info(f'{Settings.gettext("msg_autoreconnect")}{Settings.gettext("open") if val else Settings.gettext("close")}')
|
769
781
|
|
770
782
|
def act_copy(self):
|
771
783
|
"菜单: 复制纯文本"
|
@@ -808,7 +820,7 @@ class PyMudApp:
|
|
808
820
|
con_sessions.append(session.name)
|
809
821
|
|
810
822
|
if len(con_sessions) > 0:
|
811
|
-
dlgQuery = QueryDialog(HTML('<b fg="red"
|
823
|
+
dlgQuery = QueryDialog(HTML(f'<b fg="red">{Settings.gettext('warning_exit')}</b>'), HTML(f'<style fg="red">{Settings.gettext("app_exit_prompt", len(con_sessions), ", ".join(con_sessions))}</style>'))
|
812
824
|
result = await self.show_dialog_as_float(dlgQuery)
|
813
825
|
if result:
|
814
826
|
for ss_name in con_sessions:
|
@@ -844,7 +856,7 @@ class PyMudApp:
|
|
844
856
|
|
845
857
|
def get_input_prompt(self):
|
846
858
|
"命令输入行提示符"
|
847
|
-
return HTML(Settings.
|
859
|
+
return HTML(Settings.gettext("input_prompt"))
|
848
860
|
|
849
861
|
def btn_title_clicked(self, name, mouse_event: MouseEvent):
|
850
862
|
"顶部会话标签点击切换鼠标事件"
|
@@ -906,17 +918,17 @@ class PyMudApp:
|
|
906
918
|
"状态栏右侧内容"
|
907
919
|
con_str, mouse_support, tri_status, beautify = "", "", "", ""
|
908
920
|
if not Settings.client["beautify"]:
|
909
|
-
beautify = "
|
921
|
+
beautify = Settings.gettext("status_nobeautify") + " "
|
910
922
|
|
911
923
|
if not self._mouse_support:
|
912
|
-
mouse_support = "
|
924
|
+
mouse_support = Settings.gettext("status_mouseinh") + " "
|
913
925
|
|
914
926
|
if self.current_session:
|
915
927
|
if self.current_session._ignore:
|
916
|
-
tri_status = "
|
928
|
+
tri_status = Settings.gettext("status_ignore") + " "
|
917
929
|
|
918
930
|
if not self.current_session.connected:
|
919
|
-
con_str = "
|
931
|
+
con_str = Settings.gettext("status_notconnect")
|
920
932
|
else:
|
921
933
|
dura = self.current_session.duration
|
922
934
|
DAY, HOUR, MINUTE = 86400, 3600, 60
|
@@ -927,15 +939,14 @@ class PyMudApp:
|
|
927
939
|
dura = dura - hours * HOUR
|
928
940
|
mins = dura // MINUTE
|
929
941
|
sec = dura - mins * MINUTE
|
930
|
-
|
931
942
|
if days > 0:
|
932
|
-
con_str = "
|
943
|
+
con_str = Settings.gettext("status_connected") + ": {0:.0f}{4}{1:.0f}{5}{2:.0f}{6}{3:.0f}{7}".format(days, hours, mins, sec, Settings.gettext("Day"), Settings.gettext("Hour"), Settings.gettext("Minute"), Settings.gettext("Second"))
|
933
944
|
elif hours > 0:
|
934
|
-
con_str = "
|
945
|
+
con_str = Settings.gettext("status_connected") + ": {0:.0f}{3}{1:.0f}{4}{2:.0f}{5}".format(hours, mins, sec, Settings.gettext("Hour"), Settings.gettext("Minute"), Settings.gettext("Second"))
|
935
946
|
elif mins > 0:
|
936
|
-
con_str = "
|
947
|
+
con_str = Settings.gettext("status_connected") + ": {0:.0f}{2}{1:.0f}{3}".format(mins, sec, Settings.gettext("Minute"), Settings.gettext("Second"))
|
937
948
|
else:
|
938
|
-
con_str = "
|
949
|
+
con_str = Settings.gettext("status_connected") + ": {:.0f}{}".format(sec, Settings.gettext("Second"))
|
939
950
|
|
940
951
|
return "{}{}{}{} {} {} ".format(beautify, mouse_support, tri_status, con_str, Settings.__appname__, Settings.__version__)
|
941
952
|
|
@@ -1056,7 +1067,7 @@ class PyMudApp:
|
|
1056
1067
|
nothandle = not self._quickHandleSession(group, name)
|
1057
1068
|
|
1058
1069
|
else:
|
1059
|
-
errmsg =
|
1070
|
+
errmsg = Settings.gettext("msg_cmd_session_error")
|
1060
1071
|
|
1061
1072
|
elif len(args) >= 3:
|
1062
1073
|
session_name = args[0]
|
@@ -1096,7 +1107,7 @@ class PyMudApp:
|
|
1096
1107
|
self.current_session.writeline("")
|
1097
1108
|
else:
|
1098
1109
|
try:
|
1099
|
-
self.current_session.log.log(f"
|
1110
|
+
self.current_session.log.log(f"{Settings.gettext('msg_cmdline_input')} {cmd_line}\n")
|
1100
1111
|
|
1101
1112
|
cb = CodeBlock(cmd_line)
|
1102
1113
|
cb.execute(self.current_session)
|
@@ -1109,7 +1120,7 @@ class PyMudApp:
|
|
1109
1120
|
elif (cmd_line == "#close") and self.showLog:
|
1110
1121
|
self.act_close_session()
|
1111
1122
|
else:
|
1112
|
-
self.set_status(
|
1123
|
+
self.set_status(Settings.gettext("msg_no_session"))
|
1113
1124
|
|
1114
1125
|
# 配置:命令行内容保留
|
1115
1126
|
if Settings.client["remain_last_input"]:
|
@@ -1196,9 +1207,19 @@ class PyMudApp:
|
|
1196
1207
|
|
1197
1208
|
async def run_async(self):
|
1198
1209
|
"以异步方式运行本程序"
|
1210
|
+
# 运行插件启动应用,放在此处,确保插件初始化在event_loop创建完成之后运行
|
1211
|
+
for plugin in self._plugins.values():
|
1212
|
+
if isinstance(plugin, Plugin):
|
1213
|
+
plugin.onAppInit(self)
|
1214
|
+
|
1199
1215
|
asyncio.create_task(self.onSystemTimerTick())
|
1200
1216
|
await self.app.run_async(set_exception_handler = False)
|
1201
1217
|
|
1218
|
+
# 当应用退出时,运行插件销毁应用
|
1219
|
+
for plugin in self._plugins.values():
|
1220
|
+
if isinstance(plugin, Plugin):
|
1221
|
+
plugin.onAppDestroy(self)
|
1222
|
+
|
1202
1223
|
def run(self):
|
1203
1224
|
"运行本程序"
|
1204
1225
|
#self.app.run(set_exception_handler = False)
|
@@ -1235,9 +1256,9 @@ class PyMudApp:
|
|
1235
1256
|
file_name = file[:-3]
|
1236
1257
|
plugin = Plugin(file_name, file_path)
|
1237
1258
|
self._plugins[plugin.name] = plugin
|
1238
|
-
plugin.onAppInit(self)
|
1259
|
+
# plugin.onAppInit(self)
|
1239
1260
|
except Exception as e:
|
1240
|
-
self.set_status(
|
1261
|
+
self.set_status(Settings.gettext("msg_plugin_load_error", file, e))
|
1241
1262
|
|
1242
1263
|
# 然后加载当前目录下的插件
|
1243
1264
|
current_dir = os.path.abspath(".")
|
@@ -1252,7 +1273,7 @@ class PyMudApp:
|
|
1252
1273
|
self._plugins[plugin.name] = plugin
|
1253
1274
|
plugin.onAppInit(self)
|
1254
1275
|
except Exception as e:
|
1255
|
-
self.set_status(
|
1276
|
+
self.set_status(Settings.gettext("msg_plugin_load_error", file, e))
|
1256
1277
|
|
1257
1278
|
def reload_plugin(self, plugin: Plugin):
|
1258
1279
|
"重新加载指定插件"
|