Event listener not picking up GUI data

I've been trying to learn how to use an event listener (been using this as a basis: Simulink Signal Viewing using Event Listeners and a MATLAB UI.) and i seem to of found a strange error:
hf =
0x0 empty GraphicsPlaceholder array.
gui_name =
Event_listener_GUI
Error using guidata (line 87)
H must be the handle to a figure or figure descendent.
Error in Event_listener_GUI>localEventListener (line 232)
handles = guidata(mfilename)
Warning: Error occurred while evaluating listener callback.
The code that generates this is below (I removed the ';' on a few lines because I wanted to see what they were doing as well as un/commented out code from the simpleGUI.m file by Phil Goddard as i wanted to compare what his function was doing compared to mine, as it turns out his retrieves data from his GUI but mine displays what's above):
function localEventListener (block, eventdata)
% Get the application data
hf = findall(0,'Tag',mfilename)
gui_name = mfilename
%hf = gcbo
handles = guidata(mfilename)
% Get the handle to the line that needs updating
thisLineHandle = handles.ad.LineHandles([handles.ad.viewing.BlockHandle]...
==block.BlockHandle);
% Get current data for the line
xdata = get(thisLineHandles, 'XData');
ydata = get(thisLineHandles,'YData');
% Get the simulation time
sTime = block.CurrentTime;
data = block.InputPort(1).Data;
% only the last 1001 points worth of data is needed, the model sample time
% is 0,001 so this represents 1000 seconds of data
if length(xdata)<1001
newXData = [xdata sTime];
newYData = [ydata data];
else
newXData = [xdata(2:end) sTime];
newYData = [ydata(2:end) data];
end
% Display the new dataset
set(thisLineHandle,'XData',newXData,'YData',newYData);
% The axes limits might also need altering
newXLim = [max(0,sTime-10) max(10,sTime)];
set(handles.axes1,'XLim',newXLim);
For some reason when the model is started using the GUI (GUI remains open throughout) and the event listener is called it fails. any hope would be much appreciated!

6 comentarios

Matthew
Matthew el 22 de Oct. de 2018
Editada: Matthew el 22 de Oct. de 2018
If anyone else has this issue I solved it by adding these lines to all the code to do with the event listener (the code that assigns it, the code that exercutes on it, and the code that removes it):
% Get the application data
hf = findall(0,'Tag',mfilename);
handles = guidata(gcf);
not sure why the 'hf' line is required but without it the guidata(gcf) command fails.
Adam
Adam el 22 de Oct. de 2018
For future reference:
gcf is always somewhat dodgy to use in any code other than temporary scripts. It relies entirely on the 'current figure' being what you expect it to be. At a guess the findall line makes the figure you expect the current figure, although I couldn't say for sure.
You should use explicit handles for figures, axes, plots and anything else you want to refer to again to avoid unexpected bugs popping up.
Matthew
Matthew el 22 de Oct. de 2018
Editada: Matthew el 22 de Oct. de 2018
Thanks for the link, that would be quite useful.
So the GUI has a specific name (which is linked to the name of the function file where it is defined, in this case Event_listener_GUI) but using guidata(mfilename) causes it to fail. first part of my post details me using 'guidata(mfilename)', I even tried manually setting the name of the GUI figure and it still kept failing and telling me that it wasn't a figure, despite the fact that the uncommented line 'gui_name = mfilename' gives the correct name in the command window, so stuck with gcf it seems.
Adam
Adam el 22 de Oct. de 2018
Using mfilename definitely won't work. I don't use Simulink so I don't know how things work there, but when I create a figure I keep its handle if I know I will need it again e.g.
hFig = SomeGUI( );
Then I can just use hFig as the figure handle later on instead of gcf.
Interesting, I made the GUI using Guide and it uses this line in its initialisation function to define the name of the GUI figure:
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @Event_listener_GUI_OpeningFcn, ...
'gui_OutputFcn', @Event_listener_GUI_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
Would it be worth forcing it to be a specific name somehow?
Adam
Adam el 23 de Oct. de 2018
You can change the 'Tag' in guide and I always do. By default it is just 'figure1' which is not very helpful. You still have to search for it afterwards if you rely on the tag, but if you are not able to keep the GUI handle itself for some reason it is the next best thing.

Iniciar sesión para comentar.

 Respuesta aceptada

Guillaume
Guillaume el 22 de Oct. de 2018

0 votos

The problem has nothing to do with the name of the GUI. The problem is simply that guidata wants a figure handle (or other graphics handle). The name of an object doesn't matter, it's never going to be a graphics handle, and guidata(mfilename) will never work.
gcf is always going to return a graphics handle as long as you have at least one figure open. As Adam said, it's dangerous because the active figure may not actually be the expected one.
hf = findall(0,'Tag',mfilename); is also going to return a graphics handle. So that could be used for guidata. That would be a lot safer than gcf since the object tagged with mfilename is going to be your GUI figure.
The commented out hf = gcbo; is also another way to get a graphics handle. It's probably the simplest method, since it's always going to return the graphics handle of the object that triggered the callback.

9 comentarios

Matthew
Matthew el 22 de Oct. de 2018
Editada: Matthew el 22 de Oct. de 2018
So I've tried the following lines and, unfortunately they've all errored by telling me that it's not a figure handle:
Replacing mfilename with hf line:
% Get the application data
hf = findall(0,'Tag',mfilename)
handles = guidata(hf);
Gives
hf =
0x0 empty GraphicsPlaceholder array.
Error using guidata (line 87)
H must be the handle to a figure or figure descendent.
Error in Event_listener_GUI>localEventListener (line 228)
handles = guidata(hf);
Warning: Error occurred while evaluating listener callback.
Using the commented out hf = gcbo line:
% Get the application data
%hf = findall(0,'Tag',mfilename);
hf = gcbo
handles = guidata(hf);
Gives
hf =
Simulink.RunTimeBlock
Error using guidata (line 87)
H must be the handle to a figure or figure descendent.
Error in Event_listener_GUI>localEventListener (line 229)
handles = guidata(hf);
Warning: Error occurred while evaluating listener callback.
And using gcbo inside guidata:
% Get the application data
%hf = findall(0,'Tag',mfilename);
%hf = gcbo
handles = guidata(gcbo);
Gives
Error using guidata (line 87)
H must be the handle to a figure or figure descendent.
Error in Event_listener_GUI>localEventListener (line 229)
handles = guidata(gcbo);
Warning: Error occurred while evaluating listener callback.
I just have no idea what's different between the example code and mine, On Phil's implementation it still gives all of the relevant data for the GUI, on mine it seams to reference a block in simulink or otherwise fail.
For reference the event listener is only called, and acted upon, once I click the start button from within the GUI.
Guillaume
Guillaume el 22 de Oct. de 2018
If findall(0,'Tag',mfilename) returns an empty graphics object, then you do not have any graphics object whose tag is the name of the current mfile.
If gcbo returns a Simulink.RunTimeBlock object, then your callback is being executed by a simulink object, not a graphics object. I'm afraid I don't know anything about simulink so can't help with that aspect, but it looks to me that you're adapting code designed for GUIs to something that is not a GUI.
Matthew
Matthew el 22 de Oct. de 2018
Editada: Matthew el 22 de Oct. de 2018
Well I'll keep plugging away at it, it's just strange how it's not finidng the GUI unless I use the gcf command. If I get it working without gcf I'll add something to the question, in the meantime the GUI is working as intended (updating a graph from the output of a Simulink model), just have to make sure no other figures are open.
Thanks for the help though!
Since when you use findall(0, 'Tag', mfilename), you don't get the GUI figure back, the first thing to check is what is the actual tag of that figure and does it match mfilename? So, what is the output of:
get(gcf, 'Tag')
and what is the name of the m file where the callback resides (which is what mfilename returns)?
If the two don't match, of course the findall won't work. Note that if your callback resides in its own mfile, then of couse, mfilename won't match the tag of the figure. In that case, it may be safer to specify the tag explicitly:
hf = findall(0, 'Tag', 'Whatever_the_tag_actually_is');
Matthew
Matthew el 22 de Oct. de 2018
Editada: Matthew el 22 de Oct. de 2018
So I've been faffing with it and I have now enabled Handle Visibility (didn't know about it before now) and I noticed that the tag was coming back as 'figure1' (see below) but now I can't find where that is defined, is it something that Matlab arbitrarily assigns?
I've been experimenting with using the 'Name' field instead of 'Tag' but I can't seem to make the code aware of the GUI name, other than by manually typing it in, which means it breaks if it is renamed in Guide. Not a massive issue admittedly but I like it when things auto update appropriately.
TL;DR: using
hf = findall(0,'Name',mfilename);
handles = guidata(hf);
Works
The default tag of any graphics object is '' (empty char array). To be anything different it has to be set explicitly by your code. In the simpleGUI code, you can see that it is set when the figure is created
function hf = localCreateUI(modelName)
try
% Create the figure, setting appropriate properties
hf = figure('Tag',mfilename,...
'Toolbar','none',...
So the tag is set to 'simpleGUI'. If it ends up being 'figure1' by the time your callback execute some other code must have explicitly changed it. Probably, the best way to find where is to do a search for 'Figure1' (then 'Tag' if that returns nothing)
Thank you! You made me realise that the tag is defined in Guide within the properties bar! now I can name it, out of curiosity though is there a way the code can find the tag and incorporate it without me having to explicitly state it? i.e. instead of setting the tag to 'Tag1' (example) in Guide, then doing:
hf = findall(0,'Tag','Tag1);
I can get it to find the tag itself and assign it to a variable, and then use that variable in the findall command?
I don't think you've understood how the code works. A tag can be anything you want and used for anything you want, but in this case its purpose is to make it easy for any any code to identify which figure is the UI figure. If a piece of code needs to access the figure but doesn't know what it is, it asks matlab to give it the figure with the given tag. If you know neither what the figure is nor what its tag is then you're lost.
So that
hf = findall(0,'Tag','Tag1');
is asking matlab: Of all the figures that are open which one is the one with tag 'Tag1'.
You don't have to use this mechanism. Instead you could ask for a figure with a given name. But once again, you'd have to know the figure name beforehand. Or you could use an external mechanism to share the figure handle. An often misused method for this is global variables. Do not use global variables!
If you have a handle to the figure you can of course get its tag:
get(hf, 'Tag')
%or
hf.Tag
But if you already have the figure handle, then you don't need to find it.
Matthew
Matthew el 23 de Oct. de 2018
You're probably right about the code, this is all new to me, but you have helped me solve my issue for which I am thankful!

Iniciar sesión para comentar.

Más respuestas (1)

Matthew
Matthew el 22 de Oct. de 2018
So it works when I use the following code:
function localEventListener (block, eventdata)
% Get the application data
hf = findall(0,'Tag',mfilename)
handles = guidata(gcf)
And this produces this output in the command window:
hf =
0x0 empty GraphicsPlaceholder array.
handles =
figure1: [1x1 Figure]
Model_Status: [1x1 UIControl]
Stop_Button: [1x1 UIControl]
Start_Button: [1x1 UIControl]
text2: [1x1 UIControl]
Gain_Value: [1x1 UIControl]
axes1: [1x1 Axes]
ModelName: 'event_listener_attempt'
ad: [1x1 struct]
output: [1x1 Figure]
EventHandle: {[1x1 handle.listener] [1x1 handle.listener]}
Removing the 'hf =...' line breaks the 'handles = ...' line for indeterminate reasons so I've left it in.
The example code I've been following however is structured like this:
% get the application data
hf = findall(0,'Tag',mfilename)
ad = guidata(hf)
And produces this in the command window:
hf =
Figure (simpleGUI) with properties:
Number: []
Name: 'Custom UI for controlling simpleModel.mdl'
Color: [0.9400 0.9400 0.9400]
Position: [0.3542 0.2852 0.2917 0.3898]
Units: 'normalized'
Show all properties
ad =
modelName: 'simpleModel'
tuning: [1x1 struct]
originalGainValue: '1'
gainValue: '1'
viewing: [1x2 struct]
originalStopTime: 'inf'
originalMode: 'external'
originalStartFcn: ''
modelAlreadyBuilt: 0
lineHandles: [14.0181 15.0095]
handles: [1x1 struct]
eventHandle: {[1x1 handle.listener] [1x1 handle.listener]}
But when I use that code in my version:
% Get the application data
hf = findall(0,'Tag',mfilename)
ad = guidata(hf)
It does this:
hf =
0x0 empty GraphicsPlaceholder array.
Error using guidata (line 87)
H must be the handle to a figure or figure descendent.
Error in Event_listener_GUI>localEventListener (line 228)
ad = guidata(hf)
Warning: Error occurred while evaluating listener callback.
Beyond using my, at best, workaround at the start of the post (as having more than one figure would break it) I haven't gotten it to work and am at a loss as to why it works in one file and not the other, ho-hum.

Categorías

Productos

Versión

R2014b

Preguntada:

el 19 de Oct. de 2018

Comentada:

el 23 de Oct. de 2018

Community Treasure Hunt

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

Start Hunting!

Translated by