ezgo 0.0.1__py3-none-any.whl → 0.0.2__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.
ezgo/go2.py DELETED
@@ -1,633 +0,0 @@
1
- import time
2
- import threading
3
- import numpy as np
4
- import cv2
5
- import netifaces
6
- import subprocess
7
- from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
8
- from unitree_sdk2py.go2.sport.sport_client import SportClient
9
- from unitree_sdk2py.go2.video.video_client import VideoClient
10
- from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_
11
-
12
-
13
- error_code = {
14
- 100: "灵动",
15
- 1001: "阻尼",
16
- 1002: "站立锁定",
17
- 1004: "蹲下",
18
- 2006: "蹲下",
19
- 1006: "打招呼/伸懒腰/舞蹈/拜年/比心/开心",
20
- 1007: "坐下",
21
- 1008: "前跳",
22
- 1009: "扑人",
23
- 1013: "平衡站立",
24
- 1015: "常规行走",
25
- 1016: "常规跑步",
26
- 1017: "常规续航",
27
- 1091: "摆姿势",
28
- 2004: "翻身", # ???
29
- 2007: "闪避",
30
- 2008: "并腿跑",
31
- 2009: "跳跃跑",
32
- 2010: "经典",
33
- 2011: "倒立",
34
- 2012: "前空翻",
35
- 2013: "后空翻",
36
- 2014: "左空翻",
37
- 2016: "交叉步",
38
- 2017: "直立",
39
- 2019: "牵引",
40
- }
41
-
42
-
43
- class Go2:
44
- """
45
- 宇树Go2 运动控制封装类
46
- 适用于:Unitree SDK2 Python接口
47
- """
48
- def __init__(self, interface=None, timeout=20.0):
49
- """
50
- 初始化控制器
51
- :param interface: 网卡接口名称
52
- :param timeout: 超时时间(秒)
53
- """
54
- self.interface = interface
55
- self.timeout = timeout
56
- self.sport_client = None
57
- self.video_client = None
58
- self.cap = None
59
- self.error_code = 0 # 状态码
60
-
61
- # 移动控制相关变量
62
- self._moving = False
63
- self._move_params = (0, 0, 0)
64
- self._move_thread = None
65
-
66
- if self.interface is None:
67
- self.get_interface()
68
-
69
- def get_interface(self):
70
- """获取当前接口"""
71
-
72
- interfaces = netifaces.interfaces()
73
- for iface in interfaces:
74
- if iface.startswith("en"):
75
- self.interface = iface
76
- break
77
-
78
- def check_go2_connection(self):
79
- """检查机器狗IP连通性"""
80
- try:
81
- # 使用sudo ping检查机器狗IP (192.168.123.161是Go2的默认IP)
82
- result = subprocess.run(['sudo', 'ping', '-c', '1', '-W', '2', '192.168.123.161'],
83
- capture_output=True, text=True, timeout=5)
84
- return result.returncode == 0
85
- except subprocess.TimeoutExpired:
86
- print("ping超时")
87
- return False
88
- except Exception as e:
89
- print(f"检查连接时出错: {e}")
90
- return False
91
-
92
-
93
-
94
-
95
- def init(self):
96
- """初始化与Go2的连接"""
97
-
98
- # 检查机器狗IP连通性
99
- if not self.check_go2_connection():
100
- print("无法连接到机器狗,请检查网络连接")
101
- return False
102
-
103
- ChannelFactoryInitialize(0, self.interface)
104
-
105
- # 启动状态订阅
106
- self.sub_state(self.callback)
107
-
108
- # 初始化运动控制客户端
109
- self.sport_client = SportClient()
110
- self.sport_client.SetTimeout(self.timeout)
111
- self.sport_client.Init()
112
- # 初始化视频流客户端
113
- # self.video_client = VideoClient()
114
- # self.video_client.SetTimeout(self.timeout)
115
- # self.video_client.Init()
116
- self.cap = self.open_video()
117
-
118
- return True
119
-
120
-
121
- def open_video(self, width: int = 480, height: int = 320):
122
- """打开视频流"""
123
- gstreamer_str = (
124
- f"udpsrc address=230.1.1.1 port=1720 multicast-iface={self.interface} "
125
- "! application/x-rtp, media=video, encoding-name=H264 "
126
- "! rtph264depay ! h264parse "
127
- "! avdec_h264 " # 解码H.264
128
- "! videoscale " # 添加缩放元素,用于调整分辨率
129
- f"! video/x-raw,width={width},height={height} " # 目标分辨率
130
- "! videoconvert ! video/x-raw, format=BGR " # 转换为OpenCV支持的BGR格式
131
- "! appsink drop=1"
132
- )
133
- cap = cv2.VideoCapture(gstreamer_str, cv2.CAP_GSTREAMER)
134
- if not cap.isOpened():
135
- print("视频流打开失败")
136
- return
137
- return cap
138
-
139
- def read_image(self):
140
- """从视频流获取一帧图像"""
141
- ret, frame = self.cap.read()
142
- if not ret:
143
- print("读取图像失败")
144
- return None
145
- return frame
146
-
147
-
148
- def sub_state(self, callback, queue_size: int = 5):
149
- subscriber = ChannelSubscriber("rt/sportmodestate", SportModeState_)
150
- subscriber.Init(callback, queue_size)
151
-
152
- def callback(self, msg):
153
- self.error_code = msg.error_code
154
-
155
-
156
- # def read_image(self):
157
- # """从视频流获取一帧图像"""
158
- # code, data = self.video_client.GetImageSample()
159
- # if code != 0 or data is None:
160
- # print("获取图像样本失败,错误码:", code)
161
- # return None
162
- # image_data = np.frombuffer(bytes(data), dtype=np.uint8)
163
- # image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
164
- # return image
165
-
166
- def Damp(self):
167
- """进入阻尼状态。"""
168
- self._call(self.sport_client.Damp)
169
-
170
- def BalanceStand(self):
171
- """解除锁定。"""
172
- self._call(self.sport_client.BalanceStand)
173
-
174
- def StopMove(self):
175
- """停下当前动作,将绝大多数指令恢复成默认值。"""
176
- print("停止移动")
177
- # 停止持续移动线程
178
- self._moving = False
179
- if hasattr(self, '_move_thread') and self._move_thread.is_alive():
180
- self._move_thread.join(timeout=1.0)
181
-
182
- # 调用SDK的停止方法
183
- self._call(self.sport_client.StopMove)
184
-
185
- def _call(self, func, *args, **kwargs):
186
- """在线程中调用运动控制函数"""
187
- def fun_thread():
188
- ret = func(*args, **kwargs)
189
- print(f"{func.__name__} 执行结果:", ret)
190
-
191
- t = threading.Thread(target=fun_thread)
192
- t.start()
193
- t.join() # 等待线程完成
194
-
195
-
196
-
197
- def StandUp(self):
198
- """
199
- 关节锁定,站高。
200
- 执行后状态: 1002:站立锁定
201
- """
202
- # 执行前判断状态
203
- if self.error_code in [1002]: # 1002 :站立锁定
204
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
205
- return
206
- if self.error_code not in [100, 1001, 1007, 1013]: # 100 :灵动, 1001 :阻尼, 1013 :平衡站立
207
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
208
- return
209
- self._call(self.sport_client.StandUp)
210
-
211
-
212
-
213
- def StandDown(self):
214
- """
215
- 关节锁定,站低。
216
- 执行后状态: 1001:阻尼
217
- """
218
- # 执行前判断状态
219
- if self.error_code in [1001, 1004, 2006]: # 1001:阻尼 1004 :蹲下 2006 :蹲下
220
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
221
- return
222
- if self.error_code not in [100, 1002, 1013]: # 100 :灵动, 1001 :阻尼, 1013 :平衡站立
223
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
224
- return
225
-
226
- # 执行指令
227
- self._call(self.sport_client.StandDown)
228
-
229
- def RecoveryStand(self):
230
- """ 恢复站立。"""
231
- self._call(self.sport_client.RecoveryStand)
232
-
233
- def Euler(self, roll, pitch, yaw):
234
- """站立和行走时的姿态。"""
235
- pass
236
-
237
- def Move(self, vx, vy, vyaw):
238
- """移动。"""
239
- # 检查状态是否允许移动
240
- if self.error_code not in [100, 1002, 1013]: # 100:灵动, 1002:站立锁定, 1013:平衡站立
241
- print(f"当前状态不允许移动: {self.error_code} - {error_code.get(self.error_code, '未知状态')}")
242
- return False
243
-
244
- # 限制速度范围
245
- vx = max(-1.0, min(1.0, vx)) # 前后速度限制在-1到1之间
246
- vy = max(-1.0, min(1.0, vy)) # 左右速度限制在-1到1之间
247
- vyaw = max(-2.0, min(2.0, vyaw)) # 转动速度限制在-2到2之间
248
-
249
- def move_thread():
250
- ret = self.sport_client.Move(vx, vy, vyaw)
251
- success = ret == 0
252
- print(f"Move 执行结果: {ret}, 成功: {success}")
253
- return success
254
-
255
- t = threading.Thread(target=move_thread)
256
- t.start()
257
- t.join()
258
- return True
259
-
260
- def MoveForDuration(self, vx, vy, vyaw, duration):
261
- """
262
- 持续移动指定时间
263
-
264
- Args:
265
- vx (float): 前后速度 (-1.0 到 1.0)
266
- vy (float): 左右速度 (-1.0 到 1.0)
267
- vyaw (float): 转动速度 (-2.0 到 2.0)
268
- duration (float): 移动时间(秒)
269
-
270
- Returns:
271
- bool: 是否成功执行
272
- """
273
- print(f"开始移动: vx={vx}, vy={vy}, vyaw={vyaw}, 持续时间={duration}秒")
274
- start_time = time.time()
275
-
276
- try:
277
- while time.time() - start_time < duration:
278
- if not self.Move(vx, vy, vyaw):
279
- return False
280
- time.sleep(0.1) # 每100ms调用一次
281
-
282
- print("移动完成")
283
- return True
284
-
285
- except KeyboardInterrupt:
286
- print("移动被用户中断")
287
- return False
288
-
289
- def Forward(self, speed=0.3, duration=2.0):
290
- """向前移动"""
291
- return self.MoveForDuration(speed, 0, 0, duration)
292
-
293
- def Backward(self, speed=0.3, duration=2.0):
294
- """向后移动"""
295
- return self.MoveForDuration(-speed, 0, 0, duration)
296
-
297
- def Left(self, speed=0.3, duration=2.0):
298
- """向左移动"""
299
- return self.MoveForDuration(0, speed, 0, duration)
300
-
301
- def Right(self, speed=0.3, duration=2.0):
302
- """向右移动"""
303
- return self.MoveForDuration(0, -speed, 0, duration)
304
-
305
- def TurnLeft(self, speed=0.5, duration=2.0):
306
- """左转"""
307
- return self.MoveForDuration(0, 0, speed, duration)
308
-
309
- def TurnRight(self, speed=0.5, duration=2.0):
310
- """右转"""
311
- return self.MoveForDuration(0, 0, -speed, duration)
312
-
313
- def StartMove(self, vx, vy, vyaw):
314
- """
315
- 开始持续移动,需要调用StopMove来停止
316
-
317
- Args:
318
- vx (float): 前后速度 (-1.0 到 1.0)
319
- vy (float): 左右速度 (-1.0 到 1.0)
320
- vyaw (float): 转动速度 (-2.0 到 2.0)
321
-
322
- Returns:
323
- bool: 是否成功开始移动
324
- """
325
- print(f"开始持续移动: vx={vx}, vy={vy}, vyaw={vyaw}")
326
- self._moving = True
327
- self._move_params = (vx, vy, vyaw)
328
-
329
- def continuous_move():
330
- while self._moving:
331
- if not self.Move(vx, vy, vyaw):
332
- break
333
- time.sleep(0.1)
334
-
335
- self._move_thread = threading.Thread(target=continuous_move)
336
- self._move_thread.daemon = True # 设置为守护线程
337
- self._move_thread.start()
338
- return True
339
-
340
- def StartForward(self, speed=0.3):
341
- """开始向前移动"""
342
- return self.StartMove(speed, 0, 0)
343
-
344
- def StartBackward(self, speed=0.3):
345
- """开始向后移动"""
346
- return self.StartMove(-speed, 0, 0)
347
-
348
- def StartLeft(self, speed=0.3):
349
- """开始向左移动"""
350
- return self.StartMove(0, speed, 0)
351
-
352
- def StartRight(self, speed=0.3):
353
- """开始向右移动"""
354
- return self.StartMove(0, -speed, 0)
355
-
356
- def StartTurnLeft(self, speed=0.5):
357
- """开始左转"""
358
- return self.StartMove(0, 0, speed)
359
-
360
- def StartTurnRight(self, speed=0.5):
361
- """开始右转"""
362
- return self.StartMove(0, 0, -speed)
363
-
364
- def Sit(self):
365
- """
366
- 坐下。
367
- 执行后状态: 1007: 坐下
368
- """
369
- # 执行前判断状态
370
- if self.error_code in [1007]: # 1007 : 坐下
371
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
372
- return
373
- if self.error_code not in [100, 1002, 1013]: # 100 :灵动, 1001 :阻尼, 1013 :平衡站立
374
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
375
- return
376
-
377
- # 执行指令
378
- self._call(self.sport_client.Sit)
379
-
380
- def RiseSit(self):
381
- """
382
- 站起(相对于坐下)。
383
- 执行后状态:
384
- """
385
- # 执行前判断状态
386
- if self.error_code in [1007]: # 1002 :站立锁定
387
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
388
- return
389
- if self.error_code not in [100, 1007, 1013]: # 100 :灵动, 1007 : 坐下, 1013 :平衡站立
390
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
391
- return
392
-
393
- # 执行指令
394
- self._call(self.sport_client.RiseSit)
395
-
396
-
397
-
398
- def SpeedLevel(self, level: int):
399
- """设置速度档位。"""
400
- pass
401
-
402
- def Hello(self):
403
- """
404
- 打招呼
405
- 执行后状态: 1013:平衡站立
406
- """
407
- # 执行前判断状态
408
- if self.error_code in [1006]: # 1006 :打招呼/伸懒腰/舞蹈/拜年/比心/开心
409
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
410
- return
411
- if self.error_code not in [100, 1002, 1013]: # 空闲状态
412
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
413
- return
414
-
415
- # 执行指令
416
- self._call(self.sport_client.Hello)
417
-
418
- def Stretch(self):
419
- """
420
- 伸懒腰。
421
- 执行后状态: 1013:平衡站立
422
- """
423
- # 执行前判断状态
424
- if self.error_code in [1006]: # 1006 :打招呼/伸懒腰/舞蹈/拜年/比心/开心
425
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
426
- return
427
- if self.error_code not in [100, 1002, 1013]: # 站立且空闲状态
428
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
429
- return
430
-
431
- # 执行指令
432
- self._call(self.sport_client.Stretch)
433
-
434
-
435
-
436
- def Content(self):
437
- """
438
- 开心。
439
- 执行后状态: 1013:平衡站立
440
- """
441
- # 执行前判断状态
442
- if self.error_code in [1006]: # 1006 :打招呼/伸懒腰/舞蹈/拜年/比心/开心
443
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
444
- return
445
- if self.error_code not in [100, 1002, 1013]: # 空闲状态
446
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
447
- return
448
-
449
- # 执行指令
450
- self._call(self.sport_client.Content)
451
-
452
-
453
- def Heart(self):
454
- """
455
- 比心。
456
- 执行后状态: 1013:平衡站立
457
- """
458
- # 执行前判断状态
459
- if self.error_code in [1006]: # 1006 :打招呼/伸懒腰/舞蹈/拜年/比心/开心
460
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
461
- return
462
- if self.error_code not in [100, 1002, 1013]: # 空闲状态
463
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
464
- return
465
-
466
- # 执行指令
467
- self._call(self.sport_client.Heart)
468
-
469
-
470
- def Pose(self, flag):
471
- """摆姿势。"""
472
- pass
473
-
474
- def Scrape(self):
475
- """
476
- 拜年作揖。
477
- 执行后状态: 1013:平衡站立
478
- """
479
- # 执行前判断状态
480
- if self.error_code in [1006]: # 1006 :打招呼/伸懒腰/舞蹈/拜年/比心/开心
481
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
482
- return
483
- if self.error_code not in [100, 1002, 1013]: # 空闲状态
484
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
485
- return
486
-
487
- # 执行指令
488
- self._call(self.sport_client.Scrape)
489
-
490
-
491
-
492
- def FrontJump(self):
493
- """
494
- 前跳。
495
- 执行后状态: 1013: 平衡站立
496
- """
497
- # 执行前判断状态
498
- if self.error_code in [1008]: # 1008 : 前跳
499
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
500
- return
501
- if self.error_code not in [100, 1002, 1013]: # 空闲状态
502
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
503
- return
504
-
505
- # 执行指令
506
- self._call(self.sport_client.FrontJump)
507
-
508
-
509
-
510
- def FrontPounce(self):
511
- """
512
- 向前扑人。
513
- 执行后状态: 1013: 平衡站立
514
- """
515
- # 执行前判断状态
516
- if self.error_code in [1009]: # 1009 : 扑人
517
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
518
- return
519
- if self.error_code not in [100, 1002, 1013]: # 空闲状态
520
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
521
- return
522
-
523
- # 执行指令
524
- self._call(self.sport_client.FrontPounce)
525
-
526
-
527
- def Dance1(self):
528
- """
529
- 舞蹈段落1。
530
- 执行后状态: 1013: 平衡站立
531
- """
532
- # 执行前判断状态
533
- if self.error_code in [1006]: # 1006 :打招呼/伸懒腰/舞蹈/拜年/比心/开心
534
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
535
- return
536
- if self.error_code not in [100, 1002, 1013]: # 空闲状态
537
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
538
- return
539
-
540
- # 执行指令
541
- self._call(self.sport_client.Dance1)
542
-
543
- def Dance2(self):
544
- """
545
- 舞蹈段落2。
546
- 执行后状态: 1013: 平衡站立
547
- """
548
- # 执行前判断状态
549
- if self.error_code in [1006]: # 1006 :打招呼/伸懒腰/舞蹈/拜年/比心/开心
550
- print("当前状态:", self.error_code, error_code[self.error_code], "无需执行")
551
- return
552
- if self.error_code not in [100, 1002, 1013]: # 空闲状态
553
- print("繁忙中,当前状态:", self.error_code, error_code[self.error_code])
554
- return
555
-
556
- # 执行指令
557
- self._call(self.sport_client.Dance2)
558
-
559
- def HandStand(self, flag: int):
560
- """倒立行走。"""
561
- pass
562
-
563
- def LeftFlip(self):
564
- """左空翻。"""
565
- pass
566
-
567
- def BackFlip(self):
568
- """后空翻。"""
569
- pass
570
-
571
- def FreeWalk(self, flag: int):
572
- """ 灵动模式(默认步态)。"""
573
- pass
574
-
575
- def FreeBound(self, flag: int):
576
- """ 并腿跑模式。"""
577
- pass
578
-
579
- def FreeJump(self, flag: int):
580
- """ 跳跃模式。"""
581
- pass
582
-
583
- def FreeAvoid(self, flag: int):
584
- """ 闪避模式。"""
585
- pass
586
-
587
- def WalkUpright(self, flag: int):
588
- """ 后腿直立模式。"""
589
- pass
590
-
591
- def CrossStep(self, flag: int):
592
- """ 交叉步模式。"""
593
- pass
594
-
595
- def AutoRecoverSet(self, flag: int):
596
- """ 设置自动翻身是否生效。"""
597
- pass
598
-
599
- def AutoRecoverGet(self):
600
- """ 查询自动翻身是否生效。"""
601
- pass
602
-
603
- def ClassicWalk(self, flag: int):
604
- """ 经典步态。"""
605
- pass
606
-
607
- def TrotRun(self):
608
- """ 进入常规跑步模式 """
609
- pass
610
-
611
- def StaticWalk(self):
612
- """ 进入常规行走模式"""
613
- pass
614
-
615
- def EconomicGait(self):
616
- """ 进入常规续航模式 """
617
- pass
618
-
619
- def SwitchAvoidMode(self):
620
- """ 闪避模式下,关闭摇杆未推时前方障碍物的闪避以及后方的障碍物躲避"""
621
- pass
622
-
623
-
624
-
625
-
626
- # -------------------- 测试 --------------------
627
- if __name__ == "__main__":
628
- interface = "enx00e0986113a6" # 替换为你的Go2网卡接口名称
629
-
630
- go2 = Go2(interface=interface)
631
-
632
- go2.stand_down()
633
- go2.stand_up()
ezgo/ui.py DELETED
@@ -1,77 +0,0 @@
1
- import tkinter as tk
2
- import platform
3
- import os
4
-
5
-
6
- if platform.system() == "Linux":
7
- os.environ["DISPLAY"] = ":0"
8
-
9
- class APP(tk.Tk):
10
- def __init__(self, title="APP", width=480, height=800):
11
- super().__init__()
12
- self.title_text = title
13
- self.title(self.title_text)
14
- self.geometry(f"{width}x{height}")
15
-
16
- if platform.system() == "Linux":
17
- self.resizable(False, False) # 固定窗口大小
18
- self.attributes('-fullscreen', True) # 全屏
19
- self.config(cursor="none") # 隐藏鼠标
20
- self.update_idletasks() # 强制立即应用全屏,避免闪烁/延迟
21
-
22
- # 显示图像和文字的全局变量
23
- self.image = None # 显示的图像
24
- self.text = "" # 显示的文字
25
-
26
- self.is_running = True # 程序运行状态
27
-
28
- # 初始化组件
29
- self.create_widgets()
30
- # 绑定窗口关闭事件
31
- self.protocol("WM_DELETE_WINDOW", self.on_close)
32
-
33
- def create_widgets(self):
34
- # 标题
35
- title_label = tk.Label(self, text=self.title_text, font=("SimHei", 18))
36
- title_label.pack(pady=5)
37
-
38
- # 图像显示区域
39
- self.frame1 = tk.LabelFrame(self, text="图像", width=480, height=480, font=("SimHei", 10))
40
- self.frame1.pack_propagate(False)
41
- self.frame1.pack(fill=tk.X, pady=5)
42
- self.image_label = tk.Label(self.frame1)
43
- self.image_label.pack(pady=5, fill=tk.BOTH, expand=True, anchor=tk.CENTER)
44
-
45
- # 文字显示区域
46
- self.frame2 = tk.LabelFrame(self, text="结果", width=480, height=200, font=("SimHei", 10))
47
- self.frame2.pack_propagate(False)
48
- self.frame2.pack(fill=tk.X, pady=5)
49
- self.text_label = tk.Label(self.frame2,
50
- text="文本显示",
51
- font=("SimHei", 14),
52
- wraplength=450,
53
- justify=tk.CENTER,)
54
- self.text_label.pack(pady=5, fill=tk.BOTH, expand=True, anchor=tk.CENTER)
55
-
56
-
57
- # 退出按钮
58
- self.exit_btn = tk.Button(self, text="退出", command=self.on_close, font=("SimHei", 16))
59
- self.exit_btn.pack(pady=10)
60
-
61
-
62
- def set_image(self, image):
63
- """设置显示的图像"""
64
- self.image = image
65
- self.image_label.config(image=self.image)
66
-
67
- def set_text(self, text):
68
- """设置显示的文字"""
69
- self.text = text
70
- self.text_label.config(text=self.text)
71
-
72
- def on_close(self):
73
- """窗口关闭/退出按钮回调"""
74
- self.is_running = False
75
- self.quit() # 退出主循环
76
- self.destroy() # 销毁窗口
77
-