├── README.md ├── temp_qrcode1.png ├── gamestate_integration_nodecs2.cfg ├── config.json └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # CS2-DG_LAB 2 | 基于郊狼3.0socket协议的CS2实时反馈项目 3 | -------------------------------------------------------------------------------- /temp_qrcode1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kobopi/CS2-DG_LAB/HEAD/temp_qrcode1.png -------------------------------------------------------------------------------- /gamestate_integration_nodecs2.cfg: -------------------------------------------------------------------------------- 1 | "Quick Start Guide Service v0.1" 2 | { 3 | "uri" "http://127.0.0.1:3000" 4 | "timeout" "5.0" 5 | "buffer" "0.1" 6 | "throttle" "0.5" 7 | "heartbeat" "0.2" 8 | "auth" 9 | { 10 | "token" "MYTOKENHERE" 11 | } 12 | "data" 13 | { 14 | "provider" "1" 15 | "map" "1" 16 | "round" "1" 17 | "player_id" "1" 18 | "player_state" "1" 19 | "player_weapons" "1" 20 | "player_match_stats" "1" 21 | "allplayers_id" "1" 22 | "allplayers_state" "1" 23 | "allplayers_match_stats" "1" 24 | } 25 | } -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "pulse_data": { 3 | "\u6b7b\u4ea1": [ 4 | [ 5 | [ 6 | 10, 7 | 10, 8 | 10, 9 | 10 10 | ], 11 | [ 12 | 100, 13 | 100, 14 | 100, 15 | 100 16 | ] 17 | ], 18 | [ 19 | [ 20 | 10, 21 | 10, 22 | 10, 23 | 10 24 | ], 25 | [ 26 | 100, 27 | 100, 28 | 100, 29 | 100 30 | ] 31 | ], 32 | [ 33 | [ 34 | 10, 35 | 10, 36 | 10, 37 | 10 38 | ], 39 | [ 40 | 100, 41 | 100, 42 | 100, 43 | 100 44 | ] 45 | ], 46 | [ 47 | [ 48 | 10, 49 | 10, 50 | 10, 51 | 10 52 | ], 53 | [ 54 | 100, 55 | 100, 56 | 100, 57 | 100 58 | ] 59 | ], 60 | [ 61 | [ 62 | 10, 63 | 10, 64 | 10, 65 | 10 66 | ], 67 | [ 68 | 100, 69 | 100, 70 | 100, 71 | 100 72 | ] 73 | ], 74 | [ 75 | [ 76 | 10, 77 | 10, 78 | 10, 79 | 10 80 | ], 81 | [ 82 | 100, 83 | 100, 84 | 100, 85 | 100 86 | ] 87 | ], 88 | [ 89 | [ 90 | 10, 91 | 10, 92 | 10, 93 | 10 94 | ], 95 | [ 96 | 100, 97 | 100, 98 | 100, 99 | 100 100 | ] 101 | ], 102 | [ 103 | [ 104 | 10, 105 | 10, 106 | 10, 107 | 10 108 | ], 109 | [ 110 | 100, 111 | 100, 112 | 100, 113 | 100 114 | ] 115 | ] 116 | ], 117 | "\u53d7\u4f24": [ 118 | [ 119 | [ 120 | 10, 121 | 10, 122 | 10, 123 | 10 124 | ], 125 | [ 126 | 0, 127 | 0, 128 | 0, 129 | 0 130 | ] 131 | ], 132 | [ 133 | [ 134 | 10, 135 | 10, 136 | 10, 137 | 10 138 | ], 139 | [ 140 | 0, 141 | 5, 142 | 10, 143 | 20 144 | ] 145 | ], 146 | [ 147 | [ 148 | 10, 149 | 10, 150 | 10, 151 | 10 152 | ], 153 | [ 154 | 20, 155 | 25, 156 | 30, 157 | 40 158 | ] 159 | ], 160 | [ 161 | [ 162 | 10, 163 | 10, 164 | 10, 165 | 10 166 | ], 167 | [ 168 | 40, 169 | 45, 170 | 50, 171 | 60 172 | ] 173 | ], 174 | [ 175 | [ 176 | 10, 177 | 10, 178 | 10, 179 | 10 180 | ], 181 | [ 182 | 60, 183 | 65, 184 | 70, 185 | 80 186 | ] 187 | ], 188 | [ 189 | [ 190 | 10, 191 | 10, 192 | 10, 193 | 10 194 | ], 195 | [ 196 | 100, 197 | 100, 198 | 100, 199 | 100 200 | ] 201 | ] 202 | ], 203 | "\u70e7\u4f24": [ 204 | [ 205 | [ 206 | 10, 207 | 10, 208 | 10, 209 | 10 210 | ], 211 | [ 212 | 20, 213 | 25, 214 | 30, 215 | 40 216 | ] 217 | ], 218 | [ 219 | [ 220 | 10, 221 | 10, 222 | 10, 223 | 10 224 | ], 225 | [ 226 | 40, 227 | 45, 228 | 50, 229 | 60 230 | ] 231 | ], 232 | [ 233 | [ 234 | 10, 235 | 10, 236 | 10, 237 | 10 238 | ], 239 | [ 240 | 100, 241 | 100, 242 | 100, 243 | 100 244 | ] 245 | ], 246 | [ 247 | [ 248 | 10, 249 | 10, 250 | 10, 251 | 10 252 | ], 253 | [ 254 | 40, 255 | 45, 256 | 50, 257 | 60 258 | ] 259 | ], 260 | [ 261 | [ 262 | 10, 263 | 10, 264 | 10, 265 | 10 266 | ], 267 | [ 268 | 20, 269 | 25, 270 | 30, 271 | 40 272 | ] 273 | ] 274 | ], 275 | "\u50bb\u74dc\u86cb": [ 276 | [ 277 | [ 278 | 10, 279 | 10, 280 | 10, 281 | 10 282 | ], 283 | [ 284 | 100, 285 | 100, 286 | 100, 287 | 100 288 | ] 289 | ], 290 | [ 291 | [ 292 | 10, 293 | 10, 294 | 10, 295 | 10 296 | ], 297 | [ 298 | 100, 299 | 100, 300 | 100, 301 | 100 302 | ] 303 | ], 304 | [ 305 | [ 306 | 10, 307 | 10, 308 | 10, 309 | 10 310 | ], 311 | [ 312 | 100, 313 | 100, 314 | 100, 315 | 100 316 | ] 317 | ] 318 | ], 319 | "\u70df\u96fe\u5f39": [ 320 | [ 321 | [ 322 | 10, 323 | 10, 324 | 10, 325 | 10 326 | ], 327 | [ 328 | 0, 329 | 5, 330 | 10, 331 | 20 332 | ] 333 | ], 334 | [ 335 | [ 336 | 10, 337 | 10, 338 | 10, 339 | 10 340 | ], 341 | [ 342 | 20, 343 | 25, 344 | 30, 345 | 40 346 | ] 347 | ], 348 | [ 349 | [ 350 | 10, 351 | 10, 352 | 10, 353 | 10 354 | ], 355 | [ 356 | 40, 357 | 45, 358 | 50, 359 | 60 360 | ] 361 | ], 362 | [ 363 | [ 364 | 10, 365 | 10, 366 | 10, 367 | 10 368 | ], 369 | [ 370 | 20, 371 | 25, 372 | 30, 373 | 40 374 | ] 375 | ], 376 | [ 377 | [ 378 | 10, 379 | 10, 380 | 10, 381 | 10 382 | ], 383 | [ 384 | 0, 385 | 5, 386 | 10, 387 | 20 388 | ] 389 | ] 390 | ] 391 | }, 392 | "ip_path": "ws://192.168.0.150:5678", 393 | "hit": "10", 394 | "is_smoke": 1, 395 | "is_brush": 1, 396 | "is_flash": 1, 397 | "is_dead": 1, 398 | "is_voice":1, 399 | "voice_A":20, 400 | "voice_B":20 401 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import io 3 | import json 4 | import math 5 | import qrcode 6 | import socket 7 | from aiohttp import web 8 | import winreg 9 | import os 10 | import pyaudio 11 | import numpy as np 12 | from pydglab_ws import ( 13 | FeedbackButton, 14 | Channel, 15 | RetCode, 16 | DGLabWSServer, 17 | StrengthOperationType, 18 | StrengthData, 19 | ) 20 | import tkinter as tk 21 | from PIL import Image, ImageTk 22 | from multiprocessing import Process, Queue 23 | 24 | FORMAT = pyaudio.paInt16 25 | CHANNELS = 1 26 | RATE = 44100 27 | CHUNK = 1024 28 | 29 | audio = pyaudio.PyAudio() 30 | stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) 31 | 32 | # 读取 PULSE_DATA 从 JSON 文件 33 | with open("config.json", "r", encoding="utf-8") as file: 34 | config = json.load(file) 35 | PULSE_DATA = config["pulse_data"] 36 | HIT = int(config["hit"]) 37 | is_voice = config["is_voice"] 38 | voice_A = config["voice_A"] 39 | voice_B = config["voice_B"] 40 | volume_scaled = 0 41 | def get_voice(): 42 | try: 43 | while True: 44 | global volume_scaled 45 | data = stream.read(CHUNK) 46 | numpy_data = np.frombuffer(data, dtype=np.int16) 47 | normalized_data = numpy_data / 32768.0 48 | volume = np.linalg.norm(normalized_data) / np.sqrt(len(normalized_data)) 49 | volume_scaled = int(volume * 100) 50 | return volume_scaled 51 | except KeyboardInterrupt: 52 | print("程序已退出。") 53 | def get_ip_address(): 54 | """获取本机IP地址""" 55 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 56 | s.connect(("8.8.8.8", 80)) 57 | ip_address = s.getsockname()[0] 58 | s.close() 59 | ip_address = f"ws://{ip_address}:5678" 60 | return ip_address 61 | 62 | 63 | csi = None 64 | def script_load(): 65 | global csi_path 66 | 67 | try: 68 | key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\Valve\Steam', 0, winreg.KEY_READ) 69 | path, _ = winreg.QueryValueEx(key, 'SteamPath') 70 | winreg.CloseKey(key) 71 | except: 72 | return None 73 | 74 | libpath = path + '/steamapps/libraryfolders.vdf' 75 | 76 | with open(libpath, 'r') as libvdf: 77 | library = libvdf.readlines() 78 | last_path = None 79 | found = False 80 | 81 | for line in library: 82 | pair = line.strip('\n').strip('\t') 83 | if pair.startswith('"path"'): 84 | _, v = pair.split('\t\t') 85 | last_path = v.strip('"').encode().decode('unicode_escape') 86 | elif '"730"' in pair: 87 | found = True 88 | break 89 | 90 | if found and os.path.exists(last_path + '\\steamapps\\common\\Counter-Strike Global Offensive\\'): 91 | csi_path = last_path + '\\steamapps\\common\\Counter-Strike Global Offensive\\' 92 | print(f"已找到 CS2 安装路径: {csi_path}") 93 | 94 | 95 | def auto_set_cfg(): 96 | cfg = """"CS2&DGLAB" 97 | { 98 | "uri" "http://127.0.0.1:3000" 99 | "timeout" "0.1" 100 | "buffer" "0.1" 101 | "throttle" "0.5" 102 | "heartbeat" "1.0" 103 | "auth" 104 | { 105 | "token" "MYTOKENHERE" 106 | } 107 | "data" 108 | { 109 | "provider" "1" 110 | "map" "1" 111 | "round" "1" 112 | "player_id" "1" 113 | "player_state" "1" 114 | } 115 | } 116 | """ 117 | try: 118 | script_load() 119 | if os.path.exists(csi_path + 'csgo\\cfg'): 120 | with open(csi_path + 'csgo\\cfg\\gamestate_integration_nodecs2.cfg', 'w') as fd: 121 | fd.write(cfg) 122 | 123 | if os.path.exists(csi_path + 'game\\csgo\\cfg'): 124 | with open(csi_path + 'game\\csgo\\cfg\\gamestate_integration_nodecs2.cfg', 'w') as fd: 125 | fd.write(cfg) 126 | except Exception as e: 127 | print(f"写入文件时发生错误: {e}") 128 | return False 129 | def print_qrcode(data: str): 130 | """输出二维码到终端界面""" 131 | qr = qrcode.QRCode( 132 | version=1, 133 | error_correction=qrcode.constants.ERROR_CORRECT_L, 134 | box_size=10, 135 | border=4, 136 | ) 137 | qr.add_data(data) 138 | f = io.StringIO() 139 | img = qr.make_image(fill_color="black", back_color="white") 140 | img_path = "temp_qrcode1.png" 141 | img.save(img_path) 142 | return img_path 143 | 144 | 145 | def read_config(): 146 | try: 147 | with open('config.json', 'r', encoding='utf-8') as file: 148 | config_data = json.load(file) 149 | # 处理config_data,例如打印每个字段的信息 150 | for key, value in config_data.items(): 151 | print(f"{key}: {value}") 152 | except FileNotFoundError: 153 | print("config.json文件未找到") 154 | except json.JSONDecodeError: 155 | print("config.json文件格式错误") 156 | 157 | 158 | health = 100 159 | max_strength_A = 0 160 | max_strength_B = 0 161 | strenghth_A = 0 162 | strenghth_B = 0 163 | 164 | 165 | async def handle_post_request(request): 166 | global health 167 | global HIT 168 | with open('config.json', 'r', encoding='utf-8') as file: 169 | config = json.load(file) 170 | ans = int(config["hit"])/100 171 | voice_A = config["voice_A"] 172 | voice_B = config["voice_B"] 173 | dead_flag = False 174 | queue = request.app["queue"] # 从app中获取queue 175 | """处理cs数据请求请求""" 176 | try: 177 | data = await request.json() 178 | if not data: 179 | return web.json_response( 180 | {"status": "error", "message": "请求体为空"}, status=400 181 | ) 182 | if "player" in data and "map" in data: 183 | if data["provider"]["steamid"] == data["player"]["steamid"]: 184 | now_health = data["player"]["state"]["health"] 185 | flash = data["player"]["state"]["flashed"] 186 | somke = data["player"]["state"]["smoked"] 187 | if is_voice == 1: 188 | now_voice_A = int(get_voice()+(max_strength_A*voice_A/100)) 189 | now_voice_B = int(get_voice()+(max_strength_B*voice_B/100)) 190 | waveform_data = {"type": "strlst", "data": now_voice_A, "chose": "a"} 191 | await queue.put(waveform_data) 192 | waveform_data = {"type": "strlst", "data": now_voice_B, "chose": "b"} 193 | await queue.put(waveform_data) 194 | # 血量减少(调整强度,发送波形) 195 | if now_health < health: 196 | data_a = math.ceil((100 - now_health) / 100 * max_strength_A) 197 | data_b = math.ceil((100 - now_health) / 100 * max_strength_B) 198 | data_a = math.ceil(data_a * ans) 199 | data_b = math.ceil(data_b * ans) 200 | # 这里多写一层判断,判断是否被烧伤 201 | if data["player"]["state"]["burning"] > 0: 202 | waveform_data = {"type": "pluse", "data": PULSE_DATA["烧伤"]} 203 | await queue.put(waveform_data) 204 | else: 205 | waveform_data = {"type": "pluse", "data": PULSE_DATA["受伤"]} 206 | await queue.put(waveform_data) 207 | # 强度 208 | if is_voice == 0: 209 | waveform_data = { 210 | "type": "strlup", 211 | "data": strenghth_A - data_a, 212 | "chose": "a", 213 | } 214 | await queue.put(waveform_data) 215 | waveform_data = { 216 | "type": "strlup", 217 | "data": strenghth_B - data_b, 218 | "chose": "b", 219 | } 220 | await queue.put(waveform_data) 221 | health = now_health 222 | # 傻瓜蛋! 223 | if flash > 0: 224 | waveform_data = {"type": "pluse", "data": PULSE_DATA["傻瓜蛋"]} 225 | await queue.put(waveform_data) 226 | if somke > 0: 227 | waveform_data = {"type": "pluse", "data": PULSE_DATA["烟雾弹"]} 228 | await queue.put(waveform_data) 229 | # 血量归零以及回合结束重置强度以及血量 230 | if now_health == 0: 231 | waveform_data = {"type": "pluse", "data": PULSE_DATA["死亡"]} 232 | await queue.put(waveform_data) 233 | await asyncio.sleep(1) 234 | waveform_data = {"type": "strlse", "data": 100} 235 | await queue.put(waveform_data) 236 | await asyncio.sleep(3) 237 | health = 100 238 | if "round" in data: 239 | if data["round"]["phase"] == "over": 240 | waveform_data = {"type": "strlse", "data": 100} 241 | await queue.put(waveform_data) 242 | # 游戏结束重置强度 243 | if data["map"]["phase"] == "gameover": 244 | waveform_data = {"type": "strlse", "data": 100} 245 | await queue.put(waveform_data) 246 | return web.json_response({"status": "success", "message": "数据已接收"}) 247 | except Exception as e: 248 | print(f"处理POST请求时出错: {e}") 249 | return web.json_response({"status": "error", "message": str(e)}, status=500) 250 | 251 | 252 | async def send_waveform_on_queue_change(queue, client): 253 | while True: 254 | waveform_data = await queue.get() 255 | types = waveform_data["type"] 256 | data = waveform_data["data"] 257 | if types == "pluse": 258 | await client.add_pulses(Channel.A, *(data * 1)) 259 | await client.add_pulses(Channel.B, *(data * 1)) 260 | elif types == "strlup": 261 | chose = waveform_data["chose"] 262 | if chose == "a": 263 | await client.set_strength( 264 | Channel.A, StrengthOperationType.INCREASE, data 265 | ) 266 | if chose == "b": 267 | await client.set_strength( 268 | Channel.B, StrengthOperationType.INCREASE, data 269 | ) 270 | elif types == "strlse": 271 | await client.set_strength(Channel.A, StrengthOperationType.DECREASE, 200) 272 | await client.set_strength(Channel.B, StrengthOperationType.DECREASE, 200) 273 | elif types == "strlst": 274 | chose = waveform_data["chose"] 275 | if chose == "a": 276 | await client.set_strength( 277 | Channel.A, StrengthOperationType.SET_TO, data 278 | ) 279 | if chose == "b": 280 | await client.set_strength( 281 | Channel.B, StrengthOperationType.SET_TO, data 282 | ) 283 | else: 284 | print("接收到未知类型数据,请检查") 285 | 286 | 287 | async def main(): 288 | global max_strength_A, max_strength_B, strenghth_A, strenghth_B 289 | queue = asyncio.Queue() 290 | 291 | app = web.Application() 292 | app["queue"] = queue 293 | app.router.add_post("/", handle_post_request) 294 | 295 | async with DGLabWSServer("0.0.0.0", 5678, 60) as server: 296 | client = server.new_local_client() 297 | ip_path = get_ip_address() 298 | print("获取到本地IP地址:" + ip_path) 299 | if auto_set_cfg(): 300 | print("自动导入cfg成功") 301 | url = client.get_qrcode(ip_path) 302 | print("请用 DG-Lab App 扫描二维码以连接") 303 | print_qrcode(url) 304 | # 创建进程间通信的队列 305 | gui_queue = Queue() 306 | # 启动GUI进程 307 | gui_process = Process(target=start_gui, args=(strenghth_A, strenghth_B, gui_queue)) 308 | gui_process.start() 309 | 310 | # 等待绑定 311 | await client.bind() 312 | print(f"已与 App {client.target_id} 成功绑定") 313 | runner = web.AppRunner(app) 314 | await runner.setup() 315 | site = web.TCPSite(runner, "127.0.0.1", 3000) 316 | await site.start() 317 | print("HTTP服务器已启动,监听端口 3000") 318 | 319 | # 启动监视队列的任务 320 | queue_monitor_task = asyncio.create_task( 321 | send_waveform_on_queue_change(queue, client) 322 | ) 323 | 324 | async for data in client.data_generator(): 325 | # 接收通道强度数据 326 | if isinstance(data, StrengthData): 327 | max_strength_A = data.a_limit 328 | max_strength_B = data.b_limit 329 | strenghth_A = data.a 330 | strenghth_B = data.b 331 | # 将更新的数据发送到 GUI 进程 332 | gui_queue.put((strenghth_A, strenghth_B)) 333 | # 接收 App 反馈按钮 334 | elif isinstance(data, FeedbackButton): 335 | print(f"App 触发了反馈按钮:{data.name}") 336 | 337 | if data == FeedbackButton.A1: 338 | # 之后这边要写逻辑 339 | print("按下 A1") 340 | 341 | # 接收 心跳 / App 断开通知 342 | elif data == RetCode.CLIENT_DISCONNECTED: 343 | print("App 已断开连接,你可以尝试重新扫码进行连接绑定") 344 | await client.rebind() 345 | print("重新绑定成功") 346 | 347 | # 确保队列监视任务在主循环结束后取消 348 | queue_monitor_task.cancel() 349 | try: 350 | await queue_monitor_task 351 | except asyncio.CancelledError: 352 | pass 353 | 354 | 355 | def start_gui(strength_A, strength_B, gui_queue): 356 | # 声明 hit_field 为全局变量 357 | global hit_field 358 | 359 | def update_hit_field(): 360 | try: 361 | with open('config.json', 'r', encoding='utf-8') as file: 362 | config_data = json.load(file) 363 | hit_value = config_data.get('hit', '') 364 | # 这里使用全局变量 hit_field 365 | hit_field.delete(1.0, tk.END) 366 | hit_field.insert(tk.END, hit_value) 367 | except Exception as e: 368 | print(f"读取 config.json 出错: {e}") 369 | 370 | def save_hit_field(): 371 | try: 372 | with open('config.json', 'r+', encoding='utf-8') as file: 373 | config_data = json.load(file) 374 | new_hit_value = hit_field.get(1.0, tk.END).strip() 375 | config_data['hit'] = new_hit_value 376 | file.seek(0) 377 | json.dump(config_data, file, indent=4) 378 | file.truncate() 379 | except Exception as e: 380 | print(f"保存 config.json 出错: {e}") 381 | 382 | root = tk.Tk() 383 | root.title("CS2&郊狼") 384 | root.geometry("400x400") # 设置窗口大小 385 | sidebar_frame = tk.Frame(root, width=200, bg='light grey') 386 | sidebar_frame.grid(row=0, column=0, sticky="nsew") 387 | data_button = tk.Button(sidebar_frame, text="显示数据", bd=0, anchor=tk.W, command=lambda: show_section('data')) 388 | data_button.pack(pady=10, padx=10, fill=tk.X) 389 | event_button = tk.Button(sidebar_frame, text="功能", bd=0, anchor=tk.W, command=lambda: show_section('hit')) 390 | event_button.pack(pady=10, padx=10, fill=tk.X) 391 | main_frame = tk.Frame(root) 392 | main_frame.grid(row=0, column=1, sticky="nsew") 393 | root.grid_rowconfigure(0, weight=5) 394 | root.grid_columnconfigure(1, weight=1) 395 | 396 | # 显示通道强度A的标签 397 | strength_a_label = tk.Label(main_frame, text=f"通道强度A: {strength_A}") 398 | strength_a_label.pack() 399 | 400 | # 显示通道强度B的标签 401 | strength_b_label = tk.Label(main_frame, text=f"通道强度B: {strength_B}") 402 | strength_b_label.pack() 403 | 404 | def update_strength_labels(): 405 | try: 406 | new_strength_A, new_strength_B = gui_queue.get_nowait() 407 | strength_a_label.config(text=f"通道强度A: {new_strength_A}") 408 | strength_b_label.config(text=f"通道强度B: {new_strength_B}") 409 | except Exception: 410 | pass 411 | root.after(100, update_strength_labels) 412 | 413 | def show_qrcode(): 414 | img_path = "temp_qrcode1.png" 415 | img = Image.open(img_path) 416 | # 调整图像显示尺寸和质量 417 | img = img.resize((300, 300), Image.LANCZOS) 418 | img = ImageTk.PhotoImage(img) 419 | if hasattr(show_qrcode, 'qrcode_label'): 420 | show_qrcode.qrcode_label.config(image=img) 421 | show_qrcode.qrcode_label.image = img 422 | show_qrcode.qrcode_label.pack() # 确保标签显示 423 | else: 424 | qrcode_label = tk.Label(main_frame, image=img) 425 | qrcode_label.image = img 426 | qrcode_label.pack() 427 | show_qrcode.qrcode_label = qrcode_label 428 | 429 | # 显示二维码的按钮 430 | qrcode_button = tk.Button(main_frame, text="显示二维码", command=show_qrcode) 431 | qrcode_button.pack() 432 | 433 | def create_hit_textbox(): 434 | if hasattr(create_hit_textbox, 'hit_frame'): 435 | # 如果存在,显示现有的输入框 436 | create_hit_textbox.hit_frame.pack(pady=10) 437 | else: 438 | # 否则创建新的输入框 439 | hit_frame = tk.Frame(main_frame) 440 | hit_frame.pack(pady=10) 441 | 442 | create_hit_textbox.hit_frame = hit_frame 443 | hit_label = tk.Label(hit_frame, text="血量实时强度百分比:(原为100%)") 444 | hit_label.pack(side=tk.TOP) 445 | global hit_field 446 | hit_field = tk.Text(hit_frame, height=1, width=5) 447 | hit_field.pack(side=tk.TOP) 448 | update_hit_field() 449 | 450 | save_button = tk.Button(hit_frame, text="保存", command=save_hit_field) 451 | save_button.pack(side=tk.TOP) 452 | 453 | def show_section(section): 454 | # 隐藏所有组件 455 | strength_a_label.pack_forget() 456 | strength_b_label.pack_forget() 457 | qrcode_button.pack_forget() 458 | if hasattr(create_hit_textbox, 'hit_frame'): 459 | create_hit_textbox.hit_frame.pack_forget() 460 | if hasattr(show_qrcode, 'qrcode_label'): 461 | show_qrcode.qrcode_label.pack_forget() 462 | 463 | if section == 'data': 464 | # 显示通道强度和二维码按钮 465 | strength_a_label.pack() 466 | strength_b_label.pack() 467 | qrcode_button.pack() 468 | show_qrcode() # 显示二维码 469 | elif section == 'hit': 470 | # 显示血量强度衰减选项 471 | create_hit_textbox() 472 | 473 | update_strength_labels() 474 | root.mainloop() 475 | 476 | if __name__ == "__main__": 477 | import multiprocessing 478 | multiprocessing.freeze_support() 479 | asyncio.run(main()) --------------------------------------------------------------------------------