修改demo.py使所有类别的输出在同一窗口并输出mAP的值

本文介绍了如何修改Faster-RCNN在TensorFlow中的demo.py,使得同一图片的不同类别检测结果能在一个窗口显示,并详细说明了如何添加代码以输出mAP值,用于评估模型的检测性能。

(一)所有类别的输出在同一窗口

在上一篇博客中我跑通了train.py和demo.py,得到了输出结果,但是发现如果一张图片检测出来有不同的类别,它是分开输出的,如下图所示:

  

所以需要将demo.py代码进行修改,让同一张图片的所有类别输出在同一窗口上。

我是参考下面这个博客进行修改代码的:

https://blog.youkuaiyun.com/10km/article/details/68926498

(1)在这里添加ax参数



(2)删除以下代码

  

(3)在下述地方添加上面删除的代码

import PySimpleGUI as Py import time import os import hashlib import logging Py.theme('Default') # PySimpleGUI 设置主题颜色,接近护眼色的内置主题:'LightGreen''TanBlue''DefaultNoMoreNagging''GreenTan' # ======== 原始主程序内容从这里开始 ======== image_path2 = "D:/software/demo/pythonProject/images/image2.png" image_path3 = "D:/software/demo/pythonProject/images/image3.png" image_path4 = "D:/software/demo/pythonProject/images/image4.png" image_path5 = "D:/software/demo/pythonProject/images/image5.png" image_path6 = "D:/software/demo/pythonProject/images/image6.png" image_path7 = "D:/software/demo/pythonProject/images/image7.png" image_path8 = "D:/software/demo/pythonProject/images/image8.png" image_path9 = "D:/software/demo/pythonProject/images/image9.png" # 检查图片是否存在 if not os.path.exists(image_path2): print(f"[!] 图片2不存在: {image_path2}") image_path2 = None if not os.path.exists(image_path3): print(f"[!] 图片3不存在: {image_path3}") image_path3 = None if not os.path.exists(image_path4): print(f"[!] 图片4不存在: {image_path4}") image_path4 = None if not os.path.exists(image_path5): print(f"[!] 图片5不存在: {image_path5}") image_path5 = None if not os.path.exists(image_path6): print(f"[!] 图片6不存在: {image_path6}") image_path6 = None if not os.path.exists(image_path7): print(f"[!] 图片7不存在: {image_path7}") image_path7 = None if not os.path.exists(image_path8): print(f"[!] 图片8不存在: {image_path8}") image_path8 = None if not os.path.exists(image_path9): print(f"[!] 图片9不存在: {image_path9}") image_path9 = None a = ' 漫息!' # 其显示收到layout2 = [size=(170, 10)]中10行显示的限制 b = '【' layout1 = [[Py.Button(b,button_color=('#006400', 'white'), font=("华文楷体", 12))]] window1_position = (514, 24) # 定义主窗口的布局,这里设置了窗口在屏幕上的起始位置为(横坐标50, 纵坐标50) window1 = Py.Window('', layout1, size=(1532, 44), no_titlebar=True, location=window1_position, finalize=True) window1.keep_on_top_set() layout2 = [[Py.Image(key='img2', filename=image_path2) if image_path2 else Py.Text("加载失败")], [Py.Text(a, size=(23, 200), auto_size_text=True)]] window2_position = (2340, 145) # 屏幕尺寸2560*1440 window2 = Py.Window('', layout2, size=(220, 750), no_titlebar=True, location=window2_position, finalize=True) window2.keep_on_top_set() # 全局标志防止重复运行 reminder_running = False def run_periodic_reminder(): global reminder_running if reminder_running: print("[!] 提醒功能已在运行,忽略重复调用") return reminder_running = True layout9 = [[Py.Image(key='img9', filename=image_path9) if image_path9 else Py.Text("加载失败")], [Py.Text("天", font=("华文楷体", 10), text_color='blue')], [Py.Text("📌 周期性提醒已激活:每次取消后10秒自动重试")], [Py.Multiline("", size=(60, 4), key='-OUTPUT-', disabled=True, autoscroll=True)], [Py.Button('退出', key='EXIT', button_color=('#800000', 'Silver')), Py.Text("", font=("华文楷体", 10), text_color='red')]] window9_position = (1795, 300) window9 = Py.Window('', layout9, size=(460, 490), finalize=True, location=window9_position) window9.keep_on_top_set() next_popup_time = time.time() + 1 reminder_active = True popup_active = False input_popup = None def show_input_popup(): layout_popup = [[Py.Text(':')], [Py.Input(key='-INPUT-', size=(6, 1), font=("Arial", 20), focus=True), Py.Text('或', font=("华文楷体", 16), text_color='#000000'), Py.Text('动', font=("华文楷体", 22), text_color='red', background_color='#86A8FF')], [Py.Button('属于', button_color=('#800000', 'white'), bind_return_key=True), Py.Push(), Py.Button('不属于', button_color=('#000000', 'white'))]] windowpopup_position = (1890, 680) return Py.Window('', layout_popup, keep_on_top=True, modal=True, location=windowpopup_position, finalize=True, grab_anywhere=False) try: while True: # === 安全 read():用 try-except 捕获 TclError === try: event9, values9 = window9.read(timeout=100) except (RuntimeError, Exception) as e: # 常见于窗口关闭后底层 TK 资源已释放 if "wrapped C/C++ object has been deleted" in str(e) or "TclError" in str(e): print("[INFO] 窗口已被系统关闭,退出提醒循环。") break else: print(f"[ERROR] Unexpected error: {e}") break if event9 in (None, 'EXIT', Py.WINDOW_CLOSED): # 如果 read 返回 None,说明窗口被关闭 layout10 = [[Py.Image(key='img2', filename=image_path2) if image_path2 else Py.Text("加载失败")], [Py.Text(a, size=(23, 200), auto_size_text=True)]] window10_position = (2340, 145) # 屏幕尺寸2560*1440 window10 = Py.Window('', layout10, size=(220, 750), location=window10_position, finalize=True) window10.keep_on_top_set() break now = time.time() # 自动弹出输入框逻辑 if reminder_active and not popup_active and next_popup_time and now >= next_popup_time: popup_active = True try: input_popup = show_input_popup() except Exception as e: print(f"[Error] 无法创建弹窗: {e}") popup_active = False next_popup_time = now + 10 continue if input_popup: # 处理输入弹窗 try: event_p, values_p = input_popup.read(timeout=100) except (RuntimeError, Exception): input_popup = None popup_active = False continue if event_p == '属于': user_input = values_p['-INPUT-'].strip() try: num = float(user_input) formatted_num = int(num) if num.is_integer() else num window9['-OUTPUT-'].update(f"✔️ 录入成功:{formatted_num} —— 提醒停止\n", append=True) reminder_active = False next_popup_time = None except ValueError: window9['-OUTPUT-'].update(f"❌ 输入无效:'{user_input}',60秒后重试...\n", append=True) next_popup_time = time.time() + 60 finally: try: input_popup.close() except: pass input_popup = None popup_active = False elif event_p in (None, '不属于', Py.WINDOW_CLOSED): try: input_popup.close() except: pass input_popup = None popup_active = False window9['-OUTPUT-'].update("⛔ 用户取消,60秒后将再次提醒...\n", append=True) next_popup_time = time.time() + 60 finally: # 确保清理 if 'window9' in locals() or window9 is not None: try: window9.close() except: pass if input_popup is not None: try: input_popup.close() except: pass reminder_running = False def run_single_flow(): """运行一次完整的 window4 流程""" Py.theme('LightBlue') input_history = [] # 存储已确认的数字 last_input_time = None INPUT_TIMEOUT = 5.0 current_text = '' # 使用一个固定宽度的 Text 框实现横向排列 + 自动换行 layout4 = [[Py.Image(key='img4', filename=image_path4) if image_path4 else Py.Text("加载失败")], [Py.Text("请在5秒内输入当前动量,之后5秒无操作将自动记录:")], [Py.Input(key='-INPUT-', size=(10, 1), font=("Arial", 20), focus=True), Py.Text("", size=(60, 1), key='-STATUS-')], [Py.Text(":")], # 设置size=(None, 10)表示不限宽度列,高度最多10行;text_color和background_color提升可读性 [Py.Frame('', [[Py.Text("", size=(48, 15), key='-HISTORY-', relief='sunken', background_color='white', text_color='black', font=('Courier', 15))]], size=(590, 75), pad=10)], # 外层长590*75,最多10行,字体大小为15,内层长度48超出则自动换行,控制内层显示区域大小以便触发换行,内层过长超过外层将会看不到换行 [Py.Button('美边', button_color=('#006400', 'Silver'))], # 用一个大括号使他们在同一行显示 [Py.Button('同向', button_color=('#000000', 'white')), Py.Button('不符合', button_color=('#800000', 'Silver'))]] window4_position = (1795, 300) window4 = Py.Window('', layout4, size=(460, 500), resizable=True, location=window4_position, finalize=True) window4.keep_on_top_set() while True: event4, values4 = window4.read(timeout=100) if event4 == Py.WINDOW_CLOSED: break new_text = values4['-INPUT-'].strip() input_changed = new_text != current_text current_text = new_text valid_number = False # 验证是否为有效数字 try: if current_text: float(current_text) valid_number = True except ValueError: pass if input_changed and valid_number: # 输入变化且合法 → 重置计时器 last_input_time = time.time() window4['-STATUS-'].update(f"✅ 输入中 '{current_text}' ... 5秒无操作将自动提交") if last_input_time is not None: # 超时自动提交 elapsed = time.time() - last_input_time if elapsed >= INPUT_TIMEOUT: try: num = float(current_text) formatted_num = int(num) if num.is_integer() else num input_history.append(formatted_num) history_str = ' '.join(map(str, input_history)) # 更新历史显示:空格分隔,让系统自动换行 window4['-HISTORY-'].update(history_str) window4['-STATUS-'].update("🎉 已自动记录!") window4['-INPUT-'].update('') current_text = '' last_input_time = None except Exception as e: window4['-STATUS-'].update("❌ 提交失败") last_input_time = None elif last_input_time is None and current_text and not valid_number: # 状态栏提示 window4['-STATUS-'].update("❌ 请输入有效的数字") elif last_input_time and valid_number: remaining = max(0, int(INPUT_TIMEOUT - (time.time() - last_input_time) + 0.9)) if remaining > 0: window4['-STATUS-'].update(f"⏳ 还剩 {remaining} 秒自动提交...") if event4 in (Py.WIN_CLOSED, '不符合'): window4.close() elif event4 == '同向': window4.close() layout5 = [[Py.Image(key='img5', filename=image_path5) if image_path5 else Py.Text("加载失败")], [Py.Text("请在5秒内输入当前动量,之后5秒无操作将自动记录:")], [Py.Input(key='-INPUT-', size=(10, 1), font=("Arial", 20)), Py.Text("", size=(60, 1), key='-STATUS-')], # 设置 size=(None, 10) 表示不限宽度列,高度最多10行;text_color 和 background_color 提升可读性 [Py.Frame('', [[Py.Text("", size=(49, 15), key='-HISTORY-', relief='sunken', background_color='white', text_color='black', font=('Courier', 15))]], size=(410, 50), pad=10)], # 控制整体显示区域大小以便触发换行 [Py.Button('进入弱', button_color=('#000000', 'Silver')), Py.Push(), Py.Button('进入强', button_color=('#000000', 'Silver'))]] window5_position = (1795, 300) window5 = Py.Window('', layout5, size=(460, 430), resizable=True, location=window5_position, finalize=True) window5.keep_on_top_set() while True: event5, values5 = window5.read(timeout=100) if event5 == Py.WINDOW_CLOSED: break new_text = values5['-INPUT-'].strip() input_changed = new_text != current_text current_text = new_text valid_number = False # 验证是否为有效数字 try: if current_text: float(current_text) valid_number = True except ValueError: pass if input_changed and valid_number: # 输入变化且合法 → 重置计时器 last_input_time = time.time() window5['-STATUS-'].update(f"✅ 输入中 '{current_text}' ... 5秒无操作将自动提交") if last_input_time is not None: # 超时自动提交 elapsed = time.time() - last_input_time if elapsed >= INPUT_TIMEOUT: try: num = float(current_text) formatted_num = int(num) if num.is_integer() else num input_history.append(formatted_num) history_str = ' '.join(map(str, input_history)) # 更新历史显示:空格分隔,让系统自动换行 window5['-HISTORY-'].update(history_str) window5['-STATUS-'].update("🎉 已自动记录!") window5['-INPUT-'].update('') current_text = '' last_input_time = None except Exception as e: window5['-STATUS-'].update("❌ 提交失败") last_input_time = None elif last_input_time is None and current_text and not valid_number: # 状态栏提示 window5['-STATUS-'].update("❌ 请输入有效的数字") elif last_input_time and valid_number: remaining = max(0, int(INPUT_TIMEOUT - (time.time() - last_input_time) + 0.9)) if remaining > 0: window5['-STATUS-'].update(f"⏳ 还剩 {remaining} 秒自动提交...") elif event5 == '进入弱': window5.close() Py.theme('LightBlue') recorded_numbers = [] # 历史记录列表 layout6 = [[Py.Image(key='img6', filename=image_path6) if image_path6 else Py.Text("加载失败")], [Py.Input(key='-INPUT-', size=(10, 1), font=("Arial", 20), do_not_clear=False)], # do_not_clear=False 表示默认清空 [Py.Button('【中进】', button_color=('#006400', 'Silver'), bind_return_key=True), Py.Button('不于进', button_color=('#800000', 'Silver'))], [Py.HorizontalSeparator()], [Py.Text("", size=(50, 2), key='-HISTORY-', relief='sunken', background_color='white', text_color='black')]] window6_position = (1795, 300) window6 = Py.Window('', layout6, size=(460, 395), location=window6_position, finalize=True) window6.keep_on_top_set() while True: event6, values6 = window6.read() if event6 == Py.WINDOW_CLOSED: break input_value = values6['-INPUT-'].strip() if event6 == '【中进】': window2.close() if input_value == '': window6['-HISTORY-'].update("⚠️ 输入为空,无法保留") else: try: num = float(input_value) formatted_num = int(num) if num.is_integer() else num recorded_numbers.append(formatted_num) history_text = ' '.join(map(str, recorded_numbers)) window6['-HISTORY-'].update(history_text) window6['-INPUT-'].update('') # ✅ 在 close 前更新 except ValueError: # 注意:这里 '-STATUS-' 未定义,建议检查 layout 是否包含该 key pass # 避免异常中断 window6.close() # ✅ 关闭放最后 run_periodic_reminder() elif event6 == '不于进': window6['-INPUT-'].update('') # 直接清空输入框 elif event5 == '进入强': window5.close() Py.theme('LightBlue') recorded_numbers = [] # 历史记录列表 layout7 = [[Py.Image(key='img7', filename=image_path7) if image_path7 else Py.Text("加载失败")], [Py.Text("请:")], [Py.Input(key='-INPUT-', size=(10, 1), font=("Arial", 20), do_not_clear=False)], # do_not_clear=False表示默认清空 [Py.Button('出现【再', button_color=('#006400', 'Silver'), bind_return_key=True), Py.Button('不于进', button_color=('#800000', 'Silver'))], [Py.HorizontalSeparator()], [Py.Text("", size=(53, 2), key='-HISTORY-', relief='sunken', background_color='white', text_color='black')]] window7_position = (1795, 350) window7 = Py.Window('', layout7, size=(460, 450), location=window7_position, finalize=True) window7.keep_on_top_set() while True: event7, values7 = window7.read() if event7 == Py.WINDOW_CLOSED: break input_value = values7['-INPUT-'].strip() if event7 == '出现【再': window2.close() if input_value == '': window7['-HISTORY-'].update("⚠️ 输入为空,无法保留") else: try: num = float(input_value) formatted_num = int(num) if num.is_integer() else num recorded_numbers.append(formatted_num) history_text = ' '.join(map(str, recorded_numbers)) window7['-HISTORY-'].update(history_text) window7['-INPUT-'].update('') # ✅ 先更新 except ValueError: pass window7.close() # ✅ 再关闭 run_periodic_reminder() elif event7 == '不于进': # 直接清空输入框 window7['-INPUT-'].update('') elif event4 == '美边': window2.close() window4.close() run_periodic_reminder() # 弹出礼物窗口 while True: # 主事件循环 event1, values1 = window1.read() if event1 == Py.WIN_CLOSED: break if event1 == b: layout3 = [[Py.Image(key='img3', filename=image_path3) if image_path3 else Py.Text("加载失败")], [Py.Button('第1', button_color=('#006400', 'white')),Py.Push(), Py.Button('第3', button_color=('#006400', 'white'))]] # 用一个大括号使他们在同一行显示 window3_position = (1795, 400) window3 = Py.Window('交易类型', layout3, size=(460, 190), location=window3_position, finalize=True, keep_on_top=True) window3.keep_on_top_set() while True: event3, values3 = window3.read() if event3 == Py.WINDOW_CLOSED: break if event3 == '第1': window3.close() run_single_flow() if event3 == '第3': window3.close() layout8 = [[Py.Image(key='img8', filename=image_path8) if image_path8 else Py.Text("加载失败")], [Py.Text("决战", font=("华文楷体", 13), text_color='blue')], [Py.Input(key='-INPUT-', size=(10, 1), font=("Arial", 20), focus=True), Py.Text("", size=(60, 1), key='-STATUS-')], # 设置size=(None, 10)表示不限宽度列,高度最多10行;text_color和background_color提升可读性 [Py.Frame('', [ [Py.Text("", size=(48, 15), key='-HISTORY-', relief='sunken', background_color='white', text_color='black', font=('Courier', 15))]], size=(590, 75), pad=10)], # 外层长590*75,最多10行,字体大小为15,内层长度48超出则自动换行,控制内层显示区域大小以便触发换行,内层过长超过外层将会看不到换行 [Py.Button('败退', button_color=('#006400', 'Silver')), Py.Button('反击', button_color=('#006400', 'Silver')), # 用一个大括号使他们在同一行显示 Py.Button('不符合', button_color=('#800000', 'Silver'))]] window8_position = (1795, 300) window8 = Py.Window('', layout8, size=(460, 470), resizable=True, location=window8_position, finalize=True) window8.keep_on_top_set() while True: event8, values8 = window8.read(timeout=100) if event8 == Py.WINDOW_CLOSED: break if event8 in (Py.WIN_CLOSED, '不符合'): window8.close() if event8 == '败退' or event8 == '反击': window8.close() window2.close() run_periodic_reminder() if event8 == '不符合': window8.close() 在窗口9的点击退出按钮后会出现窗口10,然后在窗口4的美边按钮,窗口6的中进按钮,窗口7的出现【再按钮,窗口8的败退和反击按钮,在点击上述按钮时候窗口10关闭
最新发布
11-14
package com.example.demo.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.thread.ThreadFactoryBuilder; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.dao.entity.WExecuteHost; import com.example.demo.dao.entity.WHostProcess; import com.example.demo.dao.entity.WPersonalHost; import com.example.demo.dao.mapper.WExecuteHostMapper; import com.example.demo.request.ProcessRequest; import com.example.demo.service.WExecuteHostService; import com.example.demo.service.WHostProcessService; import com.example.demo.service.WPersonalHostService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.interceptor.TransactionAspectSupport; import javax.annotation.PreDestroy; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @Service public class WExecuteHostServiceImpl extends ServiceImpl<WExecuteHostMapper, WExecuteHost> implements WExecuteHostService { @Autowired private WPersonalHostService wPersonalHostService; @Autowired private WHostProcessService wHostProcessService; @Data private static class UserSession { private Process cmdProcess; private volatile boolean isProcessRunning; private String sessionId; private String logFilePath; private final Queue<String> logBuffer = new ConcurrentLinkedQueue<>(); private static final int BATCH_SIZE = 50; // 批量写入阈 // 命令执行状态锁 private final AtomicBoolean isExecuting = new AtomicBoolean(false); } private final Map<String, UserSession> userSessions = new ConcurrentHashMap<>(); private final SimpMessagingTemplate messagingTemplate; // 异步日志写入线程池 private static final ExecutorService LOG_WRITER_POOL = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNamePrefix("log-writer-").build()); // 日志刷新调度器 private static final ScheduledExecutorService LOG_FLUSH_SCHEDULER = Executors.newSingleThreadScheduledExecutor(); public WExecuteHostServiceImpl(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; startLogFlushService(); } // 初始化日志刷新服务 private void startLogFlushService() { LOG_FLUSH_SCHEDULER.scheduleAtFixedRate(() -> { userSessions.values().forEach(session -> { if (!session.getLogBuffer().isEmpty()) { List<String> batch = CollUtil.newArrayList(session.getLogBuffer()); session.getLogBuffer().clear(); asyncWriteLog(session.getLogFilePath(), String.join("\n", batch)); } }); }, 0, 1, TimeUnit.SECONDS); } @Override public void executeCommand(String command, String host, String userId) { // 0. ABORT命令特殊处理(优先处理终止请求) if ("ABORT".equalsIgnoreCase(command)) { handleAbort(userId); return; } // 1. 权限校验 if (!validateUserHost(userId, host)) { sendError("无权访问该主机", userId); return; } // 2. 检查用户当前会话状态 UserSession session = userSessions.get(userId); if (session != null && session.isExecuting.get()) { sendError("已有命令执行中,请等待完成或使用ABORT终止", userId); return; } // 3. 创建新会话(带原子状态检查) session = userSessions.computeIfAbsent(userId, key -> { UserSession newSession = createNewSession(userId, host); if (newSession != null) { newSession.isExecuting.set(true); // 标记为执行中 } return newSession; }); if (session == null) return; // 4. 写入日志执行命令 try { // 确保获得执行锁 if (!session.isExecuting.compareAndSet(true, true)) { sendError("命令执行冲突,请重试", userId); return; } session.getLogBuffer().offer("——————————————— " + DateUtil.now() + " ———————————————"); // 发送命令到进程 IoUtil.write(session.getCmdProcess().getOutputStream(), Charset.forName("gbk"), true, command + "\n"); } catch (Exception e) { session.isExecuting.set(false); // 发生异常时释放锁 sendError("命令发送失败: " + e.getMessage(), userId); } } @Override public Boolean isCommandExecuting(String userId) { UserSession session = userSessions.get(userId); return session != null && session.isExecuting.get(); } @Override public void handleAbort(String userId) { UserSession session = userSessions.get(userId); if (session == null || session.getCmdProcess() == null) { sendError("没有活动的命令进程", userId); return; } try { long pid = session.getCmdProcess().pid(); System.out.println("Attempting to kill process with PID: " + pid); // 使用 taskkill 命令终止进程 ProcessBuilder taskKill = new ProcessBuilder("taskkill", "/F", "/T", "/PID", String.valueOf(pid)); Process killProcess = taskKill.start(); // 等待命令执行完成 int exitCode = killProcess.waitFor(); System.out.println("taskkill exit code: " + exitCode); if (exitCode == 0) { // 进程终止成功 session.isExecuting.set(false); cleanupSession(userId); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "✔️" + "进程已通过 taskkill 终止 (PID: " + pid + ")"); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, ""); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, System.getProperty("user.dir") + ">"); } else { // 进程终止失败 sendError("终止进程失败,错误码: " + exitCode, userId); } } catch (IOException | InterruptedException e) { System.err.println("Error killing process: " + e.getMessage()); sendError("终止进程失败: " + e.getMessage(), userId); } } @Override public String startProcess(ProcessRequest processRequest) { try { // 数据库表新增数据 String p13 = processRequest.getP13().trim(); String processName = processRequest.getProcessName().trim(); String productNumber = processRequest.getProductNumber().trim(); String executeHost = processRequest.getExecuteHost().trim(); String department = processRequest.getDepartment().trim(); String version = processRequest.getVersion().trim(); if (StrUtil.isEmpty(p13) || StrUtil.isEmpty(processName) || StrUtil.isEmpty(productNumber) || StrUtil.isEmpty(executeHost) || StrUtil.isEmpty(department) || StrUtil.isEmpty(version)) { return "新增进程失败。"; } WHostProcess wHostProcess = new WHostProcess(); wHostProcess.setP13(p13); wHostProcess.setProcessName(processName); wHostProcess.setProductNumber(productNumber); wHostProcess.setHost(executeHost); wHostProcess.setPid(null); wHostProcess.setDepartment(department); wHostProcess.setState("离线"); wHostProcess.setVersion(version); wHostProcess.setBeginTime(new Date()); boolean saveResult = wHostProcessService.save(wHostProcess); if (saveResult) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper .eq(WPersonalHost::getExecuteHost, processRequest.getExecuteHost()) .eq(WPersonalHost::getSharedHost, processRequest.getSharedHost()); WPersonalHost wPersonalHost = wPersonalHostService.getOne(queryWrapper); // 执行py启动命令 //todo 后续动态 String pythonEXEPath = "D:\\miniforge\\envs" + File.separator + p13 + File.separator + "python" + wPersonalHost.getPythonEnv() + File.separator + "python.exe"; String mainPyPath = System.getProperty("user.dir") + File.separator + "python-package" + File.separator + executeHost + File.separator + p13 + File.separator + "test" + File.separator + "main.py"; executeCommand(pythonEXEPath + " " + mainPyPath, processRequest.getExecuteHost(), processRequest.getP13()); return "正在启动项目..."; } } catch (Exception e) { e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } return "新增进程失败。"; } private boolean validateUserHost(String userId, String executeHost) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(WPersonalHost::getP13, userId) .eq(WPersonalHost::getExecuteHost, executeHost) .eq(WPersonalHost::getState, "在线"); return wPersonalHostService.getOne(queryWrapper) != null; } private UserSession createNewSession(String userId, String executeHost) { try { UserSession session = new UserSession(); session.setSessionId(UUID.randomUUID().toString()); session.setLogFilePath(initLogFile(userId, executeHost)); // 启动CMD进程(带唯一标题) ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/k", "title " + generateUniqueTitle(userId)); session.setCmdProcess(pb.redirectErrorStream(true).start()); // 启动输出监听线程 startOutputThread(session, userId); return session; } catch (IOException e) { sendError("进程启动失败: " + e.getMessage(), userId); return null; } } private String initLogFile(String userId, String executeHost) { // 1. 构建基础路径(使用File.separator) String baseDir = FileUtil.normalize( System.getProperty("user.dir") + File.separator + "command-log"); // 2. 构建安全路径(统一使用File.separator) String safePath = FileUtil.normalize( baseDir + File.separator + userId + File.separator + executeHost + File.separator + "项目名称"); // 3. 安全校验(现在路径分隔符一致) if (!safePath.startsWith(baseDir)) { throw new SecurityException("非法日志路径: " + safePath); } // 4. 创建目录(自动处理路径分隔符) FileUtil.mkdir(safePath); // 5. 生成日志文件 String logFileName = DateUtil.today() + ".log"; return FileUtil.touch(safePath + File.separator + logFileName).getAbsolutePath(); } private void startOutputThread(UserSession session, String userId) { new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(session.getCmdProcess().getInputStream(), "GBK"))) { String line; while ((line = reader.readLine()) != null) { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, line); session.getLogBuffer().offer(line); } } catch (Exception e) { sendError("输出流异常: " + e.getMessage(), userId); } finally { session.isExecuting.set(false); // 命令结束释放锁 cleanupSession(userId); // 通知前端命令执行结束 messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "⏹ 该命令执行已结束"); } }).start(); } private void asyncWriteLog(String logFilePath, String content) { CompletableFuture.runAsync(() -> { try { // 替换掉日志文件中没用的信息,如多余的路径信息 String currentDir = System.getProperty("user.dir"); String escapedDir = currentDir.replace("\\", "\\\\"); String regex = "(?m)^\\s*" + escapedDir + ">(?!\\S)\\s*"; // 创建 Pattern 和 Matcher 对象 Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); // 检查是否存在匹配的模式 if (matcher.find()) { // 如果存在匹配,则进行替换 String cleaned = content.replaceAll(regex, ""); FileUtil.appendString(cleaned + System.lineSeparator(), logFilePath, "GBK"); } else { FileUtil.appendString(content + System.lineSeparator(), logFilePath, "GBK"); } } catch (Exception e) { System.err.println("日志写入失败: " + e.getMessage()); } }, LOG_WRITER_POOL); } private void cleanupSession(String userId) { UserSession session = userSessions.remove(userId); if (session != null) { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } // 强制将剩余日志写入文件(新增代码) if (!session.getLogBuffer().isEmpty()) { asyncWriteLog(session.getLogFilePath(), String.join("\n", session.getLogBuffer())); session.getLogBuffer().clear(); } } catch (Exception ignored) { } } } @PreDestroy public void cleanup() { LOG_FLUSH_SCHEDULER.shutdown(); LOG_WRITER_POOL.shutdown(); userSessions.forEach((userId, session) -> { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } } catch (Exception ignored) { } }); userSessions.clear(); } /** * 发送错误日志 */ private void sendError(String message, String userId) { try { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "❌" + message); } catch (Exception ignored) { } } /** * 生成cmd窗口唯一id */ private String generateUniqueTitle(String userId) { return "CMD_SESSION_" + userId + "_" + System.currentTimeMillis(); } }我有个问题,为什么我执行main.py文件#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 输出1到10的Python脚本 """ for i in range(1, 11): print(i)。输出的数字不是按照顺序的,是凌乱的1 2 10 5 3 7 4 8 9 6 但如果我用本地的cmd窗口,执行,就是正常的
06-18
import PySimpleGUI as Py import time import os image_path3 = "D:/software/demo/pythonProject/images/image3.png" image_path4 = "D:/software/demo/pythonProject/images/image4.png" image_path5 = "D:/software/demo/pythonProject/images/image5.png" image_path6 = "D:/software/demo/pythonProject/images/image6.png" image_path7 = "D:/software/demo/pythonProject/images/image7.png" # 检查图片是否存在 if not os.path.exists(image_path3): print(f"[!] 图片3不存在: {image_path3}") image_path3 = None if not os.path.exists(image_path4): print(f"[!] 图片4不存在: {image_path4}") image_path4 = None if not os.path.exists(image_path5): print(f"[!] 图片5不存在: {image_path5}") image_path5 = None if not os.path.exists(image_path6): print(f"[!] 图片6不存在: {image_path6}") image_path6 = None if not os.path.exists(image_path7): print(f"[!] 图片7不存在: {image_path7}") image_path7 = None b = 'zbjszj' layout1 = [[Py.Text('jdsq---')], [Py.Button('tips:zb'), Py.Button('wykd')]] window_position = (435, 0) # 定义主窗口的布局,这里设置了窗口在屏幕上的起始位置为(横坐标50, 纵坐标50) window1 = Py.Window('jdsq', layout1, size=(1665, 68), no_titlebar=True, location=window_position, finalize=True) window1.keep_on_top_set() Py.theme('DarkBlue3') # PySimpleGUI 设置主题颜色 # 全局标志防止重复运行 reminder_running = False def run_periodic_reminder(): global reminder_running if reminder_running: print("[!] 提醒功能已在运行,忽略重复调用") return reminder_running = True layout7 = [[Py.Image(key='img7', filename=image_path7) if image_path7 else Py.Text("加载失败")], [Py.Text("txdj")], [Py.Text("📌 周期性提醒已激活:每次取消后10秒自动重试")], [Py.Multiline("", size=(60, 4), key='-OUTPUT-', disabled=True, autoscroll=True)], [Py.Button('yzxpc', key='EXIT')]] window7 = Py.Window('jbdz:cc', layout7, size=(475, 480), finalize=True) window7.keep_on_top_set() next_popup_time = time.time() + 1 reminder_active = True popup_active = False input_popup = None def show_input_popup(): layout_popup = [[Py.Text('mbwbyd')], [Py.Input(key='-INPUT-', size=(15, 1), focus=True)], [Py.Button('fsydlc', bind_return_key=True), Py.Button('bsyyd')]] return Py.Window('xkdz', layout_popup, keep_on_top=True, modal=True, finalize=True, grab_anywhere=False) try: while True: # === 安全 read():用 try-except 捕获 TclError === try: event7, values7 = window7.read(timeout=100) except (RuntimeError, Exception) as e: # 常见于窗口关闭后底层 TK 资源已释放 if "wrapped C/C++ object has been deleted" in str(e) or "TclError" in str(e): print("[INFO] 窗口已被系统关闭,yzxpc提醒循环。") break else: print(f"[ERROR] Unexpected error: {e}") break if event7 in (None, 'EXIT', Py.WINDOW_CLOSED): # 如果 read 返回 None,说明窗口被关闭 break now = time.time() # 自动弹出输入框逻辑 if reminder_active and not popup_active and next_popup_time and now >= next_popup_time: popup_active = True try: input_popup = show_input_popup() except Exception as e: print(f"[Error] 无法创建弹窗: {e}") popup_active = False next_popup_time = now + 10 continue if input_popup: # 处理输入弹窗 try: event_p, values_p = input_popup.read(timeout=100) except (RuntimeError, Exception): input_popup = None popup_active = False continue if event_p == 'fsydlc': user_input = values_p['-INPUT-'].strip() try: num = float(user_input) formatted_num = int(num) if num.is_integer() else num window7['-OUTPUT-'].update(f"✔️ 录入成功:{formatted_num} —— 提醒停止\n", append=True) reminder_active = False next_popup_time = None except ValueError: window7['-OUTPUT-'].update(f"❌ 输入无效:'{user_input}',10秒后重试...\n", append=True) next_popup_time = time.time() + 10 finally: try: input_popup.close() except: pass input_popup = None popup_active = False elif event_p in (None, 'bsyyd', Py.WINDOW_CLOSED): try: input_popup.close() except: pass input_popup = None popup_active = False window7['-OUTPUT-'].update("⛔ 用户取消,10秒后将再次提醒...\n", append=True) next_popup_time = time.time() + 10 finally: # 确保清理 if 'window7' in locals() or window7 is not None: try: window7.close() except: pass if input_popup is not None: try: input_popup.close() except: pass reminder_running = False def run_single_flow(): """运行一次完整的 window3 流程""" Py.theme('LightBlue') input_history = [] # 存储已确认的数字 last_input_time = None INPUT_TIMEOUT = 5.0 current_text = '' # 使用一个固定宽度的 Text 框实现横向排列 + 自动换行 layout3 = [[Py.Image(key='img3', filename=image_path3) if image_path3 else Py.Text("加载失败")], [Py.Text("rmxglx")], [Py.Text("请在5秒内输入当前数字,之后5秒无操作将自动记录:")], [Py.Input(key='-INPUT-', size=(20, 1), focus=True)], [Py.Text("", size=(60, 1), key='-STATUS-')], [Py.Text("已记录的数字(横向排列,自动换行):")], # 设置 size=(None, 10) 表示不限宽度列,高度最多10行;text_color 和 background_color 提升可读性 [Py.Frame('', [[Py.Text("", size=(590, 10), key='-HISTORY-', relief='sunken', background_color='white', text_color='black', font=('Courier', 10))]], size=(590, 50), pad=10)], # 长度约600 字符,超出则自动换行,最多10行,控制整体显示区域大小以便触发换行 [Py.Button('mphcxxl'), Py.Button('jchdj')], # 用一个大括号使他们在同一行显示 [Py.Button('txlxk'), Py.Button('bfhycg')]] window3 = Py.Window('jbdz:pddb', layout3, size=(630, 565), resizable=True, finalize=True) window3.keep_on_top_set() while True: event3, values3 = window3.read(timeout=100) if event3 == Py.WINDOW_CLOSED: break new_text = values3['-INPUT-'].strip() input_changed = new_text != current_text current_text = new_text valid_number = False # 验证是否为有效数字 try: if current_text: float(current_text) valid_number = True except ValueError: pass if input_changed and valid_number: # 输入变化且合法 → 重置计时器 last_input_time = time.time() window3['-STATUS-'].update(f"✅ 输入中 '{current_text}' ... 5秒无操作将自动提交") if last_input_time is not None: # 超时自动提交 elapsed = time.time() - last_input_time if elapsed >= INPUT_TIMEOUT: try: num = float(current_text) formatted_num = int(num) if num.is_integer() else num input_history.append(formatted_num) history_str = ' '.join(map(str, input_history)) # 更新历史显示:空格分隔,让系统自动换行 window3['-HISTORY-'].update(history_str) window3['-STATUS-'].update("🎉 已自动记录!") window3['-INPUT-'].update('') current_text = '' last_input_time = None except Exception as e: window3['-STATUS-'].update("❌ 提交失败") last_input_time = None elif last_input_time is None and current_text and not valid_number: # 状态栏提示 window3['-STATUS-'].update("❌ 请输入有效的数字") elif last_input_time and valid_number: remaining = max(0, int(INPUT_TIMEOUT - (time.time() - last_input_time) + 0.9)) if remaining > 0: window3['-STATUS-'].update(f"⏳ 还剩 {remaining} 秒自动提交...") if event3 in (Py.WIN_CLOSED, 'bfhycg'): window3.close() elif event3 == 'mphcxxl': window1.hide() elif event3 == 'txlxk': window3.close() layout4 = [[Py.Image(key='img4', filename=image_path4) if image_path4 else Py.Text("加载失败")], [Py.Text("请在5秒内输入当前数字,之后5秒无操作将自动记录:")], [Py.Input(key='-INPUT-', size=(20, 1), focus=True)], [Py.Text("", size=(60, 1), key='-STATUS-')], [Py.Text("已记录的数字(横向排列,自动换行):")], # 设置 size=(None, 10) 表示不限宽度列,高度最多10行;text_color 和 background_color 提升可读性 [Py.Frame('', [[Py.Text("", size=(410, 10), key='-HISTORY-', relief='sunken', background_color='white', text_color='black', font=('Courier', 10))]], size=(410, 50), pad=10)], # 控制整体显示区域大小以便触发换行 [Py.Button('jrrsby'), Py.Button('jrqsby')]] window4 = Py.Window('jbdz:pdbyqr', layout4, size=(450, 440), resizable=True, finalize=True) window4.keep_on_top_set() while True: event4, values4 = window4.read(timeout=100) if event4 == Py.WINDOW_CLOSED: break new_text = values4['-INPUT-'].strip() input_changed = new_text != current_text current_text = new_text valid_number = False # 验证是否为有效数字 try: if current_text: float(current_text) valid_number = True except ValueError: pass if input_changed and valid_number: # 输入变化且合法 → 重置计时器 last_input_time = time.time() window4['-STATUS-'].update(f"✅ 输入中 '{current_text}' ... 5秒无操作将自动提交") if last_input_time is not None: # 超时自动提交 elapsed = time.time() - last_input_time if elapsed >= INPUT_TIMEOUT: try: num = float(current_text) formatted_num = int(num) if num.is_integer() else num input_history.append(formatted_num) history_str = ' '.join(map(str, input_history)) # 更新历史显示:空格分隔,让系统自动换行 window4['-HISTORY-'].update(history_str) window4['-STATUS-'].update("🎉 已自动记录!") window4['-INPUT-'].update('') current_text = '' last_input_time = None except Exception as e: window4['-STATUS-'].update("❌ 提交失败") last_input_time = None elif last_input_time is None and current_text and not valid_number: # 状态栏提示 window4['-STATUS-'].update("❌ 请输入有效的数字") elif last_input_time and valid_number: remaining = max(0, int(INPUT_TIMEOUT - (time.time() - last_input_time) + 0.9)) if remaining > 0: window4['-STATUS-'].update(f"⏳ 还剩 {remaining} 秒自动提交...") elif event4 == 'jrrsby': window4.close() Py.theme('LightBlue') recorded_numbers = [] # 历史记录列表 layout5 = [[Py.Image(key='img5', filename=image_path5) if image_path5 else Py.Text("加载失败")], [Py.Text("请输入当前数字:")], [Py.Input(key='-INPUT-', size=(30, 1), do_not_clear=False)], # do_not_clear=False 表示默认清空 [Py.Button('cxzjqpqh', bind_return_key=True), Py.Button('bsyjcxh')], [Py.HorizontalSeparator()], [Py.Text("已保留的数字(横向排列,自动换行):")], [Py.Text("", size=(50, 2), key='-HISTORY-', relief='sunken', background_color='white', text_color='black')]] window5 = Py.Window('jbdz:rsby', layout5, finalize=True) window5.keep_on_top_set() while True: event5, values5 = window5.read() if event5 == Py.WINDOW_CLOSED: break input_value = values5['-INPUT-'].strip() if event5 == 'cxzjqpqh': window5.close() run_periodic_reminder() if input_value == '': window4['-HISTORY-'].update("⚠️ 输入为空,无法保留") else: try: num = float(input_value) formatted_num = int(num) if num.is_integer() else num recorded_numbers.append(formatted_num) history_text = ' '.join(map(str, recorded_numbers)) # 更新历史显示(横向空格分隔,自动换行) window5['-HISTORY-'].update(history_text) window5['-INPUT-'].update('') # 清空输入框(do_not_clear=False 已自动处理,但仍显式更新以防万一) except ValueError: window5['-STATUS-'].update("❌ 不是有效数字!") elif event5 == 'bsyjcxh': window5['-INPUT-'].update('') # 直接清空输入框 elif event4 == 'jrqsby': window4.close() Py.theme('LightBlue') recorded_numbers = [] # 历史记录列表 layout6 = [[Py.Image(key='img6', filename=image_path6) if image_path6 else Py.Text("加载失败")], [Py.Text("请输入当前数字:")], [Py.Input(key='-INPUT-', size=(30, 1), do_not_clear=False)], # do_not_clear=False 表示默认清空 [Py.Button('cxzcpb', bind_return_key=True), Py.Button('bsyjcxh')], [Py.HorizontalSeparator()], [Py.Text("已保留的数字(横向排列,自动换行):")], [Py.Text("", size=(50, 2), key='-HISTORY-', relief='sunken', background_color='white', text_color='black')]] window6 = Py.Window('jbdz:qsby', layout6, finalize=True) window6.keep_on_top_set() while True: event6, values6 = window6.read() if event6 == Py.WINDOW_CLOSED: break input_value = values6['-INPUT-'].strip() if event6 == 'cxzcpb': window6.close() run_periodic_reminder() if input_value == '': window6['-HISTORY-'].update("⚠️ 输入为空,无法保留") else: try: num = float(input_value) formatted_num = int(num) if num.is_integer() else num recorded_numbers.append(formatted_num) history_text = ' '.join(map(str, recorded_numbers)) # 更新历史显示(横向空格分隔,自动换行) window6['-HISTORY-'].update(history_text) window6['-INPUT-'].update('') # 清空输入框(do_not_clear=False 已自动处理,但仍显式更新以防万一) except ValueError: window6['-STATUS-'].update("❌ 不是有效数字!") elif event6 == 'bsyjcxh': # 直接清空输入框 window6['-INPUT-'].update('') elif event3 == 'jchdj': window3.close() window1.un_hide() run_periodic_reminder() # 弹出礼物窗口 while True: # 主事件循环 event1, values1 = window1.read() if event1 == Py.WIN_CLOSED: break if event1 == 'tips:zb': layout2 = [[Py.Text(b, size=(170, 2), auto_size_text=True)]] window2 = Py.Window('ktrx', layout2, size=(940, 210), finalize=True, keep_on_top=True) window2.read(timeout=5000, close=True) if event1 == 'wykd': run_single_flow() PySimpleGUI版本4.6.运行出错Traceback (most recent call last): File "D:\software\demo\pythonProject\study\mypy01.py", line 334, in <module> run_single_flow() File "D:\software\demo\pythonProject\study\mypy01.py", line 277, in run_single_flow window5['-INPUT-'].update('') # 清空输入框(do_not_clear=False 已自动处理,但仍显式更新以防万一) File "D:\software\demo\pythonProject\venv\lib\site-packages\PySimpleGUI\PySimpleGUI.py", line 2026, in update self.TKEntry.icursor(tk.END) File "D:\software\python\lib\tkinter\__init__.py", line 3047, in icursor self.tk.call(self._w, 'icursor', index) _tkinter.TclError: invalid command name ".!toplevel18.!frame3.!entry"
11-10
import PySimpleGUI as Py import time import os image_path3 = "D:/software/demo/pythonProject/images/image3.png" image_path4 = "D:/software/demo/pythonProject/images/image4.png" image_path5 = "D:/software/demo/pythonProject/images/image5.png" image_path6 = "D:/software/demo/pythonProject/images/image6.png" image_path7 = "D:/software/demo/pythonProject/images/image7.png" # 检查图片是否存在 if not os.path.exists(image_path3): print(f"[!] 图片3不存在: {image_path3}") image_path3 = None if not os.path.exists(image_path4): print(f"[!] 图片4不存在: {image_path4}") image_path4 = None if not os.path.exists(image_path5): print(f"[!] 图片5不存在: {image_path5}") image_path5 = None if not os.path.exists(image_path6): print(f"[!] 图片6不存在: {image_path6}") image_path6 = None if not os.path.exists(image_path7): print(f"[!] 图片7不存在: {image_path7}") image_path7 = None b = 'zbjszj' layout1 = [[Py.Text('jdsq---')], [Py.Button('tips:zb'), Py.Button('wykd')]] window_position = (435, 0) # 定义主窗口的布局,这里设置了窗口在屏幕上的起始位置为(横坐标50, 纵坐标50) window1 = Py.Window('jdsq', layout1, size=(1665, 68), no_titlebar=True, location=window_position, finalize=True) window1.keep_on_top_set() Py.theme('DarkBlue3') # PySimpleGUI 设置主题颜色 # 全局标志防止重复运行 reminder_running = False def run_periodic_reminder(): global reminder_running if reminder_running: print("[!] 提醒功能已在运行,忽略重复调用") return reminder_running = True layout7 = [[Py.Image(key='img7', filename=image_path7) if image_path7 else Py.Text("加载失败")], [Py.Text("txdj")], [Py.Text("📌 周期性提醒已激活:每次取消后10秒自动重试")], [Py.Multiline("", size=(60, 4), key='-OUTPUT-', disabled=True, autoscroll=True)], [Py.Button('yzxpc', key='EXIT')]] window7 = Py.Window('jbdz:cc', layout7, size=(475, 480), finalize=True) next_popup_time = time.time() + 1 reminder_active = True popup_active = False input_popup = None def show_input_popup(): layout_popup = [[Py.Text('mbwbyd')], [Py.Input(key='-INPUT-', size=(15, 1), focus=True)], [Py.Button('fsydlc', bind_return_key=True), Py.Button('bsyyd')]] return Py.Window('xkdz', layout_popup, keep_on_top=True, finalize=True) try: while True: # === 安全 read():用 try-except 捕获 TclError === try: event7, values7 = window7.read(timeout=100) except (RuntimeError, Exception) as e: # 常见于窗口关闭后底层 TK 资源已释放 if "wrapped C/C++ object has been deleted" in str(e) or "TclError" in str(e): print("[INFO] 窗口已被系统关闭,yzxpc提醒循环。") break else: print(f"[ERROR] Unexpected error: {e}") break if event7 in (None, 'EXIT', Py.WINDOW_CLOSED): # 如果 read 返回 None,说明窗口被关闭 break now = time.time() # 自动弹出输入框逻辑 if reminder_active and not popup_active and next_popup_time and now >= next_popup_time: popup_active = True try: input_popup = show_input_popup() except Exception as e: print(f"[Error] 无法创建弹窗: {e}") popup_active = False next_popup_time = now + 10 continue if input_popup: # 处理输入弹窗 try: event_p, values_p = input_popup.read(timeout=100) except (RuntimeError, Exception): input_popup = None popup_active = False continue if event_p == 'fsydlc': user_input = values_p['-INPUT-'].strip() try: num = float(user_input) formatted_num = int(num) if num.is_integer() else num window7['-OUTPUT-'].update(f"✔️ 录入成功:{formatted_num} —— 提醒停止\n", append=True) reminder_active = False next_popup_time = None except ValueError: window7['-OUTPUT-'].update(f"❌ 输入无效:'{user_input}',10秒后重试...\n", append=True) next_popup_time = time.time() + 10 finally: try: input_popup.close() except: pass input_popup = None popup_active = False elif event_p in (None, 'bsyyd', Py.WINDOW_CLOSED): try: input_popup.close() except: pass input_popup = None popup_active = False window7['-OUTPUT-'].update("⛔ 用户取消,10秒后将再次提醒...\n", append=True) next_popup_time = time.time() + 10 finally: # 确保清理 if 'window7' in locals() or window7 is not None: try: window7.close() except: pass if input_popup is not None: try: input_popup.close() except: pass reminder_running = False def run_single_flow(): """运行一次完整的 window3 流程""" Py.theme('LightBlue') input_history = [] # 存储已确认的数字 last_input_time = None INPUT_TIMEOUT = 5.0 current_text = '' # 使用一个固定宽度的 Text 框实现横向排列 + 自动换行 layout3 = [[Py.Image(key='img3', filename=image_path3) if image_path3 else Py.Text("加载失败")], [Py.Text("rmxglx")], [Py.Text("请在5秒内输入当前数字,之后5秒无操作将自动记录:")], [Py.Input(key='-INPUT-', size=(20, 1), focus=True)], [Py.Text("", size=(60, 1), key='-STATUS-')], [Py.Text("已记录的数字(横向排列,自动换行):")], # 设置 size=(None, 10) 表示不限宽度列,高度最多10行;text_color 和 background_color 提升可读性 [Py.Frame('', [[Py.Text("", size=(590, 10), key='-HISTORY-', relief='sunken', background_color='white', text_color='black', font=('Courier', 10))]], size=(590, 50), pad=10)], # 长度约600 字符,超出则自动换行,最多10行,控制整体显示区域大小以便触发换行 [Py.Button('mphcxxl'), Py.Button('jchdj')], # 用一个大括号使他们在同一行显示 [Py.Button('txlxk'), Py.Button('bfhycg')]] window3 = Py.Window('jbdz:pddb', layout3, size=(630, 565), resizable=True) while True: event3, values3 = window3.read(timeout=100) if event3 == Py.WINDOW_CLOSED: break new_text = values3['-INPUT-'].strip() input_changed = new_text != current_text current_text = new_text valid_number = False # 验证是否为有效数字 try: if current_text: float(current_text) valid_number = True except ValueError: pass if input_changed and valid_number: # 输入变化且合法 → 重置计时器 last_input_time = time.time() window3['-STATUS-'].update(f"✅ 输入中 '{current_text}' ... 5秒无操作将自动提交") if last_input_time is not None: # 超时自动提交 elapsed = time.time() - last_input_time if elapsed >= INPUT_TIMEOUT: try: num = float(current_text) formatted_num = int(num) if num.is_integer() else num input_history.append(formatted_num) history_str = ' '.join(map(str, input_history)) # 更新历史显示:空格分隔,让系统自动换行 window3['-HISTORY-'].update(history_str) window3['-STATUS-'].update("🎉 已自动记录!") window3['-INPUT-'].update('') current_text = '' last_input_time = None except Exception as e: window3['-STATUS-'].update("❌ 提交失败") last_input_time = None elif last_input_time is None and current_text and not valid_number: # 状态栏提示 window3['-STATUS-'].update("❌ 请输入有效的数字") elif last_input_time and valid_number: remaining = max(0, int(INPUT_TIMEOUT - (time.time() - last_input_time) + 0.9)) if remaining > 0: window3['-STATUS-'].update(f"⏳ 还剩 {remaining} 秒自动提交...") if event3 in (Py.WIN_CLOSED, 'bfhycg'): window3.close() elif event3 == 'mphcxxl': window1.hide() elif event3 == 'txlxk': window3.close() layout4 = [[Py.Image(key='img4', filename=image_path4) if image_path4 else Py.Text("加载失败")], [Py.Text("请在5秒内输入当前数字,之后5秒无操作将自动记录:")], [Py.Input(key='-INPUT-', size=(20, 1), focus=True)], [Py.Text("", size=(60, 1), key='-STATUS-')], [Py.Text("已记录的数字(横向排列,自动换行):")], # 设置 size=(None, 10) 表示不限宽度列,高度最多10行;text_color 和 background_color 提升可读性 [Py.Frame('', [[Py.Text("", size=(410, 10), key='-HISTORY-', relief='sunken', background_color='white', text_color='black', font=('Courier', 10))]], size=(410, 50), pad=10)], # 控制整体显示区域大小以便触发换行 [Py.Button('jrrsby'), Py.Button('jrqsby')]] window4 = Py.Window('jbdz:pdbyqr', layout4, size=(450, 440), resizable=True) while True: event4, values4 = window4.read(timeout=100) if event4 == Py.WINDOW_CLOSED: break new_text = values4['-INPUT-'].strip() input_changed = new_text != current_text current_text = new_text valid_number = False # 验证是否为有效数字 try: if current_text: float(current_text) valid_number = True except ValueError: pass if input_changed and valid_number: # 输入变化且合法 → 重置计时器 last_input_time = time.time() window4['-STATUS-'].update(f"✅ 输入中 '{current_text}' ... 5秒无操作将自动提交") if last_input_time is not None: # 超时自动提交 elapsed = time.time() - last_input_time if elapsed >= INPUT_TIMEOUT: try: num = float(current_text) formatted_num = int(num) if num.is_integer() else num input_history.append(formatted_num) history_str = ' '.join(map(str, input_history)) # 更新历史显示:空格分隔,让系统自动换行 window4['-HISTORY-'].update(history_str) window4['-STATUS-'].update("🎉 已自动记录!") window4['-INPUT-'].update('') current_text = '' last_input_time = None except Exception as e: window4['-STATUS-'].update("❌ 提交失败") last_input_time = None elif last_input_time is None and current_text and not valid_number: # 状态栏提示 window4['-STATUS-'].update("❌ 请输入有效的数字") elif last_input_time and valid_number: remaining = max(0, int(INPUT_TIMEOUT - (time.time() - last_input_time) + 0.9)) if remaining > 0: window4['-STATUS-'].update(f"⏳ 还剩 {remaining} 秒自动提交...") elif event4 == 'jrrsby': window4.close() Py.theme('LightBlue') recorded_numbers = [] # 历史记录列表 layout5 = [[Py.Image(key='img5', filename=image_path5) if image_path5 else Py.Text("加载失败")], [Py.Text("请输入当前数字:")], [Py.Input(key='-INPUT-', size=(30, 1), do_not_clear=False)], # do_not_clear=False 表示默认清空 [Py.Button('cxzjqpqh', bind_return_key=True), Py.Button('bsyjcxh')], [Py.HorizontalSeparator()], [Py.Text("已保留的数字(横向排列,自动换行):")], [Py.Text("", size=(50, 2), key='-HISTORY-', relief='sunken', background_color='white', text_color='black')]] window5 = Py.Window('jbdz:rsby', layout5) while True: event5, values5 = window5.read() if event5 == Py.WINDOW_CLOSED: break input_value = values5['-INPUT-'].strip() if event5 == 'cxzjqpqh': window5.close() run_periodic_reminder() if input_value == '': window4['-HISTORY-'].update("⚠️ 输入为空,无法保留") else: try: num = float(input_value) formatted_num = int(num) if num.is_integer() else num recorded_numbers.append(formatted_num) history_text = ' '.join(map(str, recorded_numbers)) # 更新历史显示(横向空格分隔,自动换行) window5['-HISTORY-'].update(history_text) window5['-INPUT-'].update('') # 清空输入框(do_not_clear=False 已自动处理,但仍显式更新以防万一) except ValueError: window5['-STATUS-'].update("❌ 不是有效数字!") elif event5 == 'bsyjcxh': window5['-INPUT-'].update('') # 直接清空输入框 elif event4 == 'jrqsby': window4.close() Py.theme('LightBlue') recorded_numbers = [] # 历史记录列表 layout6 = [[Py.Image(key='img6', filename=image_path6) if image_path6 else Py.Text("加载失败")], [Py.Text("请输入当前数字:")], [Py.Input(key='-INPUT-', size=(30, 1), do_not_clear=False)], # do_not_clear=False 表示默认清空 [Py.Button('cxzcpb', bind_return_key=True), Py.Button('bsyjcxh')], [Py.HorizontalSeparator()], [Py.Text("已保留的数字(横向排列,自动换行):")], [Py.Text("", size=(50, 2), key='-HISTORY-', relief='sunken', background_color='white', text_color='black')]] window6 = Py.Window('jbdz:qsby', layout6) while True: event6, values6 = window6.read() if event6 == Py.WINDOW_CLOSED: break input_value = values6['-INPUT-'].strip() if event6 == 'cxzcpb': window6.close() run_periodic_reminder() if input_value == '': window6['-HISTORY-'].update("⚠️ 输入为空,无法保留") else: try: num = float(input_value) formatted_num = int(num) if num.is_integer() else num recorded_numbers.append(formatted_num) history_text = ' '.join(map(str, recorded_numbers)) # 更新历史显示(横向空格分隔,自动换行) window6['-HISTORY-'].update(history_text) window6['-INPUT-'].update('') # 清空输入框(do_not_clear=False 已自动处理,但仍显式更新以防万一) except ValueError: window6['-STATUS-'].update("❌ 不是有效数字!") elif event6 == 'bsyjcxh': # 直接清空输入框 window6['-INPUT-'].update('') elif event3 == 'jchdj': window3.close() window1.un_hide() run_periodic_reminder() # 弹出礼物窗口 while True: # 主事件循环 event1, values1 = window1.read() if event1 == Py.WIN_CLOSED: break if event1 == 'tips:zb': layout2 = [[Py.Text(b, size=(170, 2), auto_size_text=True)]] window2 = Py.Window('ktrx', layout2, size=(940, 210), finalize=True, keep_on_top=True) window2.read(timeout=5000, close=True) if event1 == 'wykd': run_single_flow() 代码所有窗口均置顶
11-10
评论 20
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值