timer misbehaving in GUI after upgrade to 2017b?

Many years ago I wrote some GUIs for animating 3D marker data from motion capture. I was using 2009b at the time and I know the guis worked fine in 2010b. I recently tried to run one of them with 2017b, but the timer is not behaving correctly. It started to play, but seemed choppy and would not respond immediately to button commands after the timer was started. The more I tested it, and tested other GUIs that use the same code for setting and controlling the timer, the worse it seemed to get, to the point the timer would completely hang up. All my guis using the same timer code seem to be affected.
If I run a simple timer from the command window it works fine, so I'm perplexed.
Because no error is generated I'm not even sure where to look to try to solve this problem.
Is there something about Matlab2017b timer control behavior in a GUI (built with GUIDE) that would be different from older (2009b) versions?
Not sure it is important, but the 'TimerFcn' callback code has a global variable "frm" (index frame) for displaying the current position of markers that is also updated. This has never been a problem and I don't think it is causing this, but otherwise I'm not sure how else to pass an index to the TimerFcn.

1 comentario

Jan
Jan el 26 de Feb. de 2018
Without seeing the code it is hard to guess, where the problem is.
Using the same timer code for several GUIs with the same global variable is a bad idea, but it should not produce the observed behavior. But what do you observe exactly? "seemed choppy and would not respond immediately to button commands after the timer was started" is not really clear. What is a "button command"?

Iniciar sesión para comentar.

 Respuesta aceptada

Cam Salzberger
Cam Salzberger el 26 de Feb. de 2018

0 votos

Hello Chris,
The biggest (relevant to you) change that has happened in that time is a complete change to the graphics engine in R2014b. This has had a large impact on GUIs in general, and especially on GUIs developed before that release and being used in a later release. I suspect that the timer isn't the issue, but instead the graphics input or updates causing the slow down.
Can you divorce the old timer code from the GUI, and use it with some text-only function to eliminate the GUI from the equation? If it works fine, then I suspect the GUI has some issues from the compatibility or inefficiencies due to the new graphics engine.
I'd highly suggest checking out the "Additional Resources" at the bottom of the new graphics engine page to get some suggestions for optimizing graphics operations.
-Cam

4 comentarios

Chris McGibbon
Chris McGibbon el 26 de Feb. de 2018
Editada: Chris McGibbon el 26 de Feb. de 2018
Hmmm... The fact that graphics handles are now objects, and not numeric values, might be the problem, but I'm not sure I understand how it even works at all then...
The GUI first draws a frame of data using the line function, assigns the handle to a variable which is then passed to the TimerFcn. The TimerFcn then uses the set command to update the line X,Y,Z data.
The code would look something like this:
The timer is set up in the GUI's OpenFcn. The global variable "frm" is set when the data are loaded. When user presses start button, the following callback is executed:
function pushbutton_fwd_Callback(hObject, eventdata, handles)
stop(handles.timerhandle);
handles.incframe = 1;
set(handles.timerhandle,'ExecutionMode','fixedRate');
handles = animdata(handles);
guidata(hObject, handles);
function handles = animdata(handles)
global frm; f=frm;
...
% - plot 3D points of nMarkers
for j=1:nMark
pmark(j) = line('XData',handles.data(1,j,f),...
'YData',handles.data(2,j,f),...
'ZData',handles.data(3,j,f),...
'Marker','o','Color','w','LineStyle','none');
end
handles.pmark = pmark;
handles.nMark = nMark;
% - set timer for animation
set(handles.timerhandle,'TimerFcn',{@showframe_Callback,handles});
start(handles.timerhandle);
function showframe_Callback(hObject, eventdata, handles)
global frm; f=frm;
pmark = handles.pmark;
nMark = handles.nMark;
for j=1:nMark
set(pmark(j),'XData',handles.data(1,j,f),...
'YData',handles.data(2,j,f),...
'ZData',handles.data(3,j,f));
end
frm = f+handles.incframe;
...
Yep, that was one of the big things that affected people in the graphics upgrade. Graphics objects now come with a fair bit of overhead every time you create an object. One of the big strategies for improving performance is to eliminate unnecessary object creation.
You are currently looping over the data, creating a line object with a single point in it. You're doing the right thing in the timer callback where you simply update the data, but there being however many nMark lines will slow down the code and graphics rendering.
So in the pushbutton callback, do this instead:
% - plot 3D points of nMarkers
pmark = line('XData',handles.data(1,:,f),...
'YData',handles.data(2,:,f),...
'ZData',handles.data(3,:,f),...
'Marker','o','Color','w','LineStyle','none');
And do it similarly when calling set in the timer callback.
Also don't forget to save the updated handles in the GUI application data by calling guidata near the end of the button callback.
One other big thing that applies to both pre and post-R2014b is to avoid passing large amounts of data in the handles structure. I know it's convenient, but every time a callback is called, handles gets copied into a new scope. If there's a large amount of data in it, that can quickly back things up. It's alright if you've just got a few settings or parameters in the form of scalars, but large amounts of data can tie up your memory and execution time.
One way to work around this is to save the data to the application data using setappdata. Then you can get it later with getappdata. The handles structure is stored there too, but using get/setappdata allows you to specify only getting the data you want when you need it, rather than copying the whole handles structure every time.
-Cam
Chris McGibbon
Chris McGibbon el 27 de Feb. de 2018
Editada: Chris McGibbon el 27 de Feb. de 2018
What ended up working was adding the "drawnow" command to end of the TimerFcn code. This seems to fix the issue with the timer hanging, presumably by forcing it to the prescribed period.
But it's probably just a band-aid... The markers still "flicker" during the animation playback, which tells me there are still some issues with this legacy code that I haven't found yet.
I'm not entirely convinced it's from passing the handles structure to the animate function... I created a brand new skinny version to troubleshoot the problem and it had the same problem.
But I will try to use setappdata/getappdata for passing the larger arrays -- this makes good sense.
Thanks for all the help!
Cam Salzberger
Cam Salzberger el 27 de Feb. de 2018
No problem. I'm glad I was able to help at least a bit. Using drawnow makes sense if you want to see the graphics updates immediately, but there is a lot of other code running as well. Typically this is an issue when updating graphics in a loop, not from a timer callback, but it's reasonable that it could happen then too. Sorry for not thinking to suggest that before.
One thing to make sure of is that you aren't clearing the axes at any point in this animation cycle. I didn't see anything like that in your code, but maybe it's somewhere else. The flickering could also be caused by the axes limits changing every time the line data is changed. If you know that the data will always be within a reasonable range, you could set the limits manually, and ensure that 'XLimMode', 'YLimMode', and 'ZLimMode' are 'manual' to avoid limits changing.
-Cam

Iniciar sesión para comentar.

Más respuestas (0)

Categorías

Más información sobre Graphics Performance en Centro de ayuda y File Exchange.

Preguntada:

el 26 de Feb. de 2018

Comentada:

el 27 de Feb. de 2018

Community Treasure Hunt

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

Start Hunting!

Translated by