What would cause Matlab to lose track of gcf()?

I'm trying to reproduce a bug/reduce it to something I can report, but so far it only happens when I call a particular 150-line function. I can put the offending code, up to and including the line that causes the error, in a new function and it runs fine, so I imagine there's something going on behind the scenes with the other 140 lines. T̶h̶i̶s̶ ̶c̶o̶d̶e̶ ̶r̶u̶n̶s̶ ̶f̶i̶n̶e̶ ̶o̶n̶ ̶W̶i̶n̶d̶o̶w̶s̶.̶
If I pause inside the function and type
which gcf
I get: built-in (/Applications/MATLAB_R2024a.app/toolbox/matlab/graphics/gcf)
Which is correct. But when I call gcf immediately after, I get:
Unrecognized function or variable 'gcf'.
Sorry I don't have an example to upload. I'm just looking for troubleshooting ideas.

5 commentaires

dpb
dpb le 7 Sep 2024
Modifié(e) : dpb le 7 Sep 2024
Try the "divide and conquer" technique...comment out the rest of the function and ensure it runs, then uncomment roughly half, etc., ... If it is something such as your hypothesis, that will narrow down where pretty quickly.
Chris
Chris le 7 Sep 2024
Modifié(e) : Chris le 7 Sep 2024
Woof, what a journey.
So first of all, the behavior is the same on Windows. I must have modified this function on the mac, got distracted, and ran the code later. Boiled way down, I ended up with something like this:
function findThisFig
spy
gcf().Children.Title % To demonstrate there's a figure
% gcf().Children.Title.String = "I AM ERROR"
end
findThisFig
ans =
Text with properties: String: '' FontSize: 11 FontWeight: 'bold' FontName: 'Helvetica' Color: [0 0 0] HorizontalAlignment: 'center' Position: [46.0001 -1.0583 2.2204e-16] Units: 'data' Use GET to show all properties
------------------------------------------------------------------------------
Now, if I switch out the last line of the function, an error is thrown:
function lastLineError
spy
% gcf().Children.Title % To demonstrate there's a figure
gcf().Children.Title.String = "I AM ERROR"
end
lastLineError
At least one index is required.

Error in solution>lastLineError (line 16)
gcf().Children.Title.String = "I AM ERROR"
[Continued Below]
Chris
Chris le 7 Sep 2024
Modifié(e) : Chris le 7 Sep 2024
[Cont'd]
If I leave both lines uncommented, the error moves up to the first line.
function prevLineError
spy
gcf().Children.Title % To demonstrate there's a figure
gcf().Children.Title.String = "I AM ERROR"
end
prevLineError
Unrecognized function or variable 'gcf'.

Error in solution>prevLineError (line 5)
gcf().Children.Title % To demonstrate there's a figure
I had a few dozen lines of code between the two offending lines, calling other functions and creating and closing figures (yes, it's ugly code). So when the error moved up to the top, it became very difficult to trace.
In lastLineError(), Matlab tries to create a struct array called gcf, but throws an error because there's no index provided.
In prevLineError(), it seems to skip over that error. I'm not sure if this is intended behavior, or what.
dpb
dpb le 7 Sep 2024
Modifié(e) : dpb le 7 Sep 2024
I figured it would be something of the sort where somehow you would have redefined gcf or got bad syntax with it...glad you did find it.
But,
gcf().Children.Title % To demonstrate there's a figure
I would strongly recommend against writing code of such nature where you call either gcf or gca repeatedly to refer to the same figure/axes; besides such syntax errors as the above, since MATLAB is event driven, it is possible an external event such as user interaction can change focus and instead of the desired figure/axis being returned, you get what that new focus is.
Much more reliable coding is to save the handles of the objects when you create them, or if it is necessary to call gcf; then do it like
hF=gcf;
...
and then use hF consistently in the function -- you'll avoid such issues as writing such bad syntax and also be assured of using/referring to the correct figure later on. If you do have multiple figures/axes, create an array of grapics objects handles to hold them.
% gcf().Children.Title.String = "I AM ERROR"
and certainly, trying to assign to a property of an inderect reference to the property of the secondary graphic object is not surprising to have confused the parser even if it isn't specifically written to prohibit such.
axes
gcf().Children
ans =
Axes with properties: XLim: [0 1] YLim: [0 1] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1100 0.7750 0.8150] Units: 'normalized' Use GET to show all properties
gcf().Children.Title
ans =
Text with properties: String: '' FontSize: 11 FontWeight: 'bold' FontName: 'Helvetica' Color: [0 0 0] HorizontalAlignment: 'center' Position: [0.5000 1.0077 0.5000] Units: 'data' Use GET to show all properties
does return the handle to the title, but trying to do an assignment in that syntax is just expecting too much.
But, consider
figure
axes;
colorbar;
gcf().Children
ans =
2x1 graphics array: ColorBar Axes
and now you've got an array of children and what is
gcf().Children.Title
Unrecognized method, property, or field 'Title' for class 'matlab.graphics.primitive.world.Group'.
to try to do now?
It is far better to save handles to the objects when you create them and use those, but if you find you simply must find a particular object such as the title in an axes, then use findobj to do so by 'Type' or 'Tag' or similarly.
Chris
Chris le 7 Sep 2024
> I would strongly recommend against writing code of such nature where you call either gcf or gca repeatedly to refer to the same figure/axes;
In the original code I was calling functions (created for another purpose) that generate figures I either didn't want here, or wanted to modify. The first problem line was close(gcf), getting rid of one of those figures.
The final problem line was immediately after another function call, to change the titles in a tiledarray it had generated.
But yeah, saving the handles at least causes the correct error to be thrown. And of course, cleaner code would have made this much easier to trace.

Connectez-vous pour commenter.

 Réponse acceptée

Walter Roberson
Walter Roberson le 7 Sep 2024

0 votes

Unrecognized function or variable 'gcf'.
You can get that error if you had previously defined gcf as a variable in your code, but then you cleared the variable. When you create a variable then MATLAB removes the name from the list of available functions; when you then clear the variable it no longer knows it as a variable and does not add it back in as a function.

6 commentaires

Right, but the error is introduced in this case after the function would be called acceptably (gcf().Children.Title), and it raises an unhelpful error message because it refers to a line that would otherwise be correct. Hence my initial confusion.
function example
spy
close(gcf)
% 100 lines of irrelevant code
gcf().Children.Title.String = "error"
end
example
Unrecognized function or variable 'gcf'.

Error in solution>example (line 3)
close(gcf)
Stephen23
Stephen23 le 11 Sep 2024
Modifié(e) : Stephen23 le 12 Sep 2024
I suspect that the root of this problem boils down to an unfortunate design decision several decades ago, which made it impossible to distinguish calling a function from indexing into a variable using only static code parsing:
A(1,1) % calling a function or indexing?
Recent changes to the JIT engine have apparently tightened some rules regarding using the same name for both a function and a variable in the same code. It is important to note that MATLAB is not an interpreted language: it does not run the 1st line, then the 2nd, then the 3rd etc. until getting to the end or throwing an error. Such things were done long ago in the misty past. Instead the modern JIT engine takes the code and already provides code optimisations and compilation before/while it is run:
This means that it must use some code analysis to determine what is going on... which we have already seen, static code analysis has some fundamental... ummm, weak points.
For better or for worse, this line tells the JIT engine that GCF refers to a variable (I am guessing because of the lots of trailing indexing makes it think that the thing on the left must be a variable):
gcf().Children.Title.String = "error"
So the JIT engine thinks to itself: where is the variable GCF defined? Which it isn't, thus the error.
The solution is simple: make the code clearer for the poor old JIT engine by making it clearer what your code is referring to**:
tmp = gcf();
tmp.Children.Whatever.String = 'hello';
Try it and let me know what happens.
** Adding another line does not make MATLAB code less efficient: code golf might be popular, but it is (in the case of the JIT engine) not 100% synonymous with efficient code. Code clarity actually helps the JIT engine. Explicit is best.
Chris
Chris le 11 Sep 2024
Modifié(e) : Chris le 11 Sep 2024
In that case the error makes sense. Thank you for this bit of history.
function example
spy
close(gcf)
% 100 lines of irrelevant code
tmp = gcf();
tmp.Children.Title.String = "error"
end
example
Unrecognized method, property, or field 'Title' for class 'matlab.graphics.GraphicsPlaceholder'.

Error in solution>example (line 8)
tmp.Children.Title.String = "error"
After the first close(gcf) the existing figure would be closed. If there are additional figures with HandleVisibility set on, then one of them will (probably) be made the current figure and be returned by the gcf() that follows; otherwise the gcf() that follows would create a new figure and return it.
I'm a bit lost as to why the second gcf() returned a graphics placeholder. I speculate that the figure created by the gcf() is in the process of being created.
dpb
dpb le 12 Sep 2024
Modifié(e) : dpb le 12 Sep 2024
figure;
close(gcf)
% nongraphics code here...
tmp=gcf;
tmp.Children
ans =
0x0 empty GraphicsPlaceholder array.
If there were only one figure when closed gcf(), then the gcf() call subsequently will create a new one; but that's all it will have done at that point.
Still is very unsafe code construction using implicit handles; there's no guarantee about what the Children of gcf() will be; is not necessarily even a single object, nor any, the latter of which is the case here because creating the default figure only results in the figure handle itself; there are no children until put an axes or other allowable child of a figure graphics object into it...referring to an empty figure's children does return the placeholder without error, but then trying to address a property of an as yet uncreated child object rightfully errors.
figure; axes;
figure;
close(gcf)
% nongraphics code here...
tmp=gcf;
tmp.Children
ans =
Axes with properties: XLim: [0 1] YLim: [0 1] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1100 0.7750 0.8150] Units: 'normalized' Use GET to show all properties
Alternatively, if there were multiple figures extant and an earlier one did have something inside it; then the result would be the previous....
Again, can't emphasize enough how unsafe/undpredictable such coding practice is and should be strictly avoided.

Connectez-vous pour commenter.

Plus de réponses (1)

Image Analyst
Image Analyst le 7 Sep 2024
Modifié(e) : Image Analyst le 7 Sep 2024
Don't do it like that: gcf().something. Assign something to gcf, like g:
g = gcf; % No parentheses after gcf. I think a figure must have already been created before doing this.
then you can set properties of g to change the gcf properties:
g.WindowState = 'maximized' % Maximize the figure window.
When you do
gcf().Children.Title % To demonstrate there's a figure
it assumes gcf is a variable and is a vector. But it is NOT. But since you put parentheses, it assumes it is and it wants an index inside the parentheses, which you didn't provide. That's why it's giving you the error "At least one index is required." As far as I know gcf just refers to the current figure and therefore is never an array and so won't take parenthese or an index. If you need a bunch of figures up simultaneously and need to manipulate their properties, you need to save their handles, which can be an array of handles, or just simply separate variables, like
g1 = figure('Name', 'This is figure 1');
g2 = figure('Name', 'This is figure 2');
g3 = figure('Name', 'This is figure 3');
Then you can manipulate whichever one you want individually, as long as that figure window still exists (you haven't closed it).

1 commentaire

Chris
Chris le 11 Sep 2024
Modifié(e) : Chris le 11 Sep 2024
> As far as I know gcf just refers to the current figure and therefore is never an array and so won't take parenthese or an index.
The function gcf offers dot notation for accessing the properties of the figure. If I try gcf.Children, I get an error:
gcf.Children
Dot indexing into the result of a function call requires parentheses after the function name. The supported syntax is 'gcf().Children'.
So gcf().Children is correct.
Clearly, it's not advisable to attempt this inside a function, however.

Connectez-vous pour commenter.

Catégories

En savoir plus sur Graphics Performance dans Centre d'aide et File Exchange

Produits

Version

R2024a

Tags

Community Treasure Hunt

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

Start Hunting!

Translated by