Update figure in background without stealing focus?

I have a MATLAB script that periodically loads data output from a external model simulation of fluid flow, and then plots (or re-plots) it. When data are plotted, the figure window becomes the active window and is brought to the front of the screen, stealing the focus from any other window (emacs, word, etc).
I would like the matlab figure to simply update in the background so I can visually track model progress while working on other things.
Thanks!

1 Kommentar

Jess
Jess am 11 Jan. 2021
Oh for this to be a universal preference or setting that a MATLAB user could toggle and set and forget!

Melden Sie sich an, um zu kommentieren.

 Akzeptierte Antwort

Sean de Wolski
Sean de Wolski am 10 Apr. 2012
How are you updating the figure? For example this little function with a timer that updates a plot doesn't steal focus from me.
function example_focusin
T = timer('timerfcn',@updatePlot,'period',5,'executionmode','fixedrate','taskstoexecute',10);
figure;
h = plot(rand(1,10));
start(T);
function updatePlot(src,evt)
set(h,'ydata',rand(1,10));
end
end

11 Kommentare

Michael H.
Michael H. am 10 Apr. 2012
I have a gui and am updating dynamic text using the "set" command. For plots of the data, I use the "axes" command to specify the plotting axes, then use the "plot" command.
yeah, avoid plot, update the components directly
When doing this, is there anyway to avoid an error if the figure gets closed, and create a new one?
You can save the figure handle and check it
f = [] % Put at the beginning of your code (not in the function)
function [f] = updateFigure(f)
if isempty(f) || ~ishandle(f)
f = figure();
end
% update plot here
end
LeChat
LeChat am 17 Feb. 2020
Bearbeitet: LeChat am 17 Feb. 2020
Would you have an alternative if instead of plot I am doing the following (viscircle)?
%% loading movie in BioFormat:
reader = bfGetReader(fname);
%Individual planes can be queried using the bfGetPlane.m function:
IMG1 = bfGetPlane(reader, imgstart);
% make sure last index frame is at most the last of the movie
nbrIMGtot=reader.getImageCount;
if imgend >= nbrIMGtot
imgend = nbrIMGtot;
fprintf('Last image to be treatted is the end of the complete movie.\n');
end
image_range = [imgstart:1:imgend];
%
figure(10)
for ik=1:numel(image_range)
clf(10)
imagesc(double(bfGetPlane(reader,image_range(ik))))
% detect circles
IMAG=imbinarize(cleaned_img_ik,1500);
[centers, radii, metric] = imfindcircles(IMAG,[radmin, radmax],'Method','TwoStage','ObjectPolarity','dark');% range in pix
% %%%%%
hold on
viscircles(centers, radii)
end
Thank you!
You can record the handle returned by viscircles. It wil be an hggroup object, which has a Children property, and there will be one Chart Line object for each circle that is drawn. You can access those children and update their XData and YData properties.
Thank you for your answer. Following your advice, I tried:
vc=viscircles(centers,radii,'EdgeColor','y')
set(vc.Children,'xdata',[vc.Children(1).XData],'ydata',[vc.Children(1).YData])
It does not seem to work though.
I think what might cause the issue is that at each roll of the loop, I do imagesc which makes the window pops up.
Also, I am not sure how I should do if I detect several circles and I want to update their respectives XData and YData.
Thank you for your help.
imagesc() does not in itself make the window pop up.
set(vc.Children,'xdata',[vc.Children(1).XData],'ydata',[vc.Children(1).YData])
What is your theory about what that would do? I indicated that viscircles returns ah hggroup. hggroup are variable about exactly what properties they have, but I have never seen a hggroup that had an XData or YData property.
The XData and YData properties of the Line objects stored in the hggroup created by vc, are the complete set of cartesian coordinates to draw the circles, with 2 degree resolution.
which has a Children property, and there will be one Chart Line object for each circle that is drawn.
I was wrong about that. There are exactly two children created, unless the 'EnhanceVisibility' option was passed as false (in which case there is only one child.) The last child will be the inner line for all of the circles together, with nan separating the individual circles. If 'EnhanceVisibility' was not turned off, then the first child will be a thicker outer line using the same coordinates. The total number of XData values is (length(0:2:360)=181 + 1 separating nan) times the number of circles drawn at the same time; likewise for YData.
What you get out of imfindcircles is the centers and the radii, not the coordinates of the edge of the circle, so you cannot do a direct update.
Note too that you might get back a different number of circles each time.
There is a way to avoid updating all of the circle X Y coordinates that can sometimes be attractive. I will talk about it more after I do some laundry.
LeChat
LeChat am 19 Feb. 2020
• well actually my intension with [vc.Children(1).XData] (and same for YData) was precisely to access the coordinates of the points forming the edges of the detected circles (or more accurately here the first detected circles, for starters). I tried this piece of code and I got the coordinates of the edges of the circle. It was the set that was problematic.
• I indeed realized that there is one more complexety in the fact that I might detect more than one circle, hence the end of my last comment...
Thank you for your insights and patience :)
vc being the output of viscircles has no information about where the detected circles are -- that information is returned by imfindcircles() . viscircles only has information about where the circles are drawn on the plot, not where they are in the image.
Anyhow:
  1. Before: Use hgtransform() to create a reference hg transform group object.
  2. Before: Use plot() or line() to create a single circle of radius 1 and center [0 0], parented to the above hgtransform group. Do not use viscircles() for this purpose: for whatever reason, you cannot parent a viscircles() to a hgtransform group.
  3. Before: initialize a pool of handle objects with hgobjects(0)
  4. In loop: do the detection of circles
  5. In loop: count how many circles you got back
  6. In loop: if the number of circles you got back is fewer than the number of elements in the pool, then set() the extra pool entries 'Visible', 'off' -- do not delete them, just set them off.
  7. In loop: if the number of circles you got back is more than the number of elemetns in the pool, then use copyobj() as many times as needed to create copies of the reference hgtransform group, adding them to the pool.
  8. In loop: For as many entries as there are circles detected this time around, set the corresponding hgtransform group to have a transform matrix that scales by the detected radius, and translates by the detected center.
  9. In loop: set the Visible property to 'on' for the first so-many entries in the pool.
  10. In loop: drawnow() to make the changes visible.
LeChat
LeChat am 20 Feb. 2020
I'll try this out thank you.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (2)

Aastav Sen
Aastav Sen am 23 Nov. 2020
This works for me (adding data to a plot within a loop without having Matlab steal the focus so I can work on other things at the same time)! So in a nutshell:
Call before your loop:
fg = figure(1)
But during your loop when you want to update the contents of a figure this requires you to 'grab' the figure and set it to your current figure handle = 'gcf'. You do this using:
set(0,'CurrentFigure',fg);
Note: that if you instead used in you loop:
fg = figure(1)
This would set the 'gcf' to our figure (what we want), but, would also steal the focus (what we don't want)!
Hope this helps.

1 Kommentar

Jess
Jess am 11 Jan. 2021
That looks like something that would work if one was updating a single figure many times. (Is that correct?) If one was to make a dozen plots once each, wouldn't one have to do that a dozen times?

Melden Sie sich an, um zu kommentieren.

Sergio López-Ureña
Sergio López-Ureña am 19 Okt. 2020
You can select the figure by number.
set(groot,'CurrentFigure',figNumber);
It does exactly the same that
figure(figNumber);
but without focusing.
It is useful when you don't want to save the figure handler.

1 Kommentar

Matthieu
Matthieu am 15 Aug. 2023
Bearbeitet: Matthieu am 15 Aug. 2023
Thanks Segio for this tip, I then integrated it into a wrapper function to replace the figure() function:
function fig(indFig)
% Convenient function to replace the figure(X),
% Very convenient in loop where an update of figure must be done without stealing the focus
if nargin == 0
figure() % if no argument then just creates a new figure, but steal focus
return
end
try
set(groot,'CurrentFigure',indFig); % replace figure(indFig) without stealing the focus
catch
figure(indFig) % if indFig is not an exsting figure it creates it (and steal the focus)
end
% Drawnow is here in order to force update of the figure,
% Otherwise I observed that content of figure is not updated in loop.
% Normally 'drawnow' should take place at the end of plotting
% and NOT before plotting however in loop functions it updates
% with one loop late, this prevent to write 'drawnow' everywhere in the code.
drawnow

Melden Sie sich an, um zu kommentieren.

Kategorien

Mehr zu Graphics Performance finden Sie in Hilfe-Center und File Exchange

Produkte

Community Treasure Hunt

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

Start Hunting!

Translated by