kotonebot 0.5.0__py3-none-any.whl → 0.6.0__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.
- kotonebot/__init__.py +39 -39
- kotonebot/backend/bot.py +312 -312
- kotonebot/backend/color.py +525 -525
- kotonebot/backend/context/__init__.py +3 -3
- kotonebot/backend/context/context.py +1002 -1002
- kotonebot/backend/context/task_action.py +183 -183
- kotonebot/backend/core.py +86 -129
- kotonebot/backend/debug/entry.py +89 -89
- kotonebot/backend/debug/mock.py +78 -78
- kotonebot/backend/debug/server.py +222 -222
- kotonebot/backend/debug/vars.py +351 -351
- kotonebot/backend/dispatch.py +227 -227
- kotonebot/backend/flow_controller.py +196 -196
- kotonebot/backend/image.py +36 -5
- kotonebot/backend/loop.py +222 -208
- kotonebot/backend/ocr.py +535 -535
- kotonebot/backend/preprocessor.py +103 -103
- kotonebot/client/__init__.py +9 -9
- kotonebot/client/device.py +369 -529
- kotonebot/client/fast_screenshot.py +377 -377
- kotonebot/client/host/__init__.py +43 -43
- kotonebot/client/host/adb_common.py +101 -107
- kotonebot/client/host/custom.py +118 -118
- kotonebot/client/host/leidian_host.py +196 -196
- kotonebot/client/host/mumu12_host.py +353 -353
- kotonebot/client/host/protocol.py +214 -214
- kotonebot/client/host/windows_common.py +58 -58
- kotonebot/client/implements/__init__.py +65 -70
- kotonebot/client/implements/adb.py +89 -89
- kotonebot/client/implements/nemu_ipc/__init__.py +11 -11
- kotonebot/client/implements/nemu_ipc/external_renderer_ipc.py +284 -284
- kotonebot/client/implements/nemu_ipc/nemu_ipc.py +327 -327
- kotonebot/client/implements/remote_windows.py +188 -188
- kotonebot/client/implements/uiautomator2.py +85 -85
- kotonebot/client/implements/windows.py +176 -176
- kotonebot/client/protocol.py +69 -69
- kotonebot/client/registration.py +24 -24
- kotonebot/client/scaler.py +467 -0
- kotonebot/config/base_config.py +96 -96
- kotonebot/config/config.py +61 -0
- kotonebot/config/manager.py +36 -36
- kotonebot/core/__init__.py +13 -0
- kotonebot/core/entities/base.py +182 -0
- kotonebot/core/entities/compound.py +75 -0
- kotonebot/core/entities/ocr.py +117 -0
- kotonebot/core/entities/template_match.py +198 -0
- kotonebot/devtools/__init__.py +42 -0
- kotonebot/devtools/cli/__init__.py +6 -0
- kotonebot/devtools/cli/main.py +53 -0
- kotonebot/{tools → devtools}/mirror.py +354 -354
- kotonebot/devtools/project/project.py +41 -0
- kotonebot/devtools/project/scanner.py +202 -0
- kotonebot/devtools/project/schema.py +99 -0
- kotonebot/devtools/resgen/__init__.py +42 -0
- kotonebot/devtools/resgen/codegen.py +331 -0
- kotonebot/devtools/resgen/core.py +94 -0
- kotonebot/devtools/resgen/parsers.py +360 -0
- kotonebot/devtools/resgen/utils.py +158 -0
- kotonebot/devtools/resgen/validation.py +115 -0
- kotonebot/devtools/web/dist/assets/bootstrap-icons-BOrJxbIo.woff +0 -0
- kotonebot/devtools/web/dist/assets/bootstrap-icons-BtvjY1KL.woff2 +0 -0
- kotonebot/devtools/web/dist/assets/ext-language_tools-CD021WJ2.js +2577 -0
- kotonebot/devtools/web/dist/assets/index-B_m5f2LF.js +2836 -0
- kotonebot/devtools/web/dist/assets/index-BlEDyGGa.css +9 -0
- kotonebot/devtools/web/dist/assets/language-client-C9muzqaq.js +128 -0
- kotonebot/devtools/web/dist/assets/mode-python-CtHp76XS.js +476 -0
- kotonebot/devtools/web/dist/icons/symbol-class.svg +3 -0
- kotonebot/devtools/web/dist/icons/symbol-file.svg +3 -0
- kotonebot/devtools/web/dist/icons/symbol-method.svg +3 -0
- kotonebot/devtools/web/dist/index.html +25 -0
- kotonebot/devtools/web/server/__init__.py +0 -0
- kotonebot/devtools/web/server/rest_api.py +217 -0
- kotonebot/devtools/web/server/server.py +85 -0
- kotonebot/errors.py +76 -76
- kotonebot/interop/win/__init__.py +11 -9
- kotonebot/interop/win/_mouse.py +310 -310
- kotonebot/interop/win/message_box.py +313 -313
- kotonebot/interop/win/reg.py +37 -37
- kotonebot/interop/win/shake_mouse.py +224 -0
- kotonebot/interop/win/shortcut.py +43 -43
- kotonebot/interop/win/task_dialog.py +513 -513
- kotonebot/logging/__init__.py +2 -2
- kotonebot/logging/log.py +17 -17
- kotonebot/primitives/__init__.py +19 -17
- kotonebot/primitives/geometry.py +1067 -862
- kotonebot/primitives/visual.py +143 -63
- kotonebot/ui/file_host/sensio.py +36 -36
- kotonebot/ui/file_host/tmp_send.py +54 -54
- kotonebot/ui/pushkit/__init__.py +3 -3
- kotonebot/ui/pushkit/image_host.py +88 -88
- kotonebot/ui/pushkit/protocol.py +13 -13
- kotonebot/ui/pushkit/wxpusher.py +54 -54
- kotonebot/ui/user.py +148 -148
- kotonebot/util.py +436 -436
- {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/METADATA +84 -82
- kotonebot-0.6.0.dist-info/RECORD +105 -0
- kotonebot-0.6.0.dist-info/entry_points.txt +2 -0
- {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/licenses/LICENSE +673 -673
- kotonebot/client/implements/adb_raw.py +0 -163
- kotonebot-0.5.0.dist-info/RECORD +0 -71
- /kotonebot/{tools → devtools/project}/__init__.py +0 -0
- {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/WHEEL +0 -0
- {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,314 +1,314 @@
|
|
|
1
|
-
import ctypes
|
|
2
|
-
from typing import Optional, Literal, List, overload
|
|
3
|
-
from typing_extensions import assert_never
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# 按钮常量
|
|
7
|
-
MB_OK = 0x00000000
|
|
8
|
-
MB_OKCANCEL = 0x00000001
|
|
9
|
-
MB_ABORTRETRYIGNORE = 0x00000002
|
|
10
|
-
MB_YESNOCANCEL = 0x00000003
|
|
11
|
-
MB_YESNO = 0x00000004
|
|
12
|
-
MB_RETRYCANCEL = 0x00000005
|
|
13
|
-
MB_CANCELTRYCONTINUE = 0x00000006
|
|
14
|
-
|
|
15
|
-
# 图标常量
|
|
16
|
-
MB_ICONSTOP = 0x00000010
|
|
17
|
-
MB_ICONERROR = 0x00000010
|
|
18
|
-
MB_ICONQUESTION = 0x00000020
|
|
19
|
-
MB_ICONWARNING = 0x00000030
|
|
20
|
-
MB_ICONINFORMATION = 0x00000040
|
|
21
|
-
|
|
22
|
-
# 默认按钮常量
|
|
23
|
-
MB_DEFBUTTON1 = 0x00000000
|
|
24
|
-
MB_DEFBUTTON2 = 0x00000100
|
|
25
|
-
MB_DEFBUTTON3 = 0x00000200
|
|
26
|
-
MB_DEFBUTTON4 = 0x00000300
|
|
27
|
-
|
|
28
|
-
# 模态常量
|
|
29
|
-
MB_APPLMODAL = 0x00000000
|
|
30
|
-
MB_SYSTEMMODAL = 0x00001000
|
|
31
|
-
MB_TASKMODAL = 0x00002000
|
|
32
|
-
|
|
33
|
-
# 其他选项
|
|
34
|
-
MB_HELP = 0x00004000
|
|
35
|
-
MB_NOFOCUS = 0x00008000
|
|
36
|
-
MB_SETFOREGROUND = 0x00010000
|
|
37
|
-
MB_DEFAULT_DESKTOP_ONLY = 0x00020000
|
|
38
|
-
MB_TOPMOST = 0x00040000
|
|
39
|
-
MB_RIGHT = 0x00080000
|
|
40
|
-
MB_RTLREADING = 0x00100000
|
|
41
|
-
MB_SERVICE_NOTIFICATION = 0x00200000
|
|
42
|
-
|
|
43
|
-
# 返回值常量
|
|
44
|
-
IDOK = 1
|
|
45
|
-
IDCANCEL = 2
|
|
46
|
-
IDABORT = 3
|
|
47
|
-
IDRETRY = 4
|
|
48
|
-
IDIGNORE = 5
|
|
49
|
-
IDYES = 6
|
|
50
|
-
IDNO = 7
|
|
51
|
-
IDCLOSE = 8
|
|
52
|
-
IDHELP = 9
|
|
53
|
-
IDTRYAGAIN = 10
|
|
54
|
-
IDCONTINUE = 11
|
|
55
|
-
|
|
56
|
-
# 为清晰起见,定义类型别名
|
|
57
|
-
ButtonsType = Literal['ok', 'ok_cancel', 'abort_retry_ignore', 'yes_no_cancel', 'yes_no', 'retry_cancel', 'cancel_try_continue']
|
|
58
|
-
IconType = Optional[Literal['stop', 'error', 'question', 'warning', 'information']]
|
|
59
|
-
DefaultButtonType = Literal['button1', 'button2', 'button3', 'button4']
|
|
60
|
-
ModalType = Literal['application', 'system', 'task']
|
|
61
|
-
OptionsType = Optional[List[Literal['help', 'no_focus', 'set_foreground', 'default_desktop_only', 'topmost', 'right', 'rtl_reading', 'service_notification']]]
|
|
62
|
-
ReturnType = Literal['ok', 'cancel', 'abort', 'retry', 'ignore', 'yes', 'no', 'close', 'help', 'try_again', 'continue']
|
|
63
|
-
|
|
64
|
-
user32 = ctypes.windll.user32
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@overload
|
|
68
|
-
def message_box(
|
|
69
|
-
hWnd: Optional[int],
|
|
70
|
-
text: str,
|
|
71
|
-
caption: str,
|
|
72
|
-
buttons: Literal['ok'] = 'ok',
|
|
73
|
-
icon: IconType = None,
|
|
74
|
-
default_button: DefaultButtonType = 'button1',
|
|
75
|
-
modal: ModalType = 'application',
|
|
76
|
-
options: OptionsType = None
|
|
77
|
-
) -> Literal['ok']: ...
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
@overload
|
|
81
|
-
def message_box(
|
|
82
|
-
hWnd: Optional[int],
|
|
83
|
-
text: str,
|
|
84
|
-
caption: str,
|
|
85
|
-
buttons: Literal['ok_cancel'],
|
|
86
|
-
icon: IconType = None,
|
|
87
|
-
default_button: DefaultButtonType = 'button1',
|
|
88
|
-
modal: ModalType = 'application',
|
|
89
|
-
options: OptionsType = None
|
|
90
|
-
) -> Literal['ok', 'cancel']: ...
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
@overload
|
|
94
|
-
def message_box(
|
|
95
|
-
hWnd: Optional[int],
|
|
96
|
-
text: str,
|
|
97
|
-
caption: str,
|
|
98
|
-
buttons: Literal['abort_retry_ignore'],
|
|
99
|
-
icon: IconType = None,
|
|
100
|
-
default_button: DefaultButtonType = 'button1',
|
|
101
|
-
modal: ModalType = 'application',
|
|
102
|
-
options: OptionsType = None
|
|
103
|
-
) -> Literal['abort', 'retry', 'ignore']: ...
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@overload
|
|
107
|
-
def message_box(
|
|
108
|
-
hWnd: Optional[int],
|
|
109
|
-
text: str,
|
|
110
|
-
caption: str,
|
|
111
|
-
buttons: Literal['yes_no_cancel'],
|
|
112
|
-
icon: IconType = None,
|
|
113
|
-
default_button: DefaultButtonType = 'button1',
|
|
114
|
-
modal: ModalType = 'application',
|
|
115
|
-
options: OptionsType = None
|
|
116
|
-
) -> Literal['yes', 'no', 'cancel']: ...
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
@overload
|
|
120
|
-
def message_box(
|
|
121
|
-
hWnd: Optional[int],
|
|
122
|
-
text: str,
|
|
123
|
-
caption: str,
|
|
124
|
-
buttons: Literal['yes_no'],
|
|
125
|
-
icon: IconType = None,
|
|
126
|
-
default_button: DefaultButtonType = 'button1',
|
|
127
|
-
modal: ModalType = 'application',
|
|
128
|
-
options: OptionsType = None
|
|
129
|
-
) -> Literal['yes', 'no']: ...
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
@overload
|
|
133
|
-
def message_box(
|
|
134
|
-
hWnd: Optional[int],
|
|
135
|
-
text: str,
|
|
136
|
-
caption: str,
|
|
137
|
-
buttons: Literal['retry_cancel'],
|
|
138
|
-
icon: IconType = None,
|
|
139
|
-
default_button: DefaultButtonType = 'button1',
|
|
140
|
-
modal: ModalType = 'application',
|
|
141
|
-
options: OptionsType = None
|
|
142
|
-
) -> Literal['retry', 'cancel']: ...
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
@overload
|
|
146
|
-
def message_box(
|
|
147
|
-
hWnd: Optional[int],
|
|
148
|
-
text: str,
|
|
149
|
-
caption: str,
|
|
150
|
-
buttons: Literal['cancel_try_continue'],
|
|
151
|
-
icon: IconType = None,
|
|
152
|
-
default_button: DefaultButtonType = 'button1',
|
|
153
|
-
modal: ModalType = 'application',
|
|
154
|
-
options: OptionsType = None
|
|
155
|
-
) -> Literal['cancel', 'try_again', 'continue']: ...
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def message_box(
|
|
159
|
-
hWnd: Optional[int],
|
|
160
|
-
text: str,
|
|
161
|
-
caption: str,
|
|
162
|
-
buttons: ButtonsType = 'ok',
|
|
163
|
-
icon: IconType = None,
|
|
164
|
-
default_button: DefaultButtonType = 'button1',
|
|
165
|
-
modal: ModalType = 'application',
|
|
166
|
-
options: OptionsType = None
|
|
167
|
-
) -> ReturnType:
|
|
168
|
-
"""
|
|
169
|
-
显示消息框。
|
|
170
|
-
|
|
171
|
-
:param hWnd: 所属窗口的句柄。可以为 None。
|
|
172
|
-
:param text: 要显示的消息。
|
|
173
|
-
:param caption: 消息框的标题。
|
|
174
|
-
:param buttons: 要显示的按钮。
|
|
175
|
-
:param icon: 要显示的图标。
|
|
176
|
-
:param default_button: 默认按钮。
|
|
177
|
-
:param modal: 消息框的模态。
|
|
178
|
-
:param options: 其他杂项选项列表。
|
|
179
|
-
:return: 表示用户点击的按钮的字符串。
|
|
180
|
-
"""
|
|
181
|
-
uType = 0
|
|
182
|
-
|
|
183
|
-
# --- 按钮类型 ---
|
|
184
|
-
match buttons:
|
|
185
|
-
case 'ok':
|
|
186
|
-
uType |= MB_OK
|
|
187
|
-
case 'ok_cancel':
|
|
188
|
-
uType |= MB_OKCANCEL
|
|
189
|
-
case 'abort_retry_ignore':
|
|
190
|
-
uType |= MB_ABORTRETRYIGNORE
|
|
191
|
-
case 'yes_no_cancel':
|
|
192
|
-
uType |= MB_YESNOCANCEL
|
|
193
|
-
case 'yes_no':
|
|
194
|
-
uType |= MB_YESNO
|
|
195
|
-
case 'retry_cancel':
|
|
196
|
-
uType |= MB_RETRYCANCEL
|
|
197
|
-
case 'cancel_try_continue':
|
|
198
|
-
uType |= MB_CANCELTRYCONTINUE
|
|
199
|
-
case _:
|
|
200
|
-
assert_never(buttons)
|
|
201
|
-
|
|
202
|
-
# --- 图标类型 ---
|
|
203
|
-
if icon:
|
|
204
|
-
match icon:
|
|
205
|
-
case 'stop' | 'error':
|
|
206
|
-
uType |= MB_ICONSTOP
|
|
207
|
-
case 'question':
|
|
208
|
-
uType |= MB_ICONQUESTION
|
|
209
|
-
case 'warning':
|
|
210
|
-
uType |= MB_ICONWARNING
|
|
211
|
-
case 'information':
|
|
212
|
-
uType |= MB_ICONINFORMATION
|
|
213
|
-
case _:
|
|
214
|
-
assert_never(icon)
|
|
215
|
-
|
|
216
|
-
# --- 默认按钮 ---
|
|
217
|
-
match default_button:
|
|
218
|
-
case 'button1':
|
|
219
|
-
uType |= MB_DEFBUTTON1
|
|
220
|
-
case 'button2':
|
|
221
|
-
uType |= MB_DEFBUTTON2
|
|
222
|
-
case 'button3':
|
|
223
|
-
uType |= MB_DEFBUTTON3
|
|
224
|
-
case 'button4':
|
|
225
|
-
uType |= MB_DEFBUTTON4
|
|
226
|
-
case _:
|
|
227
|
-
assert_never(default_button)
|
|
228
|
-
|
|
229
|
-
# --- 模态 ---
|
|
230
|
-
match modal:
|
|
231
|
-
case 'application':
|
|
232
|
-
uType |= MB_APPLMODAL
|
|
233
|
-
case 'system':
|
|
234
|
-
uType |= MB_SYSTEMMODAL
|
|
235
|
-
case 'task':
|
|
236
|
-
uType |= MB_TASKMODAL
|
|
237
|
-
case _:
|
|
238
|
-
assert_never(modal)
|
|
239
|
-
|
|
240
|
-
# --- 其他选项 ---
|
|
241
|
-
if options:
|
|
242
|
-
for option in options:
|
|
243
|
-
match option:
|
|
244
|
-
case 'help':
|
|
245
|
-
uType |= MB_HELP
|
|
246
|
-
case 'no_focus':
|
|
247
|
-
uType |= MB_NOFOCUS
|
|
248
|
-
case 'set_foreground':
|
|
249
|
-
uType |= MB_SETFOREGROUND
|
|
250
|
-
case 'default_desktop_only':
|
|
251
|
-
uType |= MB_DEFAULT_DESKTOP_ONLY
|
|
252
|
-
case 'topmost':
|
|
253
|
-
uType |= MB_TOPMOST
|
|
254
|
-
case 'right':
|
|
255
|
-
uType |= MB_RIGHT
|
|
256
|
-
case 'rtl_reading':
|
|
257
|
-
uType |= MB_RTLREADING
|
|
258
|
-
case 'service_notification':
|
|
259
|
-
uType |= MB_SERVICE_NOTIFICATION
|
|
260
|
-
case _:
|
|
261
|
-
assert_never(option)
|
|
262
|
-
|
|
263
|
-
result = user32.MessageBoxW(hWnd, text, caption, uType)
|
|
264
|
-
|
|
265
|
-
match result:
|
|
266
|
-
case 1: # IDOK
|
|
267
|
-
return 'ok'
|
|
268
|
-
case 2: # IDCANCEL
|
|
269
|
-
return 'cancel'
|
|
270
|
-
case 3: # IDABORT
|
|
271
|
-
return 'abort'
|
|
272
|
-
case 4: # IDRETRY
|
|
273
|
-
return 'retry'
|
|
274
|
-
case 5: # IDIGNORE
|
|
275
|
-
return 'ignore'
|
|
276
|
-
case 6: # IDYES
|
|
277
|
-
return 'yes'
|
|
278
|
-
case 7: # IDNO
|
|
279
|
-
return 'no'
|
|
280
|
-
case 8: # IDCLOSE
|
|
281
|
-
return 'close'
|
|
282
|
-
case 9: # IDHELP
|
|
283
|
-
return 'help'
|
|
284
|
-
case 10: # IDTRYAGAIN
|
|
285
|
-
return 'try_again'
|
|
286
|
-
case 11: # IDCONTINUE
|
|
287
|
-
return 'continue'
|
|
288
|
-
case _:
|
|
289
|
-
# 对于标准消息框,不应发生这种情况
|
|
290
|
-
raise RuntimeError(f"Unknown MessageBox return code: {result}")
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if __name__ == '__main__':
|
|
294
|
-
# 示例用法
|
|
295
|
-
response = message_box(
|
|
296
|
-
None,
|
|
297
|
-
"是否要退出程序?",
|
|
298
|
-
"确认",
|
|
299
|
-
buttons='yes_no',
|
|
300
|
-
icon='question'
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
if response == 'yes':
|
|
304
|
-
print("程序退出。")
|
|
305
|
-
else:
|
|
306
|
-
print("程序继续运行。")
|
|
307
|
-
|
|
308
|
-
message_box(
|
|
309
|
-
None,
|
|
310
|
-
"操作已完成。",
|
|
311
|
-
"通知",
|
|
312
|
-
buttons='ok',
|
|
313
|
-
icon='information'
|
|
1
|
+
import ctypes
|
|
2
|
+
from typing import Optional, Literal, List, overload
|
|
3
|
+
from typing_extensions import assert_never
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# 按钮常量
|
|
7
|
+
MB_OK = 0x00000000
|
|
8
|
+
MB_OKCANCEL = 0x00000001
|
|
9
|
+
MB_ABORTRETRYIGNORE = 0x00000002
|
|
10
|
+
MB_YESNOCANCEL = 0x00000003
|
|
11
|
+
MB_YESNO = 0x00000004
|
|
12
|
+
MB_RETRYCANCEL = 0x00000005
|
|
13
|
+
MB_CANCELTRYCONTINUE = 0x00000006
|
|
14
|
+
|
|
15
|
+
# 图标常量
|
|
16
|
+
MB_ICONSTOP = 0x00000010
|
|
17
|
+
MB_ICONERROR = 0x00000010
|
|
18
|
+
MB_ICONQUESTION = 0x00000020
|
|
19
|
+
MB_ICONWARNING = 0x00000030
|
|
20
|
+
MB_ICONINFORMATION = 0x00000040
|
|
21
|
+
|
|
22
|
+
# 默认按钮常量
|
|
23
|
+
MB_DEFBUTTON1 = 0x00000000
|
|
24
|
+
MB_DEFBUTTON2 = 0x00000100
|
|
25
|
+
MB_DEFBUTTON3 = 0x00000200
|
|
26
|
+
MB_DEFBUTTON4 = 0x00000300
|
|
27
|
+
|
|
28
|
+
# 模态常量
|
|
29
|
+
MB_APPLMODAL = 0x00000000
|
|
30
|
+
MB_SYSTEMMODAL = 0x00001000
|
|
31
|
+
MB_TASKMODAL = 0x00002000
|
|
32
|
+
|
|
33
|
+
# 其他选项
|
|
34
|
+
MB_HELP = 0x00004000
|
|
35
|
+
MB_NOFOCUS = 0x00008000
|
|
36
|
+
MB_SETFOREGROUND = 0x00010000
|
|
37
|
+
MB_DEFAULT_DESKTOP_ONLY = 0x00020000
|
|
38
|
+
MB_TOPMOST = 0x00040000
|
|
39
|
+
MB_RIGHT = 0x00080000
|
|
40
|
+
MB_RTLREADING = 0x00100000
|
|
41
|
+
MB_SERVICE_NOTIFICATION = 0x00200000
|
|
42
|
+
|
|
43
|
+
# 返回值常量
|
|
44
|
+
IDOK = 1
|
|
45
|
+
IDCANCEL = 2
|
|
46
|
+
IDABORT = 3
|
|
47
|
+
IDRETRY = 4
|
|
48
|
+
IDIGNORE = 5
|
|
49
|
+
IDYES = 6
|
|
50
|
+
IDNO = 7
|
|
51
|
+
IDCLOSE = 8
|
|
52
|
+
IDHELP = 9
|
|
53
|
+
IDTRYAGAIN = 10
|
|
54
|
+
IDCONTINUE = 11
|
|
55
|
+
|
|
56
|
+
# 为清晰起见,定义类型别名
|
|
57
|
+
ButtonsType = Literal['ok', 'ok_cancel', 'abort_retry_ignore', 'yes_no_cancel', 'yes_no', 'retry_cancel', 'cancel_try_continue']
|
|
58
|
+
IconType = Optional[Literal['stop', 'error', 'question', 'warning', 'information']]
|
|
59
|
+
DefaultButtonType = Literal['button1', 'button2', 'button3', 'button4']
|
|
60
|
+
ModalType = Literal['application', 'system', 'task']
|
|
61
|
+
OptionsType = Optional[List[Literal['help', 'no_focus', 'set_foreground', 'default_desktop_only', 'topmost', 'right', 'rtl_reading', 'service_notification']]]
|
|
62
|
+
ReturnType = Literal['ok', 'cancel', 'abort', 'retry', 'ignore', 'yes', 'no', 'close', 'help', 'try_again', 'continue']
|
|
63
|
+
|
|
64
|
+
user32 = ctypes.windll.user32
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@overload
|
|
68
|
+
def message_box(
|
|
69
|
+
hWnd: Optional[int],
|
|
70
|
+
text: str,
|
|
71
|
+
caption: str,
|
|
72
|
+
buttons: Literal['ok'] = 'ok',
|
|
73
|
+
icon: IconType = None,
|
|
74
|
+
default_button: DefaultButtonType = 'button1',
|
|
75
|
+
modal: ModalType = 'application',
|
|
76
|
+
options: OptionsType = None
|
|
77
|
+
) -> Literal['ok']: ...
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@overload
|
|
81
|
+
def message_box(
|
|
82
|
+
hWnd: Optional[int],
|
|
83
|
+
text: str,
|
|
84
|
+
caption: str,
|
|
85
|
+
buttons: Literal['ok_cancel'],
|
|
86
|
+
icon: IconType = None,
|
|
87
|
+
default_button: DefaultButtonType = 'button1',
|
|
88
|
+
modal: ModalType = 'application',
|
|
89
|
+
options: OptionsType = None
|
|
90
|
+
) -> Literal['ok', 'cancel']: ...
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@overload
|
|
94
|
+
def message_box(
|
|
95
|
+
hWnd: Optional[int],
|
|
96
|
+
text: str,
|
|
97
|
+
caption: str,
|
|
98
|
+
buttons: Literal['abort_retry_ignore'],
|
|
99
|
+
icon: IconType = None,
|
|
100
|
+
default_button: DefaultButtonType = 'button1',
|
|
101
|
+
modal: ModalType = 'application',
|
|
102
|
+
options: OptionsType = None
|
|
103
|
+
) -> Literal['abort', 'retry', 'ignore']: ...
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@overload
|
|
107
|
+
def message_box(
|
|
108
|
+
hWnd: Optional[int],
|
|
109
|
+
text: str,
|
|
110
|
+
caption: str,
|
|
111
|
+
buttons: Literal['yes_no_cancel'],
|
|
112
|
+
icon: IconType = None,
|
|
113
|
+
default_button: DefaultButtonType = 'button1',
|
|
114
|
+
modal: ModalType = 'application',
|
|
115
|
+
options: OptionsType = None
|
|
116
|
+
) -> Literal['yes', 'no', 'cancel']: ...
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@overload
|
|
120
|
+
def message_box(
|
|
121
|
+
hWnd: Optional[int],
|
|
122
|
+
text: str,
|
|
123
|
+
caption: str,
|
|
124
|
+
buttons: Literal['yes_no'],
|
|
125
|
+
icon: IconType = None,
|
|
126
|
+
default_button: DefaultButtonType = 'button1',
|
|
127
|
+
modal: ModalType = 'application',
|
|
128
|
+
options: OptionsType = None
|
|
129
|
+
) -> Literal['yes', 'no']: ...
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@overload
|
|
133
|
+
def message_box(
|
|
134
|
+
hWnd: Optional[int],
|
|
135
|
+
text: str,
|
|
136
|
+
caption: str,
|
|
137
|
+
buttons: Literal['retry_cancel'],
|
|
138
|
+
icon: IconType = None,
|
|
139
|
+
default_button: DefaultButtonType = 'button1',
|
|
140
|
+
modal: ModalType = 'application',
|
|
141
|
+
options: OptionsType = None
|
|
142
|
+
) -> Literal['retry', 'cancel']: ...
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@overload
|
|
146
|
+
def message_box(
|
|
147
|
+
hWnd: Optional[int],
|
|
148
|
+
text: str,
|
|
149
|
+
caption: str,
|
|
150
|
+
buttons: Literal['cancel_try_continue'],
|
|
151
|
+
icon: IconType = None,
|
|
152
|
+
default_button: DefaultButtonType = 'button1',
|
|
153
|
+
modal: ModalType = 'application',
|
|
154
|
+
options: OptionsType = None
|
|
155
|
+
) -> Literal['cancel', 'try_again', 'continue']: ...
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def message_box(
|
|
159
|
+
hWnd: Optional[int],
|
|
160
|
+
text: str,
|
|
161
|
+
caption: str,
|
|
162
|
+
buttons: ButtonsType = 'ok',
|
|
163
|
+
icon: IconType = None,
|
|
164
|
+
default_button: DefaultButtonType = 'button1',
|
|
165
|
+
modal: ModalType = 'application',
|
|
166
|
+
options: OptionsType = None
|
|
167
|
+
) -> ReturnType:
|
|
168
|
+
"""
|
|
169
|
+
显示消息框。
|
|
170
|
+
|
|
171
|
+
:param hWnd: 所属窗口的句柄。可以为 None。
|
|
172
|
+
:param text: 要显示的消息。
|
|
173
|
+
:param caption: 消息框的标题。
|
|
174
|
+
:param buttons: 要显示的按钮。
|
|
175
|
+
:param icon: 要显示的图标。
|
|
176
|
+
:param default_button: 默认按钮。
|
|
177
|
+
:param modal: 消息框的模态。
|
|
178
|
+
:param options: 其他杂项选项列表。
|
|
179
|
+
:return: 表示用户点击的按钮的字符串。
|
|
180
|
+
"""
|
|
181
|
+
uType = 0
|
|
182
|
+
|
|
183
|
+
# --- 按钮类型 ---
|
|
184
|
+
match buttons:
|
|
185
|
+
case 'ok':
|
|
186
|
+
uType |= MB_OK
|
|
187
|
+
case 'ok_cancel':
|
|
188
|
+
uType |= MB_OKCANCEL
|
|
189
|
+
case 'abort_retry_ignore':
|
|
190
|
+
uType |= MB_ABORTRETRYIGNORE
|
|
191
|
+
case 'yes_no_cancel':
|
|
192
|
+
uType |= MB_YESNOCANCEL
|
|
193
|
+
case 'yes_no':
|
|
194
|
+
uType |= MB_YESNO
|
|
195
|
+
case 'retry_cancel':
|
|
196
|
+
uType |= MB_RETRYCANCEL
|
|
197
|
+
case 'cancel_try_continue':
|
|
198
|
+
uType |= MB_CANCELTRYCONTINUE
|
|
199
|
+
case _:
|
|
200
|
+
assert_never(buttons)
|
|
201
|
+
|
|
202
|
+
# --- 图标类型 ---
|
|
203
|
+
if icon:
|
|
204
|
+
match icon:
|
|
205
|
+
case 'stop' | 'error':
|
|
206
|
+
uType |= MB_ICONSTOP
|
|
207
|
+
case 'question':
|
|
208
|
+
uType |= MB_ICONQUESTION
|
|
209
|
+
case 'warning':
|
|
210
|
+
uType |= MB_ICONWARNING
|
|
211
|
+
case 'information':
|
|
212
|
+
uType |= MB_ICONINFORMATION
|
|
213
|
+
case _:
|
|
214
|
+
assert_never(icon)
|
|
215
|
+
|
|
216
|
+
# --- 默认按钮 ---
|
|
217
|
+
match default_button:
|
|
218
|
+
case 'button1':
|
|
219
|
+
uType |= MB_DEFBUTTON1
|
|
220
|
+
case 'button2':
|
|
221
|
+
uType |= MB_DEFBUTTON2
|
|
222
|
+
case 'button3':
|
|
223
|
+
uType |= MB_DEFBUTTON3
|
|
224
|
+
case 'button4':
|
|
225
|
+
uType |= MB_DEFBUTTON4
|
|
226
|
+
case _:
|
|
227
|
+
assert_never(default_button)
|
|
228
|
+
|
|
229
|
+
# --- 模态 ---
|
|
230
|
+
match modal:
|
|
231
|
+
case 'application':
|
|
232
|
+
uType |= MB_APPLMODAL
|
|
233
|
+
case 'system':
|
|
234
|
+
uType |= MB_SYSTEMMODAL
|
|
235
|
+
case 'task':
|
|
236
|
+
uType |= MB_TASKMODAL
|
|
237
|
+
case _:
|
|
238
|
+
assert_never(modal)
|
|
239
|
+
|
|
240
|
+
# --- 其他选项 ---
|
|
241
|
+
if options:
|
|
242
|
+
for option in options:
|
|
243
|
+
match option:
|
|
244
|
+
case 'help':
|
|
245
|
+
uType |= MB_HELP
|
|
246
|
+
case 'no_focus':
|
|
247
|
+
uType |= MB_NOFOCUS
|
|
248
|
+
case 'set_foreground':
|
|
249
|
+
uType |= MB_SETFOREGROUND
|
|
250
|
+
case 'default_desktop_only':
|
|
251
|
+
uType |= MB_DEFAULT_DESKTOP_ONLY
|
|
252
|
+
case 'topmost':
|
|
253
|
+
uType |= MB_TOPMOST
|
|
254
|
+
case 'right':
|
|
255
|
+
uType |= MB_RIGHT
|
|
256
|
+
case 'rtl_reading':
|
|
257
|
+
uType |= MB_RTLREADING
|
|
258
|
+
case 'service_notification':
|
|
259
|
+
uType |= MB_SERVICE_NOTIFICATION
|
|
260
|
+
case _:
|
|
261
|
+
assert_never(option)
|
|
262
|
+
|
|
263
|
+
result = user32.MessageBoxW(hWnd, text, caption, uType)
|
|
264
|
+
|
|
265
|
+
match result:
|
|
266
|
+
case 1: # IDOK
|
|
267
|
+
return 'ok'
|
|
268
|
+
case 2: # IDCANCEL
|
|
269
|
+
return 'cancel'
|
|
270
|
+
case 3: # IDABORT
|
|
271
|
+
return 'abort'
|
|
272
|
+
case 4: # IDRETRY
|
|
273
|
+
return 'retry'
|
|
274
|
+
case 5: # IDIGNORE
|
|
275
|
+
return 'ignore'
|
|
276
|
+
case 6: # IDYES
|
|
277
|
+
return 'yes'
|
|
278
|
+
case 7: # IDNO
|
|
279
|
+
return 'no'
|
|
280
|
+
case 8: # IDCLOSE
|
|
281
|
+
return 'close'
|
|
282
|
+
case 9: # IDHELP
|
|
283
|
+
return 'help'
|
|
284
|
+
case 10: # IDTRYAGAIN
|
|
285
|
+
return 'try_again'
|
|
286
|
+
case 11: # IDCONTINUE
|
|
287
|
+
return 'continue'
|
|
288
|
+
case _:
|
|
289
|
+
# 对于标准消息框,不应发生这种情况
|
|
290
|
+
raise RuntimeError(f"Unknown MessageBox return code: {result}")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
if __name__ == '__main__':
|
|
294
|
+
# 示例用法
|
|
295
|
+
response = message_box(
|
|
296
|
+
None,
|
|
297
|
+
"是否要退出程序?",
|
|
298
|
+
"确认",
|
|
299
|
+
buttons='yes_no',
|
|
300
|
+
icon='question'
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
if response == 'yes':
|
|
304
|
+
print("程序退出。")
|
|
305
|
+
else:
|
|
306
|
+
print("程序继续运行。")
|
|
307
|
+
|
|
308
|
+
message_box(
|
|
309
|
+
None,
|
|
310
|
+
"操作已完成。",
|
|
311
|
+
"通知",
|
|
312
|
+
buttons='ok',
|
|
313
|
+
icon='information'
|
|
314
314
|
)
|