pymud 0.20.4__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/pkuxkx.py CHANGED
@@ -1,19 +1,16 @@
1
1
  # 示例脚本:如何在PyMud中玩PKUXKX
2
2
 
3
3
  import webbrowser
4
- from pymud import Alias, Trigger, SimpleCommand, Timer, SimpleTrigger, SimpleAlias
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)、命令(Command,本示例中使用了SimpleCommand子类)都进行了代码示例
8
- # 使用#load {filename}加载的配置文件中,若有一个类型名为Coniguration,则在#load操作时,会自动创建此类型;若没有Configuration类,则仅将文件引入
7
+ # 本示例脚本对PyMud支持的变量(Variable)、触发器(Trigger,包含单行与多行触发)、别名(Alias)、定时器(Timer)进行了代码示例
8
+ # 使用#load {filename}加载的配置文件中,若有一个类型继承自IConfig,则在#load操作时,会自动创建此类型;若没有继承自IConfig的类,则仅将文件引入
9
9
  # 例如,加载本文件指定的配置,则使用 #load pymud.pkuxkx即可
10
10
 
11
- # PyMud中,触发器Trigger、别名Alias、命令Command,都是匹配对象(MatchObject)的子类,使用同一种处理逻辑
12
- # 匹配对象,意味着有匹配的pattern。在匹配对象成功后,会调用对象的onSuccess方法
13
- # Trigger、Alias仅有成功,即仅onSuccess方法会被调用,该方法参数参考了MushClient,会传递name, line, wildcards三个参数,含义与MushClient相同;
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._triggers = {}
29
- self._commands = {}
30
- self._aliases = {}
31
- self._timers = {}
32
-
33
- self._initTriggers()
34
- self._initCommands()
35
- self._initAliases()
36
- self._initTimers()
37
-
38
- def _initTriggers(self):
39
- '''
40
- 初始化触发器。
41
- 本示例中创建了2个触发器,分别对应fullme的链接触发自动打开浏览器访问该网址,以及hpbrief命令的触发器
42
- '''
43
- # Trigger的构造函数中,给定的位置参数仅有session(会话)和pattern(匹配模式)两个,其他所有参数都是使用命名参数进行实现
44
- # 支持的命名参数详细可参见 BaseObject与MatchObject 的构造方法,此处简单列举
45
- # id : 唯一标识,不指定时自动生成
46
- # group : 组名,不指定时为空
47
- # enabled : 使能状态,默认True
48
- # priority : 优先级,默认100,越小越高
49
- # oneShot : 单次匹配,默认False
50
- # ignoreCase : 忽略大小写,默认False
51
- # isRegExp : 正则表达式模式,默认True
52
- # keepEval : 持续匹配,默认False
53
- # raw : 原始匹配方式,默认False。原始匹配方式下,不对VT100下的ANSI颜色进行解码,因此可以匹配颜色;正常匹配仅匹配文本
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
- def onTimer(self, name, *args, **kwargs):
54
+ @timer(2)
55
+ def onTimer(self, id, *args, **kwargs):
131
56
  self.session.info("每2秒都会打印本信息", "定时器测试")
132
57
 
133
- def ontri_hpbrief(self, name, line, wildcards):
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
- def oncmd_hpbrief(self, name, cmd, line, wildcards):
137
- # 为了节省服务器资源,应使用hpbrief来代替hp指令
138
- # 但是hpbrief指令的数据看起来太麻烦,所以将hpbrief的一串数字输出成类似hp的样式
139
- # ┌───个人状态────────────────────┬─────────────────────────────┐
140
- # │【精神】 1502 / 1502 [100%] │【精力】 4002 / 4002 (+ 0)
141
- # │【气血】 2500 / 2500 [100%] │【内力】 5324 / 5458 (+ 0)
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.text["world"],
320
+ Settings.gettext("world"),
307
321
  children=self.create_world_menus(),
308
322
  ),
309
323
  MenuItem(
310
- Settings.text["session"],
324
+ Settings.gettext("session"),
311
325
  children=[
312
- MenuItem(Settings.text["connect"], handler = self.act_connect),
313
- MenuItem(Settings.text["disconnect"], handler = self.act_discon),
314
- MenuItem(Settings.text["closesession"], handler = self.act_close_session),
315
- MenuItem(Settings.text["autoreconnect"], handler = self.act_autoreconnect),
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.text["nosplit"], handler = self.act_nosplit),
318
- MenuItem(Settings.text["echoinput"], handler = self.act_echoinput),
319
- MenuItem(Settings.text["beautify"], handler = self.act_beautify),
320
- MenuItem(Settings.text["copy"], handler = self.act_copy),
321
- MenuItem(Settings.text["copyraw"], handler = self.act_copyraw),
322
- MenuItem(Settings.text["clearsession"], handler = self.act_clearsession),
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.text["reloadconfig"], handler = self.act_reload),
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.text["help"],
353
+ Settings.gettext("help"),
340
354
  children=[
341
- MenuItem(Settings.text["about"], handler = self.act_about)
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.text["new_session"], handler = self.act_new))
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.text["show_log"], handler = self.show_logSelectDialog))
389
+ menus.append(MenuItem(Settings.gettext("show_log"), handler = self.show_logSelectDialog))
376
390
  menus.append(MenuItem("-", disabled=True))
377
- menus.append(MenuItem(Settings.text["exit"], handler=self.act_exit))
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("已复制:{}".format(selection))
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("已复制:行数{}".format(1 + erow - srow))
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("已复制:{}".format(line))
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("已复制:行数{}".format(1 + erow - srow))
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(f"错误!已存在一个名为{name}的会话,请更换名称再试.")
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
- head_line = " {}{}{}".format('记录文件名'.ljust(15), '文件大小'.rjust(16), '最后修改时间'.center(17))
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">警告</b>'), HTML('<style fg="red">当前会话 {0} 还处于连接状态,确认要关闭?</style>'.format(self.current_session.name)))
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"显示美化已{'打开' if val else '关闭'}!")
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"回显输入命令被设置为:{'打开' if val else '关闭'}")
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"自动重连被设置为:{'打开' if val else '关闭'}")
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">程序退出警告</b>'), HTML('<style fg="red">尚有 {0} 个会话 {1} 还处于连接状态,确认要关闭?</style>'.format(len(con_sessions), ", ".join(con_sessions))))
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.text["input_prompt"])
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 = "已连接:{:.0f}{:.0f}小时{:.0f}{:.0f}".format(days, hours, mins, sec)
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 = "已连接:{:.0f}小时{:.0f}{:.0f}".format(hours, mins, sec)
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 = "已连接:{:.0f}{:.0f}".format(mins, sec)
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 = "已连接:{:.0f}".format(sec)
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 = f'通过单一参数快速创建会话时,要使用 group.name 形式,如 #session pkuxkx.newstart'
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"命令行键入: {cmd_line}\n")
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("当前没有正在运行的session.")
1123
+ self.set_status(Settings.gettext("msg_no_session"))
1113
1124
 
1114
1125
  # 配置:命令行内容保留
1115
1126
  if Settings.client["remain_last_input"]:
@@ -1247,7 +1258,7 @@ class PyMudApp:
1247
1258
  self._plugins[plugin.name] = plugin
1248
1259
  # plugin.onAppInit(self)
1249
1260
  except Exception as e:
1250
- self.set_status(f"文件: {plugins_dir}\\{file} 不是一个合法的插件文件,加载错误,信息为: {e}")
1261
+ self.set_status(Settings.gettext("msg_plugin_load_error", file, e))
1251
1262
 
1252
1263
  # 然后加载当前目录下的插件
1253
1264
  current_dir = os.path.abspath(".")
@@ -1262,7 +1273,7 @@ class PyMudApp:
1262
1273
  self._plugins[plugin.name] = plugin
1263
1274
  plugin.onAppInit(self)
1264
1275
  except Exception as e:
1265
- self.set_status(f"文件: {plugins_dir}\\{file} 不是一个合法的插件文件. 加载错误,信息为: {e}")
1276
+ self.set_status(Settings.gettext("msg_plugin_load_error", file, e))
1266
1277
 
1267
1278
  def reload_plugin(self, plugin: Plugin):
1268
1279
  "重新加载指定插件"