Matlab GUI使用GUIDE:想要动态更新图表

8
我已经写了一段Matlab脚本,使用虚拟串口进行实时数据读取。我在一个m文件中做了大量的信号处理。
接下来,我感觉需要一个简洁的GUI,以摘要形式显示信息。
我最近才开始深入挖掘和阅读Matlab内置的GUI工具GUIDE。我跟随了几个教程,成功地能够在GUI上显示我的图形,在按下按钮后实现功能。
然而,我希望GUI能够实时更新。我的数据向量不断更新(从COMM端口读取数据),我希望GUI随着新数据的到来保持更新图形,而不是依靠按钮按下更新。请问有人可以指导我如何实现后台更新吗?
以下是GUI相关代码:
% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
global data
global time

% Time domain plot
axes(handles.timeDomainPlot);
cla;
plot (time, data);

编辑后的代码:

% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

%Setting it to display something when it ends
% t = timer('TimerFcn', 'timerOn=false; disp(''Updating GUI!'')',... 
t = timer(... 
            'TasksToExecute', 10, ... % Number of times to run the timer object
            'Period', 3, ...                
            'TimerFcn', GUIUpdate()); 

%Starting the timer
start(t)

function GUIUpdate()
global data
global time
%Parameters below axes
    global min
    global max 
      % Time domain plot
    axes(handles.timeDomainPlot);
    cla;
    plot (time, data);
    %Other parameters:
    set(handles.mean, 'String', mean);
    set(handles.max, 'String', max);

The error that I get is:

??? Error using ==> GUI_Learning>GUIUpdate
Too many output arguments.

Error in ==>
@(hObject,eventdata)GUI_Learning('pushbutton1_Callback',hObject,eventdata,guidata(hObject))


??? Error while evaluating uicontrol Callback

可能重复:http://stackoverflow.com/questions/1007385/getting-matlab-timer-to-update-matlab-guide-gui - Nzbuu
@c0d3rz 请查看关于定义timerfcn回调的链接 http://www.mathworks.com/help/techdoc/matlab_prog/f9-39541.html#f9-42494 尝试将'timerfcn'设置为@GUIUpdate,并更改GUIUpdate,使其具有两个输入GUIUpdate(obj,event)。 'obj'将是计时器对象的句柄,而'event'将包含有关如何调用它的一些详细信息。默认情况下,计时器函数回调将传递至少这两个参数。我不确定这是否是您错误的确切原因,但您的示例看起来不正确。如果我今天晚些时候有机会,我将尝试发布一个计时器示例。 - Aero Engy
4个回答

10

这是一个使用定时器和定时器回调函数的示例。我制作了一个简单的GUI,其中包含一个轴和一个按钮。

在打开函数中,我初始化绘图并创建计时器。在启动按钮回调中,我启动计时器并开始操作数据。计时器函数回调只通过其句柄更新线的y数据。下面是GUI M文件中相关的函数(省略了初始化部分和输出函数)。

function testTimer_OpeningFcn(hObject, eventdata, handles, varargin)
global y x
x = 0:.1:3*pi; % Make up some data and plot
y = sin(x);
handles.plot = plot(handles.axes1,x,y);
handles.timer = timer('ExecutionMode','fixedRate',...
                    'Period', 0.5,...
                    'TimerFcn', {@GUIUpdate,handles});
handles.output = hObject;
guidata(hObject, handles);

% --- Executes on button press in startButton.
function startButton_Callback(hObject, eventdata, handles)
global y x
start(handles.timer)
for i =1:30
   y = sin(x+i/10); 
   pause(1) 
end

function GUIUpdate(obj,event,handles)
global y 
set(handles.plot,'ydata',y);
你可能需要一个“停止”按钮来停止计时器,具体取决于你的GUI结构以及数据何时/如何更新。
编辑:基本操作处理了一些这些问题,它们非常基础,你可能已经知道了:
对象的单个句柄包含许多属性,你可以使用get()函数读取这些属性或使用set()函数设置这些属性。例如,也许我想出于某种原因更改GUI中startButton的文本。
set(handles.startButton,'String','Something Other Than Start');

你可以在代码中设置断点(例如在按钮按下事件中),然后使用 get() 命令查看各个对象的属性,以此来了解 handles 结构的内容。handles 结构包含 GUI 对象的所有句柄,以及你可能想要存储的任何自定义项。大多数 GUI 回调函数都会自动传递 handles 结构,因此你可以轻松访问 GUI 的所有部分。

例如,"startButton" 回调函数将自动传递 handles。因此,我可以通过 handles.timer 轻松访问计时器对象。

这带我到将自定义内容放入 handles 中的问题上。在打开函数中,我添加了一个新项目到 handles 结构中:handles.timerhandles.plot,因为我知道它们在其他回调函数中会很有用(例如按钮按下或 timerFcn 回调函数)。

但是,要永久存储这些内容,你需要使用 'guidata' 函数。该函数基本上会根据你的调用方式,将修改后的 handles 结构存储到主 GUI 中,或检索 handles 的副本。因此,打开函数中的以下行将修改过的 handles 结构(添加了 .timer 和 .plot)存储到主 GUI 中。

guidata(hObject,handles);

基本上,每当您在handles中添加内容时,您都应该使用该行代码使更改永久生效。

现在另一种调用它的方法是:

handles = guidata(hObject); %hObject can be any handle who is a child of the main GUI.

这将获取GUI的句柄结构。

最后,handles.output = hObject只是在启动GUI时的默认输出。如果您通过Matlab命令行调用GUI,如h = myGUI;,它应该返回GUI的句柄。


非常感谢Aero Energy。我认为在你的代码帮助下,我已经按照自己的想法工作了。我真的非常感激你的帮助。我确实阅读了网站上的计时器帮助,但是无法真正理解它,你的代码片段真的很有帮助。你抽出时间写下相关的代码来帮助我,这让我感到惊讶。再次感谢! - c0d3rz
请问这两行代码的作用是什么: handles.output = hObject; guidata(hObject, handles); - c0d3rz
我不确定是否应该使用这个handles.*符号。我阅读了Matlab关于使用Handles的帮助文件,但对我来说并没有太多意义。 - c0d3rz
@c0d3rz,请查看我上面的编辑,了解GUI句柄的基本知识。 - Aero Engy
非常感谢Aero Energy。我想给你的回答点赞,但是我没有达到所需的最低声望值... - c0d3rz

1
你需要使用一个计时器对象。将回调函数设置为更新图表的函数。

据我所知,目前没有更好的方法。你不能用这种方式吗?有什么好的理由吗? - Nzbuu
你能否看一下我的编辑?我好像无法正确使用计时器功能。 - c0d3rz

1
请查看使用数据链接使图形响应linkdata命令。

如果同一变量出现在多个图中,您可以将任何一个图与该变量链接起来。您可以将链接的图形与使用数据刷选标记图形配合使用,也可以单独使用。链接图形可以让您:

  • 使图形响应基本工作区或函数内部的变量更改
  • 当您从变量编辑器和命令行更改变量时,使图形响应
  • 通过数据刷选修改影响它们不同图形表示的变量
  • 为调试目的创建图形“监视窗口”

如果您使用MATLAB语言编程,则监视窗口非常有用。例如,在优化数据处理算法以逐步执行代码时,您可以看到图表响应语句执行中变量的更改。

我做了一个快速而简单的测试,如下所示,我不确定这在GUI和函数中如何工作,但可能会奏效。

注意1:我必须在子程序中添加断点,以修改全局变量 y,才能看到绘图自动更新。如果数据正在快速更改,则您可能需要使用 drawnow、pause 或计时器的某种组合。

function testLinking()
global x y
%Links failed if the global did not also exist in the base workspace
evalin('base','global x y');
x = 0:.1:3*pi; % Make up some data and plot
y = sin(x);

h = plot(x,y,'ydatasource','y','xdatasource','x');
linkdata on
testSub

function testSub()
%Test to see if a sub can make a linked global refresh
global x y
for i = 1:10
    %This should automatically update the plot.
    y = sin(x+i/10); 
end

编辑:根据您的函数结构,可能有绕过使用全局变量的方法...但我没有太多时间深入研究。


谢谢Aero,不过我的问题表述可能不够清晰。除了更改图表外,我还有一些静态文本框需要更新。看起来计时器是解决这个问题的方法... - c0d3rz
@c0d3rz,请查看我的另一个答案,以获取有关如何使您的计时器工作的帮助。 - Aero Engy

0

您可以在串行对象上添加回调函数,以执行绘图功能。您必须将回调附加到对象的“BytesAvailableFcn”事件上(有关com对象属性的更多详细信息,请参见this)。

基本上,当com端口上有可用字节时,您会指示matlab运行特定的函数。在您的情况下,它将是更新GUI的函数。如果您需要先处理传入的数据,则您的回调函数将首先进行信号处理,然后再执行绘图命令。


谢谢Jorge,不过我的问题表述可能不够清晰。除了更改图表外,我还有一些静态文本框需要更新。看来定时器是解决这个问题的方法... - c0d3rz
您也可以使用get/set更新框。问题是,我不知道您代码的逻辑。 - Jorge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接