How do I plot live data acquired via a NIDAQ inside a GUI window?

I currently have a function which plots data acquired via a NIDAQ (NI USB-6218) in real time. This is the following function, "acquirelive" :
% acquirelive function:
% acquire signal in real time
global time;
global data;
s = daq.createSession('ni');
addAnalogInputChannel(s,'Dev1', 0, 'Voltage');
s.Rate = 1000;
s.DurationInSeconds = 10;
lh = addlistener(s,'DataAvailable', @collectData);
s.startBackground();
This function calls the collectData function, which is the following:
function collectData(s,event)
time = event.TimeStamps;
data = event.Data;
h = animatedline(time,data);
end
This works well and plots the data in real-time over a 10 second period. However, when I attach this "acquirelive" function to a button in a GUI I'm designing using GUIDE, the GUI opens but nothing happens when the plot button is clicked (inside or outside the GUI). There is also no error message generated. This is the code associated with the button in the GUI:
function PlotButton_Callback(hObject, eventdata, handles)
acquirelive;
The GUI is pretty simple. There are no buttons aside from the plot button (it looks like this):
Any ideas why the placement of the function inside the GUI causes this problem? Do I need to initialize the axes of the plot in the code at some point?
Thanks for any advice you might have, I appreciate it! Let me know if more information is needed to answer the question.

2 Comments

Eamon - you may want to put a breakpoint at the line where you call acquirelive from the pushbutton callback so that when you press the button, you can start stepping through the code within the acquirelive script. When you do so, check to see if all variables are initialized properly.
If all code seems to be getting initialized proper but still nothing happens, then try copying and pasting the code from this script into your pushbutton callback (less the global variables since they seem to be unused). Or better yet, save the s to the handles structure so that you don't "lose" it and can access it again (to perhaps stop the data acquisition). Something like
function PlotButton_Callback(hObject, eventdata, handles)
handles.s = daq.createSession('ni');
addAnalogInputChannel(handles.s,'Dev1', 0, 'Voltage');
handles.s.Rate = 1000;
handles.s.DurationInSeconds = 10;
lh = addlistener(handles.s,'DataAvailable', @collectData);
handles.s.startBackground();
guidata(hObject, handles); % to save the updated handles structure
Does the above make a difference?
Hi Geoff, thanks for your response! Your suggested change works. It generates a new window displaying the real-time signal. However, it doesn't display inside the GUI window itself, which is what I'm aiming for. I'll look into that, but any further suggestions you have would be appreciated. Thanks again.

Sign in to comment.

More Answers (1)

Eamon - animatedline allows you to specify which axes you wish to create the line in, so you will need to pass the handle to your axes into the collectData callback. If the Tag property for your axes is axes1, then I think that you can do something like
...
lh = addlistener(handles.s,'DataAvailable', {@collectData,handles.axes1});
...
In the above, we just pass the axes handles as an input to the collectData function. This function will then change to
function collectData(s,event,hAxes)
time = event.TimeStamps;
data = event.Data;
h = animatedline(hAxes,time,data);
end
That should work. One thing you may want to consider is the line
h = animatedline(hAxes,time,data);
With it, you will be creating a new graphics object each time. This may be a problem depending upon often you are creating a line (does the rate refer to how many times a second or..?). An alternative is to create one animated line and just update its data over time. See the examples from the documentation and how the function addpoints is used.

16 Comments

Hi Geoff. Thanks for responding! I incorporated your suggested edits exactly as shown below:
function PlotButton_Callback(hObject, eventdata, handles)
% hObject handle to PlotButton (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles.s = daq.createSession('ni');
addAnalogInputChannel(handles.s,'Dev1', 0, 'Voltage');
handles.s.Rate = 1000;
handles.s.DurationInSeconds = 10;
lh = addlistener(handles.s,'DataAvailable',{@collectData,handles.axes1});
guidata(hObject, handles);
Along with:
function collectData(s,event,hAxes)
time = event.TimeStamps;
data = event.Data;
h=animatedline(hAxes,time,data);
end
But running the GUI code generates the following error (also, to avoid confusion optotracker is the name of the GUI .m file):
One or more output arguments not assigned during call to "_feval".
Error in daq.Session/addlistener (line 147)
[el] = addlistener@handle(varargin{:});
Error in optotracker>PlotButton_Callback (line 92)
lh = handles.s.addlistener('DataAvailable',{@collectData,handles.axes1});
Error in gui_mainfcn (line 95)
feval(varargin{:});
Error in optotracker(line 42)
gui_mainfcn(gui_State, varargin{:});
Error in @(hObject,eventdata)optotracker('PlotButton_Callback',hObject,eventdata,guidata(hObject))
Error while evaluating UIControl Callback
I tried incorporating additional output arguments but that generated a "too many output arguments" error so I'm going to look into the above error and see if anyone else has had a similar problem.
Eamon - hmmm...perhaps you can't add additional parameters to this callback. The alternative is to have the collectData function "look" for your GUI and grab the handle to the axes from it. If the Tag property for your GUI is set to (say) myGUI and its HandleVisibility property to on, then in your collectData callback you would do
function collectData(s,event,hAxes)
time = event.TimeStamps;
data = event.Data;
hGui = findobj('Tag','myGUI');
if ~isempty(hGUI)
handles = guidata(hGui);
if isfield(handles,'axes1')
h=animatedline(handles.axes1,time,data);
end
end
end
The problem with the above is that it ties your callback to the GUI which was why the other option was preferred since we just pass in a handle to an axes.
Hi Geoff. I tried to implement your suggestion. Here is my collectData code:
function collectData(s,event,hAxes)
time = event.TimeStamps;
data = event.Data;
hGui = findobj('Tag','optogui');
if ~isempty(hGui)
handles = guidata(hGui);
if isfield(handles,'axes1')
h=animatedline(handles.axes1,time,data);
end
end
end
Here is my PlotButton code including the collectData callback:
function PlotButton_Callback(hObject, eventdata, handles)
% hObject handle to PlotButton (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles.s = daq.createSession('ni');
addAnalogInputChannel(handles.s,'Dev1', 0, 'Voltage');
handles.s.Rate = 1000;
handles.s.DurationInSeconds = 5;
lh=addlistener(handles.s,'DataAvailable', @collectData);
handles.s.startBackground();
guidata(hObject, handles);
Running the GUI and clicking the plot button generates the following error:
Warning: Error occurred while executing callback:
Error using guidata (line 87)
H must be the handle to a figure or figure descendent.
Error in collectData (line 6)
handles = guidata(hGui);
I've double-checked my tags in the GUI and everything seems to be in order, so I'm going to look into this error further. Thanks for your continued help!
Hi Eamon - can you verify that the value for
hGui = findobj('Tag','optogui');
is in fact the handle to your GUI? For example, does hGui equal the handles.optogui?
One possibility is that you might be getting multiple objects returned.
Another possibility is that 'optogui' is the tag for a hidden handle and you need findall instead of findobj.
Hi Geoff and Walter. When I run:
hGui = findobj('Tag','optogui')
I get:
h =
Figure (optogui) with properties:
Number: 1
Name: 'Untitled'
Color: [0.9400 0.9400 0.9400]
Position: [644 125 732 428]
Units: 'pixels'
Show all properties
I am not sure if the handle to my GUI is somewhere in "h." I thought that the handle to the GUI was itself the tag "optogui," but it seems that was incorrect.
Walter, I do not know what the handle list would be in this case, so I don't think findall would work. I'll look into the issue. Thanks for your help!
Eamon - what version of MATLAB are you using? I'm using R2014a and the above description for h is foreign to me. Are you using a later version?
Hi Geoff. I apologize, It should read "hGui" not "h." However, I am using MATLAB R2014b if that remains a factor.
Eamon - what happens if you type
get(hGui)
What are the properties that it returns? But the above, it seems like this is a figure so I'm not sure why you are observing that error.
I can simulate this error message if the GUI is closed before I call guidata.
Eamon - you have to do this from the collectData function where hGui is defined.
Geoff- When I run
get(hGui)
I get:
get(hGui)
Alphamap: [1x64 double]
BeingDeleted: 'off'
BusyAction: 'queue'
ButtonDownFcn: ''
Children: [9x1 Graphics]
Clipping: 'on'
CloseRequestFcn: 'closereq'
Color: [0.9400 0.9400 0.9400]
Colormap: [64x3 double]
CreateFcn: ''
CurrentAxes: [1x1 Axes]
CurrentCharacter: ''
CurrentObject: []
CurrentPoint: [0 0]
DeleteFcn: ''
DockControls: 'on'
FileName: 'C:\Users\santos.admin\Documents\MATLAB\optotracker.fig'
GraphicsSmoothing: 'on'
HandleVisibility: 'on'
IntegerHandle: 'on'
Interruptible: 'on'
InvertHardcopy: 'on'
KeyPressFcn: ''
KeyReleaseFcn: ''
MenuBar: 'none'
Name: 'Untitled'
NextPlot: 'add'
Number: 1
NumberTitle: 'off'
PaperOrientation: 'portrait'
PaperPosition: [0.2500 2.5000 8 6]
PaperPositionMode: 'manual'
PaperSize: [8.5000 11]
PaperType: 'usletter'
PaperUnits: 'inches'
Parent: [1x1 Root]
Pointer: 'arrow'
PointerShapeCData: [16x16 double]
PointerShapeHotSpot: [1 1]
Position: [644 125 732 428]
Renderer: 'opengl'
RendererMode: 'auto'
Resize: 'off'
SelectionType: 'normal'
SizeChangedFcn: ''
Tag: 'optogui'
ToolBar: 'auto'
Type: 'figure'
UIContextMenu: []
Units: 'pixels'
UserData: []
Visible: 'on'
WindowButtonDownFcn: ''
WindowButtonMotionFcn: ''
WindowButtonUpFcn: ''
WindowKeyPressFcn: ''
WindowKeyReleaseFcn: ''
WindowScrollWheelFcn: ''
WindowStyle: 'normal'
Eamon - I'm not sure why your guidata call does not work. You would probably need to attach some of your code so that we can see how all of this works together. Alternatively, you could try using findobj to get the axes from your figure (assuming that there is just the one axes)
findobj('Parent',h,'-and','Type','axes')
Again, the HandleVisibility property for your figure would need to be on.
Geoff - I think I figured out why it wasn't working, but I'm still working on solving the main problem. The error I explained above happens if I have more than one figure open. As in, if I open the non-GUIDE version of the optotracker figure in addition to running the optotracker code which opens the figure. I think this might be because there are multiple figures open with the same handles then MATLAB isn't sure which figure the program refers to.
So, now if I run optotracker and close all other figures, I get a new error, which I've copied below.
Warning: Error occurred while executing callback:
Error using animatedline
Invalid type for argument X. Type should be double.
Error in collectData (line 8)
h=animatedline(handles.axes1,time,data);
This error doesn't make sense to me because according to this page you should be able to specify the axes before you specify the x and y data to be plotted using the animatedline function, using the following syntax:
h = animatedline(ax,___) creates the line in the axes specified by ax instead of in the current axes (gca). The option ax can precede any of the input argument combinations in the previous syntaxes.
Maybe this means I am not referencing my axes handles correctly. I'll look into it. Thanks for any ideas you may have. You've been working with me on this for quite some time! Again, for reference, this is my collectData function:
function collectData(s,event,hAxes)
time = event.TimeStamps;
data = event.Data;
hGui = findobj('Tag','optogui');
if ~isempty(hGui)
handles = guidata(hGui);
if isfield(handles,'axes1')
h=animatedline(handles.axes1,time,data);
end
end
end
And this is the PlotButton call in my optotracker GUI:
function PlotButton_Callback(hObject, eventdata, handles)
% hObject handle to PlotButton (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles.s = daq.createSession('ni');
addAnalogInputChannel(handles.s,'Dev1', 0, 'Voltage');
handles.s.Rate = 1000;
handles.s.DurationInSeconds = 5;
lh=addlistener(handles.s,'DataAvailable', @collectData);
handles.s.startBackground();
guidata(hObject, handles);
Eamon - I would put a breakpoint at the line
h=animatedline(handles.axes1,time,data);
and then run your GUI. When the debugger pauses at this line, check to see what is handles.axes1, time and data. It could be that the error message is referring to the time array. Check the type of each using
class(time)
class(data)
Perhaps the time array (which is the x input to animatedline and must be of type double) is an (say) integer array instead.
Hi again, Geoff. I apologize for the delayed response. It seems that both the time and data arrays are of type double. The only part of the argument which is not, is the handles.axes1, which is of type matlab.graphics.axis.Axes, which would mean that the handles.axes1 is the part of the argument triggering the error. This seems odd given that the animatedline function is supposed to allow axes as the first part of the argument. I will look into alternative ways of using animatedline. Thanks again!
This error:
Error using animatedline
Invalid type for argument X. Type should be double.
was most likely caused by non-double-typed data being sent to the "animatedline" initializer. The AnimatedLine previously only supported double-typed data for all axes. Now in R2023a, the AnimatedLine supports all numeric datatypes as well as datetimes and durations for all axes. Therefore, this error should not occur in the newest version of MATLAB.

Sign in to comment.

Categories

Find more on Creating, Deleting, and Querying Graphics Objects in Help Center and File Exchange

Asked:

on 5 Jul 2016

Commented:

on 7 Jun 2023

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!