错误使用 uibutton (第 55 行)
您指定的文件无法找到或者不是图像。
请指定一个位于 MATLAB 路径的文件名,或者使用完整或相对路径。
出错 InteractiveDataPlotter/createMultiPlot (第 270 行)
uibutton(ctrlGrid, 'Text', '添加数据集', ...
出错 InteractiveDataPlotter>@(src,evt)obj.createMultiPlot() (第 113 行)
'ButtonPushedFcn', @(src,evt)obj.createMultiPlot());
计算 Button PrivateButtonPushedFcn 时出错。
classdef InteractiveDataPlotter < handle
properties
% 数据存储
dataMap
fig
axes
% UI 组件句柄
keyListbox
btnPlotRefresh
btnMultiPlot
btnSaveCSV
% 多图管理
multiPlotFigures = gobjects(0) % 初始化为空图形对象数组
% 选择模式
selectionMode = 'single' % 默认单选模式
end
methods
function obj = InteractiveDataPlotter(dataMap)
% === 数据验证 ===
if ~isa(dataMap, 'containers.Map')
error('输入必须是 containers.Map 对象');
end
if isempty(dataMap)
error('输入Map不能为空');
end
% 检查所有值是否为数值向量
allKeys = keys(dataMap);
for i = 1:length(allKeys)
value = dataMap(allKeys{i});
if ~isnumeric(value) || ~isvector(value)
error('键 "%s" 的值必须是数值向量', allKeys{i});
end
end
obj.dataMap = dataMap;
obj.createMainUI();
obj.updateAddButtonState(); % 修复4: 初始化按钮状态
end
function createMainUI(obj)
obj.fig = uifigure('Name', '交互式数据绘图器', ...
'Position', [100, 100, 1200, 700], ...
'AutoResizeChildren', 'off');
% ===== 修复1: 移除重复的网格创建 =====
mainGrid = uigridlayout(obj.fig, [2, 3]);
mainGrid.RowHeight = {'1x', 60};
mainGrid.ColumnWidth = {220, '1x', 220};
mainGrid.RowSpacing = 10;
mainGrid.ColumnSpacing = 15;
% === 左侧面板: 数据集选择 ===
dataPanel = uipanel(mainGrid, 'Title', '数据集选择');
dataPanel.Layout.Row = 1;
dataPanel.Layout.Column = 1;
dataGrid = uigridlayout(dataPanel, [4, 1]);
dataGrid.RowHeight = {25, '1x', 35, 35};
dataGrid.RowSpacing = 8;
uilabel(dataGrid, 'Text', '选择数据集:', ...
'FontWeight', 'bold', ...
'HorizontalAlignment', 'left');
% ===== 修复2: 初始化模式同步 =====
if strcmp(obj.selectionMode, 'single')
multiSelect = 'off';
else
multiSelect = 'on';
end
obj.keyListbox = uilistbox(dataGrid, ...
'Items', keys(obj.dataMap), ...
'Multiselect', multiSelect, ... % 与selectionMode同步
'ValueChangedFcn', @(src,evt)obj.plotSelectedData());
modeGrid = uigridlayout(dataGrid, [1, 2]);
modeGrid.Padding = [0, 0, 0, 0];
modeGrid.ColumnWidth = {'1x', '1x'};
uibutton(modeGrid, 'Text', '单选模式', ...
'ButtonPushedFcn', @(src,evt)obj.setSelectionMode('single'));
uibutton(modeGrid, 'Text', '多选模式', ...
'ButtonPushedFcn', @(src,evt)obj.setSelectionMode('multi'));
% === 中央面板: 主绘图区 ===
plotPanel = uipanel(mainGrid, 'Title', '数据可视化');
plotPanel.Layout.Row = 1;
plotPanel.Layout.Column = 2;
plotGrid = uigridlayout(plotPanel, [1,1]);
obj.axes = uiaxes(plotGrid);
title(obj.axes, '数据可视化');
grid(obj.axes, 'on');
hold(obj.axes, 'on');
% === 右侧面板: 多图控制区 ===
multiPanel = uipanel(mainGrid, 'Title', '多图管理');
multiPanel.Layout.Row = 1;
multiPanel.Layout.Column = 3;
multiGrid = uigridlayout(multiPanel, [5, 1]);
multiGrid.RowHeight = {25, 40, 40, 40, '1x'};
multiGrid.RowSpacing = 10;
uilabel(multiGrid, 'Text', '操作:', ...
'FontWeight', 'bold', ...
'HorizontalAlignment', 'left');
obj.btnMultiPlot = uibutton(multiGrid, ...
'Text', '创建多图窗口', ... ...
'ButtonPushedFcn', @(src,evt)obj.createMultiPlot());
uibutton(multiGrid, 'Text', '添加选中项', ...
'ButtonPushedFcn', @(src,evt)obj.addToActiveMultiPlot(), ...
'Enable', 'off', ...
'Tag', 'addToMultiBtn');
obj.btnSaveCSV = uibutton(multiGrid, ...
'Text', '保存为CSV', ...
'ButtonPushedFcn', @(src,evt)obj.saveToCSV());
% === 底部按钮区 ===
btnPanel = uipanel(mainGrid, 'BorderType', 'none');
btnPanel.Layout.Row = 2;
btnPanel.Layout.Column = [1, 3];
btnGrid = uigridlayout(btnPanel, [1, 4]);
btnGrid.ColumnWidth = {'1x', '1x', '1x', '1x'};
btnGrid.Padding = [5, 10, 5, 10];
uibutton(btnGrid, 'Text', '清除图表', ...
'ButtonPushedFcn', @(src,evt)cla(obj.axes));
obj.btnPlotRefresh = uibutton(btnGrid, 'Text', '刷新数据', ...
'ButtonPushedFcn', @(src,evt)obj.plotAllData(), ...
'BackgroundColor', [0.3, 0.6, 1], ...
'FontColor', 'white');
uibutton(btnGrid, 'Text', '导出图表', ...
'ButtonPushedFcn', @(src,evt)obj.exportPlot(), ...
'Tag', 'exportPlotBtn');
uibutton(btnGrid, 'Text', '帮助', ...
'ButtonPushedFcn', @(src,evt)obj.showHelp(), ...
'Tag', 'helpBtn');
% 延迟初始绘图
drawnow; % 使用drawnow替代pause
obj.plotAllData();
end
function setSelectionMode(obj, mode)
obj.selectionMode = mode;
if strcmp(mode, 'single')
obj.keyListbox.Multiselect = 'off';
% 单选时自动选择第一项
if ~isempty(obj.keyListbox.Items)
obj.keyListbox.Value = obj.keyListbox.Items{1};
end
else
obj.keyListbox.Multiselect = 'on';
end
obj.plotSelectedData();
end
function plotAllData(obj)
% === 安全检测 ===
if ~isvalid(obj.axes), return; end
cla(obj.axes);
keys = obj.dataMap.keys();
if isempty(keys)
title(obj.axes, '无可用数据');
return;
end
for i = 1:length(keys)
key = keys{i};
if isKey(obj.dataMap, key)
data = obj.dataMap(key);
if isempty(data)
warning('键 "%s" 的数据为空,跳过绘图', key);
continue;
end
if isrow(data), data = data'; end
plot(obj.axes, data, 'DisplayName', key);
end
end
if ~isempty(allchild(obj.axes))
legend(obj.axes, 'show', 'Interpreter', 'none');
else
title(obj.axes, '无有效数据可显示');
end
end
function plotSelectedData(obj)
if ~isvalid(obj.axes), return; end
cla(obj.axes);
selectedKeys = obj.keyListbox.Value;
if isempty(selectedKeys), return; end
% 统一转换为元胞数组
if ischar(selectedKeys)
selectedKeys = {selectedKeys};
elseif isstring(selectedKeys)
selectedKeys = cellstr(selectedKeys);
end
for i = 1:length(selectedKeys)
key = selectedKeys{i};
if isKey(obj.dataMap, key)
data = obj.dataMap(key);
if isrow(data), data = data'; end
plot(obj.axes, data, 'DisplayName', key);
end
end
if ~isempty(selectedKeys)
legend(obj.axes, 'show', 'Interpreter', 'none');
end
end
function saveToCSV(obj)
[filename, pathname] = uiputfile('*.csv', '保存CSV文件');
if isequal(filename, 0) || isequal(pathname, 0), return; end
fullPath = fullfile(pathname, filename);
keys = obj.dataMap.keys();
maxLength = max(cellfun(@(k)numel(obj.dataMap(k)), keys));
T = table();
for i = 1:length(keys)
key = keys{i};
% ===== 修复3: 确保列向量处理 =====
data = obj.dataMap(key);
if isrow(data), data = data'; end
data = data(:); % 强制转换为列向量
if length(data) < maxLength
data = [data; nan(maxLength-length(data), 1)];
end
T.(key) = data(1:maxLength);
end
writetable(T, fullPath);
uialert(obj.fig, sprintf('数据已保存到:\n%s', fullPath), '保存成功', 'Icon', 'success');
end
function createMultiPlot(obj)
multiFig = uifigure('Name', '多图视图', ...
'Position', [300, 300, 1000, 600], ...
'CloseRequestFcn', @(src,evt)obj.onMultiPlotClose(src));
obj.multiPlotFigures(end+1) = multiFig;
obj.updateAddButtonState();
multiGrid = uigridlayout(multiFig, [3, 1]);
multiGrid.RowHeight = {50, '1x', '1x'};
multiGrid.RowSpacing = 10;
% 控制区
ctrlPanel = uipanel(multiGrid, 'BorderType', 'none');
ctrlGrid = uigridlayout(ctrlPanel, [1, 4]);
ctrlGrid.ColumnWidth = {120, '1x', 120, 120};
uibutton(ctrlGrid, 'Text', '添加数据集', ...
'Icon', 'add', ...
'ButtonPushedFcn', @(src,evt)obj.addToMultiPlot(multiFig));
uidropdown(ctrlGrid, 'Items', keys(obj.dataMap), ...
'Tag', 'multiPlotDropdown');
uibutton(ctrlGrid, 'Text', '清除所有', ...
'ButtonPushedFcn', @(src,evt)obj.clearMultiPlot(multiFig));
uibutton(ctrlGrid, 'Text', '保存视图', ...
'ButtonPushedFcn', @(src,evt)obj.saveMultiPlot(multiFig));
% 列表区
listPanel = uipanel(multiGrid, 'Title', '已选数据集');
listGrid = uigridlayout(listPanel, [1,1]);
listbox = uilistbox(listGrid, 'Items', {}, ...
'Multiselect', 'on');
% 绘图区
plotPanel = uipanel(multiGrid, 'Title', '多图叠加显示');
plotGrid = uigridlayout(plotPanel, [1,1]);
ax = uiaxes(plotGrid);
hold(ax, 'on');
grid(ax, 'on');
% 存储句柄
multiFig.UserData = struct(...
'axesHandle', ax, ...
'listboxHandle', listbox);
end
function updateAddButtonState(obj)
addBtns = findobj(obj.fig, 'Tag', 'addToMultiBtn');
if ~isempty(obj.multiPlotFigures) && all(isvalid(obj.multiPlotFigures))
set(addBtns, 'Enable', 'on');
else
set(addBtns, 'Enable', 'off');
% ===== 修复6: 清理无效引用 =====
obj.multiPlotFigures(~isvalid(obj.multiPlotFigures)) = [];
end
end
function addToActiveMultiPlot(obj)
if isempty(obj.multiPlotFigures) || ~any(isvalid(obj.multiPlotFigures))
uialert(obj.fig, '请先创建多图窗口', '无活动窗口');
return;
end
activeFig = obj.multiPlotFigures(end);
if isvalid(activeFig)
obj.addToMultiPlot(activeFig);
end
end
function addToMultiPlot(obj, multiFig)
% ===== 修复5: 增加安全检测 =====
if ~isvalid(multiFig) || ~isfield(multiFig.UserData, 'listboxHandle')
return;
end
ud = multiFig.UserData;
selectedKeys = obj.keyListbox.Value;
if isempty(selectedKeys), return; end
if ischar(selectedKeys)
selectedKeys = {selectedKeys};
end
currentItems = ud.listboxHandle.Items;
if isempty(currentItems)
currentItems = {};
end
newItems = setdiff(selectedKeys, currentItems);
if ~isempty(newItems)
ud.listboxHandle.Items = [currentItems; newItems(:)];
obj.updateMultiPlot(multiFig);
end
end
function updateMultiPlot(obj, multiFig)
% ===== 修复5: 增加安全检测 =====
if ~isvalid(multiFig) || ~isfield(multiFig.UserData, 'axesHandle')
return;
end
ud = multiFig.UserData;
cla(ud.axesHandle);
selectedKeys = ud.listboxHandle.Items;
if isempty(selectedKeys), return; end
if ischar(selectedKeys)
selectedKeys = {selectedKeys};
end
for i = 1:length(selectedKeys)
key = selectedKeys{i};
if isKey(obj.dataMap, key)
data = obj.dataMap(key);
if isrow(data), data = data'; end
plot(ud.axesHandle, data, 'DisplayName', key);
end
end
legend(ud.axesHandle, 'show', 'Interpreter', 'none');
end
function clearMultiPlot(obj, multiFig)
if ~isvalid(multiFig), return; end
if isfield(multiFig.UserData, 'axesHandle') && isvalid(multiFig.UserData.axesHandle)
cla(multiFig.UserData.axesHandle);
legend(multiFig.UserData.axesHandle, 'off');
end
if isfield(multiFig.UserData, 'listboxHandle') && isvalid(multiFig.UserData.listboxHandle)
multiFig.UserData.listboxHandle.Items = {};
end
end
function onMultiPlotClose(obj, fig)
if isvalid(fig)
delete(fig);
end
% ===== 修复6: 完全移除引用 =====
validIdx = isvalid(obj.multiPlotFigures) & (obj.multiPlotFigures ~= fig);
obj.multiPlotFigures = obj.multiPlotFigures(validIdx);
obj.updateAddButtonState();
end
% 预留接口保持不变
function exportPlot(obj)
end
function showHelp(obj)
end
function saveMultiPlot(obj, fig)
end
end
end
最新发布