Matlab figure focus - old (highly annoying) changes

15 views (last 30 days)
Rather a long time ago matlab changed focus policy for figure windows. Prior to that change the focus was always determined by figure() and not by which window had been focused. After that change focus was determined by the last of figure() or which ever figure window had received mouse focus.
On windows this might make some sort of sense*. Elsewhere it can be highly annoying. On linux when the window manger mouse focus policy is set to "focus follows mouse" it can result in your plots just ending up on random windows.
Is there anyway to make matlab respect the figure() setting of figure focus and ignore whether the window has received mouse focus.
This is driving me mad - I've scripts that need to run over long periods and produce many figures - they are basically scrambled unless I stop using my computer while they run.
  • well not sense but it might be slightly less baffling for newbies

Accepted Answer

zdvd
zdvd on 25 Jul 2013
Latest version with better compatibility with the original functions. For those interested this restores the figure focus policy that matlab used prior to 2006 and works nicely with focus follows mouse arrangements. It gives priority over figure focus to the CLI and scripts over where the mouse has been / been clicked.
The documentation for figure() and axes() is rather woolly on my version so I've attempted to reproduce the observed behaviour rather than anything else.
figure.m
%%fix the annoying focus follows mouse bug
%%(c) GPL SA 2013 v1.0
function h=figure(varargin);
global hasfocus;
if(nargin<2)
if(nargin<1)
n=1;
else
n=varargin{:};
end
h=builtin('figure',n);
jFig = get(h,'JavaFrame');
jAxis = jFig.getAxisComponent;
set(jAxis,'FocusGainedCallback',{@setfocus,h});
hasfocus=h;
else
h=builtin('figure',varargin{:});
end;
axes.m
%%fix the annoying focus follows mouse bug
%%(c) GPL SA 2013 v1.0
function h=axes(varargin);
global hasfocus;
if(nargin~=1)
%%in this circumstance we'll either create an axes in the current figure
%%or create a new axes both of which are handled here and we return the axes
%%handle
h=builtin('axes',varargin{:});
hasfocus=get(h,'Parent');
return;
end
if(nargin==1)
%%under this circumstance axes does not return a handle
%%if it is successful we should make this figure current
%%since this axes will be current, but if not we'll throw
%%an error and keep the same focus
if(nargout>0)
%%try to maintain compatibility with builtin('axes')
%%by throwing this error if a return is asked for here
error('MATLAB:maxlhs','Too many output arguments.')
end;
try
builtin('axes',varargin{1});
%%since there was no error we set the focus appropriately
hasfocus=get(varargin{1},'Parent');
catch
%%we caught the error
rethrow(lasterror)
disp('some error in case it wasn''t printed');
end
return;
end;
if true
% code
end
setfocus.m
%%fix the annoying focus follows mouse bug
%%(c) GPL SA 2013 v1.0
function setfocus(jAxis, jEventData, hFig)
global hasfocus;
%%deal with the case when the focussed window has been lost
%%I can't remember what the correct behaviour should be so
%%I'll resort to doing nothing / focusing the current window
%%and setting the focus to the current window - nasty
t=get(0,'Children');
t=find(t==hasfocus);
if(isempty(t))
%printf('Trying to focus a dead window doing nothing',hFig);
hasfocus=hFig;
return
end;
set(0,'currentfigure',hasfocus)
end

More Answers (6)

Jan
Jan on 24 Jul 2013
Edited: Jan on 24 Jul 2013
I never trust on the current figure settings exactly for the named reasons. Even under Windows a complex GUI could get very confused, when the user clicks on any element in the current or other figure. Therefore any change or addition of a HG-property has a defined 'Parent' and I never rely on GCA or GCF.
FigH = figure;
AxesH = axes('Parent', FigH, 'NextPlot', 'add')
for k = 1:100
plot(1:100, rand(1, 100), 'color', rand(1, 3), 'Parent', AxesH);
pause(0.5);
end
Now you can click where ever you want, but the diagram will be clean.
Of course it means a lot of work to apply this to an existing program. But this is an expected side-effect of using volatile global properties (like the currently focused window). Notice that even without user-interaction the focus can move, e.g. by a timer-controlled progressbar.
A more dirty method would be to shadow GCF by a version, which stores the handle of the fixed-focus persistently. Then the new handle must be specified once when a new figure is opened. But notice, that changing builtin function can have unexpected side effects, while the above suggested clean programming techniques are simple, straight and fail-safe.
  3 Comments
Jan
Jan on 24 Jul 2013
Edited: Jan on 24 Jul 2013
@Daniel: A good question! Because then AxesH is a magic number, which is similar to the magic string a bad programming practice:
a = axes; % e.g.: 0.0009765625
plot(a, pi, 'o');
This draws the point at x=1, y=pi. But what if the very unlikely situation occurs, that I want to draw x=0.0009765625, y=pi ? I can't. Unfortunately handles are standard double values such that confusions cannot avoided - at least until R2013a, which has the new gobject command to allocate a vector of graphic handles. I hope that the upcoming HG2 graphics engine defines handles as a dedicated type.
The chance for collisions is tiny. But the rule is easy: Magic number and magic strings will collide, when you don't expect it. This is the nature of magic!
Daniel Shub
Daniel Shub on 24 Jul 2013
Nice. It will be nice when graphics handles are actual graphics objects.

Sign in to comment.


zdvd
zdvd on 24 Jul 2013
No one? I know so many people affected by this problem, surely there is an answer?
  1 Comment
Jan
Jan on 24 Jul 2013
Please do not bump your question by posting a pseudo answer. Because bumping is not efficient at all, waiting at least 24 hours is a fair limit. Usually questions are not answered, if they do not contain enough information. Then bumping does not help, but adding more details by editing the question is much better. Thanks.

Sign in to comment.


zdvd
zdvd on 24 Jul 2013
We've a large amount of legacy code over many users which now misbehaves (for a long time this could be fixed by using -nojvm on the comand line but this now has other unpleasant side effects) and the long handed way simply isn't practical.
It would seem that overloading figure() to globally store the figure number and then capturing window focus events (and set(0,'currentfigure',XX);) should pretty much fix the problem. Question is how to capture window focus events - anyone?

zdvd
zdvd on 24 Jul 2013
Edited: zdvd on 24 Jul 2013
Solution found -
(1) overload figure.m (place this function in your path)
%%fix the annoying focus follows mouse bug
%%(c) GPL SA 2013
function h=figure(n,a);
global hasfocus;
if(nargin<2)
if(nargin<1)
n=1;
end;
h=builtin('figure',n);
jFig = get(h,'JavaFrame');
jAxis = jFig.getAxisComponent;
set(jAxis,'FocusGainedCallback',{@setfocus,h});
hasfocus=h;
else
h=builtin('figure',n,a);
end;
(2) catch the mouse focus events (place this function in your path)
%%fix the annoying focus follows mouse bug
%%(c) GPL SA 2013
function setfocus(jAxis, jEventData, hFig)
global hasfocus;
set(0,'currentfigure',hasfocus)
end
(3) If you are bothered by warning messages spamming up your console put this line in your matlab start up files:
warning off MATLAB:dispatcher:nameConflict
Works for me on matlab 2007something
  3 Comments
Daniel Shub
Daniel Shub on 24 Jul 2013
This seems like a disaster hack. From what I can tell clicking on a figure will not necessarily change the focus to that figure. If however I change the focus (e..g, with axes(hax)) and plot into this new figure and then click on that figure, the current figure will revert to the previous figure. That seems non intuitive. I would suggest fixing your broken code instead of breaking MATLAB.
Jan
Jan on 24 Jul 2013
@Daniel: A nice and wise suggestion.

Sign in to comment.


zdvd
zdvd on 24 Jul 2013
warning off MATLAB:dispatcher:nameConflict I agree this hides some potential problems but these are very few and far between - I much rather hide just this conflict but I don't know how and I can't live with ML spamming the screen up all the time.
"Your overloaded figure() command accept two inputs only?" - just didn't have enough time to look up varargin - I will fix this.
"fixing your broken code instead of breaking MATLAB" -IMO matlab has been severely broken in this respect for ages - try it on a system with focus follows mouse you'll soon agree. As for axes(h) - this probably needs overloading too but I've never used this to change focus in over 15 years so I'll leave it to someone else!
  2 Comments
Daniel Shub
Daniel Shub on 24 Jul 2013
I do use MATLAB on a system where the focus follows the mouse. That is why my code, like Jan's specifies which axis to plot into in the plotting call and doesn't depend on the global state of the current figure. In fact, I would guess this is the reasons why TMW introduced the ability to provide a handle for all its plotting functions. I am glad you found a solution to your problem. I hope it doesn't cause you any bigger problems in the future.
Jan
Jan on 24 Jul 2013
Edited: Jan on 24 Jul 2013
@zdvd: Threads are easier to read, when the answer section is used for answers, and the comment section for comments. Here it is the other way around.
IMO matlab has been severely broken
I do not agree. Matlab allows a lazy programming style for convenience. Fast hacks for proof of concepts can profit from the automatic determination of the parent. But this is not a serious and reliable programming style. No programming environment can securely guess, what the programmer wants. And if the chance is used to let Matlab try its very best with guessing, Matlab cannot be blamed, when this fails.
As long as TMW still does not implement feature('dwim'), the lazy auto-parenting must fail, when multi-threading or nervously interacting users come into play. I'm convinced that no workaround can fix this without limiting the functionality at another point. I imagine a timer controlled progress-bar, which seems to let your posted figure replacement run into troubles. And then the programmer will reply, that the code works properly on all other Matlab setups in the world except for yours.

Sign in to comment.


zdvd
zdvd on 9 Jul 2014
This hack works for me perfectly - just moved the overloaded functions to a new dir and added the path in my startup.m.
Adding the functions in my usual path created some problems with octave which shares the same path in our systems.
I ideally matlab would fix this problem as realise that the last figure focussed is not necessarily the figure you want to plot to!

Categories

Find more on Interactive Control and Callbacks in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!