Textbox.Visible=False隐藏方式导致的问题

在邮件系统开发中遇到相关说明字段在正式环境下无法正常显示的问题,通过使用div替代Textbox解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天公司的正式环境有个功能不好使,但是测试环境没有问题,经过和同事的研讨,发现应该是我在写代码的时候把Textbox的visible属性设置为false导致的。

当时的需求是需要在发邮件的时候加上“相关说明”,而“相关说明”的内容是由第一个发邮件的人填写的,第二个人在接到邮件以后,登录系统,发送第二封邮件,

邮件的“相关说明”是用第一个人的最后一次写的“相关说明”,自己不写“相关说明”。由于公用一个上传,发邮件页面,并且只有第一个人能够看到“相关说明”的

文本框。因此,我把Textbox的Visible默认设置为false,第一个发邮件的时候再设置成TRUE。

程序在测试环境运行没有问题,然而今天在正式环境运行的时候,却出现了第二个人没有获取到第一个人的“相关说明”。

通过同事的帮忙排查,发现因为Textbox的visible被设置成了false,虽然可以赋值,可能由于不同浏览器的原因,没有解析出来Textbox,无法取值,所以导致了没有相关说明。

同事给出了用div的方法来隐藏和显示

            <div id="mydiv" runat="server" style="display:none;">
                <asp:Label ID="LalExplain" Text="相关说明" runat="server" Visible="false" />
                <asp:TextBox ID="TxtBoxExplain" runat="server" Width="700px" TextMode="MultiLine"></asp:TextBox>
                <asp:Label ID="Label2" runat="server" ForeColor="Red"></asp:Label>
            </div>

后台用以下代码来显示

mydiv.Style.Value = "display.block";

以下是浏览器解析后的代码

 <div id="mydiv" style="display:none;">
                
                <textarea name="TxtBoxExplain" rows="2" cols="20" id="TxtBoxExplain" disabled="disabled" class="aspNetDisabled" style="width:700px;">
1、信息查询
2、系统说明</textarea>
                <span id="Label2" style="color:Red;"></span>
            </div>

通过这种方法,只是将Textbox不可见了,浏览器同样可以解析出来。

 

转载于:https://www.cnblogs.com/ZXdeveloper/p/4024424.html

``` namespace WindowsFormsApp2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private Student student = new Student(); private void Form1_Load(object sender, EventArgs e) { student.Courses.AddRange(new[] { new Course{ Name="数学", Credit=4, Score=92 }, new Course{ Name="语文", Credit=3, Score=80 }, new Course{ Name="英语", Credit=2, Score=98 }, new Course{ Name="物理", Credit=6, Score=70 }, new Course{ Name="化学", Credit=3, Score=89 } }); //绑定数据源 dgvCourses.DataSource = student.Courses; dgvCourses.Columns["Points"].Visible = false; //隐藏计算列 } private void dgvColumnsSetup() { dgvCourses.AutoGenerateColumns = false; dgvCourses.Columns.Clear(); DataGridViewTextBoxColumn colCredit = new DataGridViewTextBoxColumn(); colCredit.HeaderText = "学分"; colCredit.DataPropertyName = "Credit"; DataGridViewTextBoxColumn colScore = new DataGridViewTextBoxColumn(); colScore.HeaderText = "成绩"; colScore.DataPropertyName = "Score"; dgvCourses.Columns.AddRange(colCredit, colScore); } private void button1_Click(object sender, EventArgs e) { double gpa = student.CalculateGPA(); textBox1.Text = $"当前GPA:{gpa:F2}"; } } public class Course { public string Name { get; set; } //课程名称 public int Credit { get; set; } //学分 public int Score { get; set; } //成绩 public double Points { get { if (Score >= 90) return 4.0; else if (Score >= 80) return 3.0; else if (Score >= 70) return 2.0; else if (Score >= 60) return 1.0; else return 0.0; } } } public class Student { public List<Course> Courses { get; set; } = new List<Course>(); //计算GPA public double CalculateGPA() { double totalPoints = 0; int totalCredits = 0; foreach (var course in Courses) { totalPoints += course.Points * course.Credit; totalCredits += course.Credit; } return totalCredits == 0 ? 0 : totalPoints / totalCredits; } } }```为什么在datagridview控件中无数据显示
03-30
classdef ECG_Monitor_System < handle properties % 通信相关 serialPort % 串口对象 isConnected = false comPort = 'COM1' % 默认串口 baudRate = 115200 % OEM协议常用波特率 dataBits = 8 % 数据位 parity = 'none' % 校验位 stopBits = 1 % 停止位 % 数据管理 ecgData = zeros(12, 10000); filteredData = zeros(12, 10000); timeStamps = zeros(1, 10000); sampleRate = 1000; currentIndex = 1; dataLength = 0; isRecording = false; timerObj maxDataPoints = 10000 % 最大数据点数 numLeads = 12 % 导联数量 activeLead = 2 % 默认显示II导联 % 滤波参数 highPassFreq = 0.5; lowPassFreq = 100; notchFreq = 50; % 工频陷波 % 分析参数 heartRate = 0; rPeakPositions = []; qtInterval = 0; stSegment = 0; % 心电特征参数 qtIntervals = [] % QT间期数组(秒) stSegments = [] % ST段偏移数组(mV) avgQT = 0 % 平均QT间期 avgST = 0 % 平均ST偏移 % GUI组件 fig % 主控制窗口 overviewFig % 总览窗口 singleLeadFigs = gobjects(12, 1); % 单导联窗口 leadPlots = gobjects(12, 1); % 单导联绘图句柄 overviewPlots = gobjects(12, 1); % 总览窗口绘图句柄 leadNames = {'I', 'II', 'III', 'aVR', 'aVL', 'aVF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6'}; % 控制面板组件 controlPanel highPassEdit lowPassEdit notchCheckbox statusText heartRateText recordButton saveButton leadSelect dataSlider positionText % 频谱分析 spectrumAx % 报告生成 patientInfo = struct('name', '未知', 'gender', '未知', 'age', 0, 'id', ''); end methods function app = ECG_Monitor_System() app.createMainGUI(); app.createOverviewWindow(); app.initializeSystem(); end function createMainGUI(app) % 创建主控制窗口 app.fig = figure('Name', '心电数据管理系统', 'NumberTitle', 'off',... 'Position', [100, 100, 800, 600], 'CloseRequestFcn', @app.onClose,... 'MenuBar', 'none', 'ToolBar', 'none'); % 创建菜单栏 app.createMenus(); % 创建控制面板 app.createControlPanel(); end function createOverviewWindow(app) % 创建总览窗口 app.overviewFig = figure('Name', '心电图总览', 'NumberTitle', 'off',... 'Position', [950, 100, 1200, 800], 'CloseRequestFcn', @app.onOverviewClose); % 创建12导联网格布局 for i = 1:12 row = ceil(i/3); col = mod(i-1,3)+1; ax = subplot(4, 3, i, 'Parent', app.overviewFig); app.overviewPlots(i) = plot(ax, nan, nan, 'b-'); title(ax, app.leadNames{i}); xlabel(ax, '间(s)'); ylabel(ax, '幅度(mV)'); grid(ax, 'on'); end end function createSingleLeadWindow(app, leadIndex) % 创建单导联窗口 if ishandle(app.singleLeadFigs(leadIndex)) figure(app.singleLeadFigs(leadIndex)); % 激活现有窗口 return; end figName = sprintf('导联 %s 详细视图', app.leadNames{leadIndex}); app.singleLeadFigs(leadIndex) = figure('Name', figName, 'NumberTitle', 'off',... 'Position', [100 + 50*leadIndex, 100 + 50*leadIndex, 600, 400],... 'CloseRequestFcn', @(src,evt) app.onSingleLeadClose(leadIndex)); ax = axes('Parent', app.singleLeadFigs(leadIndex), 'Position', [0.1, 0.15, 0.85, 0.75]); app.leadPlots(leadIndex) = plot(ax, nan, nan, 'b-'); title(ax, sprintf('导联 %s - 详细视图', app.leadNames{leadIndex})); xlabel(ax, '间(s)'); ylabel(ax, '幅度(mV)'); grid(ax, 'on'); % 添加导联特定控制 uicontrol('Parent', app.singleLeadFigs(leadIndex), 'Style', 'pushbutton',... 'String', '检测R波', 'Position', [20, 10, 80, 25],... 'Callback', @(src,evt) app.detectRPeaksForLead(leadIndex)); uicontrol('Parent', app.singleLeadFigs(leadIndex), 'Style', 'pushbutton',... 'String', '频谱分析', 'Position', [120, 10, 80, 25],... 'Callback', @(src,evt) app.showSpectrumForLead(leadIndex)); end function createMenus(app) % 文件菜单 fileMenu = uimenu(app.fig, 'Text', '文件'); uimenu(fileMenu, 'Text', '加载数据', 'MenuSelectedFcn', @app.loadDataFile); uimenu(fileMenu, 'Text', '保存数据', 'MenuSelectedFcn', @app.saveData); uimenu(fileMenu, 'Text', '生成报告', 'MenuSelectedFcn', @app.generateReport); uimenu(fileMenu, 'Text', '退出', 'Separator', 'on', 'MenuSelectedFcn', @app.onClose); % 通信菜单 comMenu = uimenu(app.fig, 'Text', '通信'); uimenu(comMenu, 'Text', '连接设备', 'MenuSelectedFcn', @app.connectDevice); uimenu(comMenu, 'Text', '断开连接', 'MenuSelectedFcn', @app.disconnect); % 视图菜单 viewMenu = uimenu(app.fig, 'Text', '视图'); uimenu(viewMenu, 'Text', '显示总览窗口', 'MenuSelectedFcn', @app.showOverviewWindow); uimenu(viewMenu, 'Text', '显示所有导联窗口', 'MenuSelectedFcn', @app.showAllLeadWindows); uimenu(viewMenu, 'Text', '关闭所有导联窗口', 'MenuSelectedFcn', @app.closeAllLeadWindows); % 设置菜单 settingsMenu = uimenu(app.fig, 'Text', '设置'); uimenu(settingsMenu, 'Text', '通信设置', 'MenuSelectedFcn', @app.setCommunication); uimenu(settingsMenu, 'Text', '患者信息', 'MenuSelectedFcn', @app.setPatientInfo); uimenu(settingsMenu, 'Text', '分析参数', 'MenuSelectedFcn', @app.setAnalysisParams); end function createControlPanel(app) % 创建控制面板 app.controlPanel = uipanel('Parent', app.fig, 'Title', '控制面板',... 'Position', [0.05, 0.05, 0.9, 0.9]); % 通信控制 uicontrol('Parent', app.controlPanel, 'Style', 'pushbutton',... 'String', '连接设备', 'Position', [20, 520, 100, 30],... 'Callback', @app.connectDevice); uicontrol('Parent', app.controlPanel, 'Style', 'pushbutton',... 'String', '断开连接', 'Position', [140, 520, 100, 30],... 'Callback', @app.disconnect); app.statusText = uicontrol('Parent', app.controlPanel, 'Style', 'text',... 'String', '状态: 未连接', 'Position', [260, 520, 300, 30],... 'HorizontalAlignment', 'left', 'FontSize', 10); % 滤波设置 uicontrol('Parent', app.controlPanel, 'Style', 'text', 'String', '高通滤波(Hz):',... 'Position', [20, 470, 100, 25], 'FontSize', 10); app.highPassEdit = uicontrol('Parent', app.controlPanel, 'Style', 'edit',... 'String', '0.5', 'Position', [130, 470, 60, 25], 'FontSize', 10); uicontrol('Parent', app.controlPanel, 'Style', 'text', 'String', '低通滤波(Hz):',... 'Position', [20, 440, 100, 25], 'FontSize', 10); app.lowPassEdit = uicontrol('Parent', app.controlPanel, 'Style', 'edit',... 'String', '100', 'Position', [130, 440, 60, 25], 'FontSize', 10); uicontrol('Parent', app.controlPanel, 'Style', 'pushbutton', 'String', '应用滤波',... 'Position', [200, 450, 80, 30], 'Callback', @app.applyFilters); % 工频陷波 app.notchCheckbox = uicontrol('Parent', app.controlPanel, 'Style', 'checkbox',... 'String', '50Hz陷波', 'Position', [300, 450, 100, 25], 'Value', 1, 'FontSize', 10); % 数据记录 app.recordButton = uicontrol('Parent', app.controlPanel, 'Style', 'togglebutton',... 'String', '开始记录', 'Position', [20, 380, 100, 30],... 'Callback', @app.toggleRecording); app.saveButton = uicontrol('Parent', app.controlPanel, 'Style', 'pushbutton',... 'String', '保存数据', 'Position', [140, 380, 100, 30],... 'Callback', @app.saveData); % 导联选择 uicontrol('Parent', app.controlPanel, 'Style', 'text',... 'String', '导联选择:', 'Position', [20, 340, 80, 25], 'FontSize', 10); app.leadSelect = uicontrol('Parent', app.controlPanel, 'Style', 'popup',... 'String', app.leadNames, 'Position', [110, 340, 100, 25],... 'Callback', @app.onLeadSelected, 'FontSize', 10); % 打开导联窗口按钮 uicontrol('Parent', app.controlPanel, 'Style', 'pushbutton',... 'String', '打开导联窗口', 'Position', [230, 340, 120, 30],... 'Callback', @app.openSelectedLeadWindow); % 心率显示 uicontrol('Parent', app.controlPanel, 'Style', 'text',... 'String', '心率:', 'Position', [20, 300, 50, 25], 'FontSize', 10); app.heartRateText = uicontrol('Parent', app.controlPanel, 'Style', 'text',... 'String', '-- bpm', 'Position', [80, 300, 80, 25], 'FontSize', 10); % 数据导航 uicontrol('Parent', app.controlPanel, 'Style', 'text',... 'String', '数据位置:', 'Position', [20, 260, 80, 25], 'FontSize', 10); app.dataSlider = uicontrol('Parent', app.controlPanel, 'Style', 'slider',... 'Position', [110, 260, 300, 25], 'Callback', @app.sliderMoved,... 'Enable', 'off'); app.positionText = uicontrol('Parent', app.controlPanel, 'Style', 'text',... 'String', '0/0', 'Position', [420, 260, 80, 25], 'FontSize', 10); % 视图控制按钮 uicontrol('Parent', app.controlPanel, 'Style', 'pushbutton',... 'String', '显示总览窗口', 'Position', [20, 200, 120, 30],... 'Callback', @app.showOverviewWindow); uicontrol('Parent', app.controlPanel, 'Style', 'pushbutton',... 'String', '显示所有导联窗口', 'Position', [160, 200, 150, 30],... 'Callback', @app.showAllLeadWindows); uicontrol('Parent', app.controlPanel, 'Style', 'pushbutton',... 'String', '关闭所有导联窗口', 'Position', [330, 200, 150, 30],... 'Callback', @app.closeAllLeadWindows); % 频谱分析 spectrumPanel = uipanel('Parent', app.controlPanel, 'Title', '频谱分析',... 'Position', [0.05, 0.05, 0.9, 0.15]); app.spectrumAx = axes('Parent', spectrumPanel, 'Position', [0.1, 0.2, 0.85, 0.7]); title(app.spectrumAx, 'ECG信号频谱'); xlabel(app.spectrumAx, '频率(Hz)'); ylabel(app.spectrumAx, '幅度(dB)'); grid(app.spectrumAx, 'on'); end function initializeSystem(app) % 初始化系统参数 app.sampleRate = 1000; app.dataLength = 0; app.currentIndex = 1; app.isRecording = false; % 初始化串口参数(默认值需符合OEM协议) app.comPort = 'COM1'; app.baudRate = 115200; app.dataBits = 8; app.parity = 'none'; app.stopBits = 1; % 设置默认患者信息 app.patientInfo = struct(... 'name', '张三', ... 'gender', '男', ... 'age', 45, ... 'id', 'P2023001', ... 'date', datestr(now, 'yyyy-mm-dd')); end %% 窗口管理功能 function showOverviewWindow(app, ~, ~) % 显示总览窗口 if ishandle(app.overviewFig) figure(app.overviewFig); else app.createOverviewWindow(); end end function showAllLeadWindows(app, ~, ~) % 显示所有导联窗口 for i = 1:12 app.createSingleLeadWindow(i); end end function closeAllLeadWindows(app, ~, ~) % 关闭所有导联窗口 for i = 1:12 if ishandle(app.singleLeadFigs(i)) delete(app.singleLeadFigs(i)); end end end function openSelectedLeadWindow(app, ~, ~) % 打开选中的导联窗口 leadIdx = get(app.leadSelect, 'Value'); app.createSingleLeadWindow(leadIdx); end function onLeadSelected(app, src, ~) % 导联选择回调 leadIdx = get(src, 'Value'); app.updateSpectrumForLead(leadIdx); end function onOverviewClose(app, ~, ~) % 总览窗口关闭回调 delete(app.overviewFig); app.overviewFig = gobjects(1); end function onSingleLeadClose(app, leadIndex) % 单导联窗口关闭回调 delete(app.singleLeadFigs(leadIndex)); app.singleLeadFigs(leadIndex) = gobjects(1); end %% 通信协议实现 function setCommunication(app, ~, ~) % 串口设置对话框 prompt = {'串口号:', '波特率:', '数据位:', '校验位:', '停止位:'}; dlgtitle = '串口设置'; dims = [1 20]; definput = {app.comPort, num2str(app.baudRate), num2str(app.dataBits), ... app.parity, num2str(app.stopBits)}; answer = inputdlg(prompt, dlgtitle, dims, definput); if ~isempty(answer) app.comPort = answer{1}; app.baudRate = str2double(answer{2}); app.dataBits = str2double(answer{3}); app.parity = answer{4}; app.stopBits = str2double(answer{5}); % 验证波特率是否合法(示例:仅允许常见波特率) validBaudRates = [9600, 19200, 38400, 57600, 115200, 230400]; if ~any(app.baudRate == validBaudRates) app.baudRate = 115200; % 默认恢复为常用值 warndlg('波特率设置不合法,已恢复为115200', '设置警告'); end set(app.statusText, 'String', '串口设置已更新'); end end function connectDevice(app, ~, ~) try % 关闭现有连接 if ~isempty(app.serialPort) && isvalid(app.serialPort) fclose(app.serialPort); delete(app.serialPort); end % 创建串口对象(根据OEM协议配置参数) app.serialPort = serialport(app.comPort, app.baudRate, ... 'DataBits', app.dataBits, ... 'Parity', app.parity, ... 'StopBits', app.stopBits); % 设置数据接收回调(OEM协议通常以特定字节结尾,如CR/LF或固定帧长) configureCallback(app.serialPort, "terminator", "linefeed", ... @(src, ~) app.serialCallback(src)); % 初始化数据缓冲区 app.ecgData = zeros(12, 10000); app.filteredData = zeros(12, 10000); app.timeStamps = zeros(1, 10000); app.currentIndex = 1; app.dataLength = 0; % 启用数据导航 set(app.dataSlider, 'Enable', 'on'); set(app.statusText, 'String', ['已连接到串口: ' app.comPort ' (波特率: ' num2str(app.baudRate) ')']); app.isConnected = true; catch ME errordlg(['串口连接失败: ' ME.message], '连接错误'); app.isConnected = false; end end function disconnect(app, ~, ~) if ~isempty(app.serialPort) && isvalid(app.serialPort) fclose(app.serialPort); delete(app.serialPort); app.serialPort = []; set(app.statusText, 'String', '已断开串口连接'); app.isConnected = false; end end function serialCallback(app, src) if src.NumBytesAvailable > 0 % 根据OEM协议读取数据(示例:假设每帧13个浮点数,以逗号分隔) % 注意:实际OEM协议可能包含帧头、校验位等,需按协议解析 try % 读取一帧数据(假设每帧以换行符结尾) data = readline(src); % 解析OEM协议数据(示例:假设数据格式为间戳+12导联值,逗号分隔) values = sscanf(data, '%f,', [13, 1]); if numel(values) == 13 % 存储数据 if app.dataLength < size(app.ecgData, 2) app.dataLength = app.dataLength + 1; end app.timeStamps(app.dataLength) = values(1) / 1000; % 转换为秒 app.ecgData(:, app.dataLength) = values(2:13); % 实滤波 app.realTimeFiltering(app.dataLength); % 更新显示 app.updatePlots(); % 实分析 if mod(app.dataLength, 50) == 0 app.realTimeAnalysis(); end % 更新数据导航 set(app.dataSlider, 'Value', app.dataLength / size(app.ecgData, 2)); set(app.positionText, 'String',... sprintf('%d/%d', app.dataLength, size(app.ecgData, 2))); end catch ME % 处理解析错误(如数据格式不符、校验失败等) warning(['OEM协议解析错误: ' ME.message]); end end end %% 数据处理模块 function realTimeFiltering(app, idx) % 实滤波处理 if idx == 1 % 初始化滤波数据 app.filteredData(:, idx) = app.ecgData(:, idx); return; end % 获取滤波参数 hpFreq = str2double(get(app.highPassEdit, 'String')); lpFreq = str2double(get(app.lowPassEdit, 'String')); useNotch = get(app.notchCheckbox, 'Value'); for i = 1:12 % 应用高通滤波 if ~isnan(hpFreq) && hpFreq > 0 [b, a] = butter(2, hpFreq/(app.sampleRate/2), 'high'); if idx > 10 segment = app.ecgData(i, max(1, idx-10):idx); filteredSeg = filtfilt(b, a, segment); app.filteredData(i, idx) = filteredSeg(end); else app.filteredData(i, idx) = app.ecgData(i, idx); end else app.filteredData(i, idx) = app.ecgData(i, idx); end % 应用低通滤波 if ~isnan(lpFreq) && lpFreq > 0 [b, a] = butter(4, lpFreq/(app.sampleRate/2), 'low'); if idx > 10 segment = app.filteredData(i, max(1, idx-10):idx); filteredSeg = filtfilt(b, a, segment); app.filteredData(i, idx) = filteredSeg(end); end end % 应用50Hz陷波 if useNotch wo = 50/(app.sampleRate/2); bw = wo/10; [b, a] = iirnotch(wo, bw); if idx > 10 segment = app.filteredData(i, max(1, idx-10):idx); filteredSeg = filtfilt(b, a, segment); app.filteredData(i, idx) = filteredSeg(end); end end end end function applyFilters(app, ~, ~) % 应用全局滤波 if app.dataLength > 0 % 获取滤波参数 hpFreq = str2double(get(app.highPassEdit, 'String')); lpFreq = str2double(get(app.lowPassEdit, 'String')); useNotch = get(app.notchCheckbox, 'Value'); for i = 1:12 % 高通滤波 if ~isnan(hpFreq) && hpFreq > 0 [b, a] = butter(2, hpFreq/(app.sampleRate/2), 'high'); app.filteredData(i, 1:app.dataLength) = ... filtfilt(b, a, app.ecgData(i, 1:app.dataLength)); end % 低通滤波 if ~isnan(lpFreq) && lpFreq > 0 [b, a] = butter(4, lpFreq/(app.sampleRate/2), 'low'); app.filteredData(i, 1:app.dataLength) = ... filtfilt(b, a, app.filteredData(i, 1:app.dataLength)); end % 50Hz陷波 if useNotch wo = 50/(app.sampleRate/2); bw = wo/10; [b, a] = iirnotch(wo, bw); app.filteredData(i, 1:app.dataLength) = ... filtfilt(b, a, app.filteredData(i, 1:app.dataLength)); end end % 更新显示 app.updatePlots(); app.updateSpectrum(); set(app.statusText, 'String', '全局滤波已应用'); end end %% 实分析模块 function realTimeAnalysis(app) % 实分析ECG信号 if app.dataLength > 100 % 在II导联检测R波 leadIndex = 2; data = app.filteredData(leadIndex, max(1, app.dataLength-500):app.dataLength); % 使用Pan-Tompkins算法检测R波 [~, qrs_i_raw] = app.pan_tompkins(data, app.sampleRate); % 计算心率 if length(qrs_i_raw) > 1 rrIntervals = diff(qrs_i_raw) / app.sampleRate; app.heartRate = 60 / mean(rrIntervals); set(app.heartRateText, 'String', sprintf('%.1f bpm', app.heartRate)); % 更新图形标记 hold(app.ax(leadIndex), 'on'); if ~isempty(app.rPeakMarkers) && isvalid(app.rPeakMarkers) delete(app.rPeakMarkers); end % 计算实际间位置 timeOffset = app.timeStamps(max(1, app.dataLength-500)); timePos = app.timeStamps(qrs_i_raw + max(1, app.dataLength-500) - 1); app.rPeakMarkers = plot(app.ax(leadIndex),... timePos, data(qrs_i_raw), 'ro', 'MarkerSize', 6, 'MarkerFaceColor', 'r'); hold(app.ax(leadIndex), 'off'); end end end function [qrs_amp_raw, qrs_i_raw] = pan_tompkins(~, ecg, fs) % Pan-Tompkins R波检测算法 % 1. 带通滤波 (5-15 Hz) f1 = 5/(fs/2); f2 = 15/(fs/2); [b, a] = butter(1, [f1, f2], 'bandpass'); ecg_filtered = filtfilt(b, a, ecg); % 2. 微分 diff_ecg = diff(ecg_filtered); diff_ecg = [diff_ecg(1), diff_ecg]; % 3. 平方 sqr_ecg = diff_ecg .^ 2; % 4. 移动平均窗口积分 window_size = round(0.150 * fs); integrated_ecg = movmean(sqr_ecg, window_size); % 5. 自适应阈值检测 max_h = max(integrated_ecg); threshold = 0.2 * max_h; % 6. 寻找R波位置 [qrs_amp_raw, qrs_i_raw] = findpeaks(integrated_ecg,... 'MinPeakHeight', threshold,... 'MinPeakDistance', round(0.2*fs)); end %% 数据管理 function toggleRecording(app, src, ~) % 开始/停止记录数据 if src.Value app.isRecording = true; set(src, 'String', '停止记录'); set(app.statusText, 'String', '记录中...'); % 初始化记录缓冲区 app.ecgData = zeros(12, 10000); app.filteredData = zeros(12, 10000); app.timeStamps = zeros(1, 10000); app.currentIndex = 1; app.dataLength = 0; else app.isRecording = false; set(src, 'String', '开始记录'); set(app.statusText, 'String', '记录已停止'); end end function loadDataFile(app, ~, ~) % 加载ECG数据文件 [filename, pathname] = uigetfile({'*.mat;*.csv;*.txt', '数据文件 (*.mat, *.csv, *.txt)'},... '选择ECG数据文件'); if isequal(filename, 0) return; end filepath = fullfile(pathname, filename); [~, ~, ext] = fileparts(filepath); try h = waitbar(0, '加载数据...', 'Name', '数据处理'); switch lower(ext) case '.mat' data = load(filepath); waitbar(0.3, h); if isfield(data, 'ecgData') app.ecgData = data.ecgData; app.filteredData = data.filteredData; app.timeStamps = data.timeStamps; app.sampleRate = data.sampleRate; else error('无法识别MAT文件中的数据格式'); end case {'.csv', '.txt'} data = readmatrix(filepath); waitbar(0.3, h); if size(data, 2) >= 13 app.timeStamps = data(:, 1)'; app.ecgData = data(:, 2:13)'; app.sampleRate = 1 / mean(diff(app.timeStamps)); else error('CSV/TXT文件应包含间戳和12导联数据'); end end app.dataLength = size(app.ecgData, 2); app.currentIndex = app.dataLength; % 应用滤波 waitbar(0.7, h, '应用滤波...'); app.applyFilters(); % 更新界面 waitbar(0.9, h, '更新界面...'); app.updatePlots(); app.updateSpectrum(); % 配置导航滑块 set(app.dataSlider, 'Min', 0, 'Max', 1, 'Value', 1,... 'SliderStep', [100/app.dataLength, 1000/app.dataLength],... 'Enable', 'on'); set(app.positionText, 'String',... sprintf('%d/%d', app.dataLength, size(app.ecgData, 2))); set(app.statusText, 'String',... sprintf('已加载: %s (%d个样本)', filename, app.dataLength)); waitbar(1, h, '完成!'); pause(0.5); delete(h); catch ME errordlg(sprintf('加载失败: %s', ME.message), '文件错误'); end end function saveData(app, ~, ~) % 保存ECG数据 if app.dataLength == 0 errordlg('没有可保存的数据', '保存错误'); return; end [filename, pathname] = uiputfile({'*.mat', 'MAT文件 (*.mat)'; '*.csv', 'CSV文件 (*.csv)'},... '保存ECG数据', 'ecg_data.mat'); if isequal(filename, 0) return; end filepath = fullfile(pathname, filename); [~, ~, ext] = fileparts(filepath); try app.ecgData = app.ecgData(:, 1:app.dataLength); app.filteredData = app.filteredData(:, 1:app.dataLength); app.timeStamps = app.timeStamps(1:app.dataLength); app.sampleRate = app.sampleRate; switch lower(ext) case '.mat' save(filepath, 'ecgData', 'filteredData', 'timeStamps', 'sampleRate'); case '.csv' dataToSave = [app.timeStamps', app.ecgData']; writematrix(dataToSave, filepath); end set(app.statusText, 'String', sprintf('数据已保存至: %s', filename)); catch ME errordlg(sprintf('保存失败: %s', ME.message), '保存错误'); end end %% 显示更新 function updatePlots(app) % 更新所有窗口的心电图显示 if app.dataLength > 0 windowSize = 500; % 显示窗口大小 startIdx = max(1, app.currentIndex - windowSize); endIdx = min(app.dataLength, app.currentIndex + windowSize); timeWindow = app.timeStamps(startIdx:endIdx); % 更新总览窗口 if ishandle(app.overviewFig) for i = 1:12 if ishandle(app.overviewPlots(i)) set(app.overviewPlots(i), 'XData', timeWindow,... 'YData', app.filteredData(i, startIdx:endIdx)); % 自动调整Y轴范围 dataRange = range(app.filteredData(i, startIdx:endIdx)); if dataRange > 0 ax = get(app.overviewPlots(i), 'Parent'); ylim(ax, [min(app.filteredData(i, startIdx:endIdx)) - 0.1*dataRange,... max(app.filteredData(i, startIdx:endIdx)) + 0.1*dataRange]); end end end drawnow; end % 更新单导联窗口 for i = 1:12 if ishandle(app.singleLeadFigs(i)) && ishandle(app.leadPlots(i)) set(app.leadPlots(i), 'XData', timeWindow,... 'YData', app.filteredData(i, startIdx:endIdx)); % 自动调整Y轴范围 dataRange = range(app.filteredData(i, startIdx:endIdx)); if dataRange > 0 ax = get(app.leadPlots(i), 'Parent'); ylim(ax, [min(app.filteredData(i, startIdx:endIdx)) - 0.1*dataRange,... max(app.filteredData(i, startIdx:endIdx)) + 0.1*dataRange]); end drawnow; end end end end function updateSpectrumForLead(app, leadIndex) % 更新指定导联的频谱分析 if app.dataLength > 0 && leadIndex >= 1 && leadIndex <= 12 signal = app.filteredData(leadIndex, max(1, app.dataLength-2000):app.dataLength); % 计算FFT N = length(signal); if N < 10, return; end f = (0:N-1)*(app.sampleRate/N); Y = fft(signal); P2 = abs(Y/N); P1 = P2(1:floor(N/2)+1); P1(2:end-1) = 2*P1(2:end-1); f = f(1:floor(N/2)+1); % 更新频谱分析图 plot(app.spectrumAx, f, 20*log10(P1), 'b'); title(app.spectrumAx, sprintf('导联 %s 频谱', app.leadNames{leadIndex})); xlabel(app.spectrumAx, '频率 (Hz)'); ylabel(app.spectrumAx, '幅度 (dB)'); xlim(app.spectrumAx, [0, min(150, app.sampleRate/2)]); grid(app.spectrumAx, 'on'); end end function showSpectrumForLead(app, leadIndex) % 在单独窗口中显示导联频谱 if app.dataLength > 0 && leadIndex >= 1 && leadIndex <= 12 figName = sprintf('导联 %s 频谱分析', app.leadNames{leadIndex}); app.fig = figure('Name', figName, 'NumberTitle', 'off',... 'Position', [200, 200, 600, 400]); ax = axes('Parent', app.fig, 'Position', [0.1, 0.15, 0.85, 0.75]); signal = app.filteredData(leadIndex, max(1, app.dataLength-2000):app.dataLength); N = length(signal); f = (0:N-1)*(app.sampleRate/N); Y = fft(signal); P2 = abs(Y/N); P1 = P2(1:floor(N/2)+1); P1(2:end-1) = 2*P1(2:end-1); f = f(1:floor(N/2)+1); plot(ax, f, 20*log10(P1), 'b'); title(ax, sprintf('导联 %s 频谱分析', app.leadNames{leadIndex})); xlabel(ax, '频率 (Hz)'); ylabel(ax, '幅度 (dB)'); xlim(ax, [0, min(150, app.sampleRate/2)]); grid(ax, 'on'); end end function detectRPeaksForLead(app, leadIndex) % 检测指定导联的R波 if app.dataLength > 0 && leadIndex >= 1 && leadIndex <= 12 data = app.filteredData(leadIndex, max(1, app.dataLength-2000):app.dataLength); % 使用Pan-Tompkins算法检测R波 [~, qrs_i_raw] = app.pan_tompkins(data, app.sampleRate); % 在图形上标记R波 if ishandle(app.singleLeadFigs(leadIndex)) app.fig = app.singleLeadFigs(leadIndex); ax = findobj(app.fig, 'Type', 'axes'); % 清除旧标记 oldMarkers = findobj(ax, 'Tag', 'RPeakMarker'); if ~isempty(oldMarkers) delete(oldMarkers); end % 添加新标记 hold(ax, 'on'); plot(ax, app.timeStamps(qrs_i_raw + max(1, app.dataLength-2000) - 1),... data(qrs_i_raw), 'ro', 'MarkerSize', 8, 'MarkerFaceColor', 'r',... 'Tag', 'RPeakMarker'); hold(ax, 'off'); % 计算并显示心率 if length(qrs_i_raw) > 1 rrIntervals = diff(qrs_i_raw) / app.sampleRate; app.heartRate = 60 / mean(rrIntervals); title(ax, sprintf('导联 %s - 心率: %.1f bpm',... app.leadNames{leadIndex}, app.heartRate)); end end end end %% 报告生成 function setPatientInfo(app, ~, ~) % 设置患者信息 prompt = {'姓名:', '性别:', '年龄:', '患者ID:'}; dlgtitle = '患者信息'; dims = [1 25]; definput = {app.patientInfo.name, app.patientInfo.gender,... num2str(app.patientInfo.age), app.patientInfo.id}; answer = inputdlg(prompt, dlgtitle, dims, definput); if ~isempty(answer) app.patientInfo.name = answer{1}; app.patientInfo.gender = answer{2}; app.patientInfo.age = str2double(answer{3}); app.patientInfo.id = answer{4}; app.patientInfo.date = datestr(now, 'yyyy-mm-dd HH:MM'); end end function generateReport(app, ~, ~) % 生成心电图报告 if app.dataLength == 0 errordlg('没有数据可生成报告', '报告错误'); return; end [filename, pathname] = uiputfile('*.pdf', '保存心电图报告', 'ECG_Report.pdf'); if filename == 0 return; end pdfFile = fullfile(pathname, filename); app.exportToPDF(pdfFile); msgbox(sprintf('心电图报告已保存至:\n%s', pdfFile), '报告生成'); end function exportToPDF(app, filename) % 导出PDF报告 app.fig = figure('Visible', 'off', 'Position', [100, 100, 1200, 900]); % 报告标题 uicontrol('Style', 'text', 'String', '心电图报告',... 'Position', [400, 850, 400, 40], 'FontSize', 20, 'FontWeight', 'bold'); % 患者信息 app.patientInfo = {sprintf('姓名: %s', app.patientInfo.name),... sprintf('性别: %s', app.patientInfo.gender),... sprintf('年龄: %d', app.patientInfo.age),... sprintf('ID: %s', app.patientInfo.id),... sprintf('检查日期: %s', app.patientInfo.date)}; uicontrol('Style', 'text', 'String', app.patientInfo,... 'Position', [50, 780, 300, 100], 'FontSize', 12, 'HorizontalAlignment', 'left'); % 分析结果 analysisInfo = {sprintf('心率: %.1f bpm', app.heartRate),... sprintf('采样率: %d Hz', app.sampleRate),... sprintf('数据长度: %.1f 秒', app.timeStamps(app.dataLength))}; uicontrol('Style', 'text', 'String', analysisInfo,... 'Position', [50, 680, 300, 60], 'FontSize', 12, 'HorizontalAlignment', 'left'); % 绘制12导联心电图 for i = 1:12 row = ceil(i/3); col = mod(i-1,3)+1; ax = subplot(4,3,i, 'Parent', app.fig); if app.dataLength > 0 % 显示完整数据 plot(ax, app.timeStamps(1:app.dataLength),... app.filteredData(i, 1:app.dataLength), 'b-'); end title(ax, app.leadNames{i}); xlabel(ax, '间(s)'); ylabel(ax, '幅度(mV)'); grid(ax, 'on'); end % 导出为PDF exportgraphics(app.fig, filename, 'ContentType', 'vector'); close(app.fig); end %% 系统控制 function onClose(app, ~, ~) % 关闭应用程序 app.disconnect(); % 关闭所有窗口 if ishandle(app.overviewFig) delete(app.overviewFig); end for i = 1:12 if ishandle(app.singleLeadFigs(i)) delete(app.singleLeadFigs(i)); end end delete(app.fig); end end end % 辅助函数 function result = iff(condition, trueValue, falseValue) if condition result = trueValue; else result = falseValue; end end
最新发布
06-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值