How do I bugfix a builtin function? (How do I call a shadowed function?)

6 vues (au cours des 30 derniers jours)
Florian
Florian le 2 Mar 2023
Commenté : Florian le 13 Mar 2023
Hi,
So, I encountered a bug in the "questdlg" function of my Matlab version (R2020a). Bug is also reported, but I want to fix it on my side so I can just continue with the rest of my project. For those interested in the bug, I think its in lines 98-100 of questdlg:
FigPos = get(0,'DefaultFigurePosition');
FigPos(3) = 267;
FigPos(4) = 70;
The values for FigPos(3:4) seem to be pixels I guess. But I have 'DefaultFigureUnits' set to 'normalized' on my installation (I just like it when I can define the on-screen sizes independent from what monitor or resolution I am using right at the moment). So FigPos now mixes the units here what leads to some large dialog window that is half off screen.
% this works
set(0,'DefaultFigureUnits','pixels');
questdlg('Questions?');
% this is bugged
set(0,'DefaultFigureUnits','normalized');
questdlg('Questions?');
Now, I was trying to fix that bug for me in some way but couldn't figure out what the best approach is. Maybe you can share any ideas? Here are my thoughts / approaches:
  • First I thought to just edit the original file. But a) thats is writing protected and b) it feels kind of messy and hard to keep track of. I have a GIT reprository for all my self-written Matlab code, so that I can share it over all my computers at work and home. So I guess it would be nice to have the bugfix also within the GIT folder.
  • Next idea was to just copy paste the questdlg file into my folder and then fix the bug. The new file would shadow the Matlab original. However, that also doesn't really work for several reasons: 1) questdlg uses private functions that are no longer available from my folder, or I would need to copy all private functions as well... that would get quite messy. 2) If I ever use a different Matlab version on another computer I need to keep track of changes in the original file to keep it up-to-date.
  • Next option: I create a new file "questdlg_fixed" that just sets the default units to pixels, then calls the original questdlg, and afterwards sets the units back to normalized. (Also needs to catch any errors or other stuff, but that wouldn't be too complicated.) That seems to be the simplest solution, but it only fixes the bug for my code where I explicitly use "questdlg_fixed". Any third-party code that uses questdlg will still have the bug.
  • So, last I came to what I think would be the "optimal" solution. A function with the same name that changes the units and then calls the original function. Like this code here:
function answer = questdlg(varargin)
units = get(0,'DefaultFigureUnits');
set(0,'DefaultFigureUnits','pixels');
% This should call the original function!
answer = questdlg(varargin{:});
set(0,'DefaultFigureUnits',units);
end
(error handling still missing, but thats not important right now.) That function won't work because the new "questdlg" shadows the original Matlab function. I haven't found any solution to this yet. Only way would be to go into the directory of the original function and call it from there. But that feels kind of hacky and not right.
function answer = questdlg(varargin)
units = get(0,'DefaultFigureUnits');
set(0,'DefaultFigureUnits','pixels');
% This should call the original function!
pth = which('questdlg','-ALL');
pth = fileparts(pth{2}); % directory of the shadowed function
pth = cd(pth);
answer = questdlg(varargin{:});
cd(pth); % back to current directory
set(0,'DefaultFigureUnits',units);
end
So, any thoughts on this? Did you ever run into that problem and how was your approach to solve it?
  4 commentaires
Stephen23
Stephen23 le 4 Mar 2023
@Florian: I think I misunderstood where you are planning on saving the new QUESTDLG, which would be locally among your own files, not the installation folders. Is that correct?
"I just want EVERY figure to open that size, not only the once from my own functions"
Hummm... that is certainly an interesting challenge. The "optimal" approach you showed would be worth trying: give it a try and let us know how it goes.
Florian
Florian le 5 Mar 2023
Yes, its stored among my own files. Its working so far. Didn't encounter any troubles, although it still feels a bit hacky.
To clarify, by "every figure" I mean every standard figure that did not set its own values. If any function uses its own parameters to plot figure, I assume whoever wrote that function had good reasons for it and it should be plotted exactly the way they intend. Only for all other figures that use the standard values, I changed it in a way its more comfortable for me.

Connectez-vous pour commenter.

Réponses (1)

Jan
Jan le 2 Mar 2023
Modifié(e) : Jan le 2 Mar 2023
"I have 'DefaultFigureUnits' set to 'normalized' on my installation." - This is a bad idea which can and will cause unexpected behavior. It is strongly recommended not to change the Default-Properties, because this influences all other software also.
It is much safer to define your own defaults only, e.g.:
figure('Name', 'A test', ...
myFigProps);
function Prop = myFigProps()
Prop.FigureUnits = 'normalized';
end
This is a small addition, which protects collisions with other projects. Imagine that you want to install a toolbox from someone else, which changes the Default properties also.
Changing the current folder by cd() just to call a specific function is a bad idea also. (Too) many software rely on the current folder to be at a specific place. Even a temporary change can influence e.g. callbacks of timers or GUIs. Prefer the function builtin() in your case.
Modifying the original code or creating a modified version and store it in a folder on top of the path are working reliably on your computer. But using your project code on another computer or installing 3rd party tools on your computer can cause collisions again.
So the reliable and clean solution is to leave the Default properties untouched, and use the above approach. Another option is to use a wrapper for the figure() command:
function H = myFig(varargin)
H = figure(varargin{:}, 'FigureUnits', 'normalized');
end
E.g. the command dialog() is such a wrapper, which adjusts all properties, which do not match dialog windows.
Using dedicated functions instead of modifying the Default properties is satble and safe. There are no collision with Matlab's built-in functions or 3rd party tools. In addition this is much easier to maintain and no workarunds are required to distinguish wanted from unwanted behavior.
But to answer the question: I do use modified built-in functions also. E.g. ind2rgb() can be accelerated massively without changing the behavior. My initialisation tool checks, if the current Matlab session uses the slow original version, and if so adds a folder on top of the path, which contains the faster version. But in this case there are no interferences with other functions.
And if any interferences would be possible, I choose another way. Remark:
  • If a code is good, it will grow and will be used by others also. So start avoiding potential interferences with other platforms and new Matlab versions as good as possible. If a software has grown to 100'000 lines of code, fundamental changes are much more difficult or even impossible.
  • If a code is not good - well, sigh, delete it or treat it as funny hack.
A very expensive software project between Lidl (a German supermarket chain) and SAP has failed after years of work, because it was impossible to adjust the different definitions of what the price of goods in the stock. For the SAP database it was the prie for buying, but for Lidl the current selling price in the sale matters. After 500 Million Euro have been spent, it got clear, that the difference cannot be solved.
I know, the example seems to be exaggerated, but it shows, that even professionals can and do loose the the overview of side-effects, when their program design include too many dependencies. Avoid collisions by using personal wrappers, if a personal behavior is wanted.
  3 commentaires
Jan
Jan le 5 Mar 2023
@Florian: The first point is correct: builtin() is limited to: "A built-in function is part of the MATLAB executable", so functions implementes as M-file cannot be caugth. But I've focussed on the mentiones figure() command, which is a built-in function.
For the second point: Of course you have to copy the private functions also or insert them as local subfunctions.
"However, I want EVERY figure (that doesn't have its own settings defined) to open like this." - This is exactly the point I meant in my answer: There is no chance to identify figures, that does not habe its own settings defined, magically. Changing the default properties will cause collisions with all functions, which expect the standard defaults. It is expected that foreign function get troubles, if you change the defaults.
"Thats what I think standard settings are made for." - Obviously this is not the case. So I might agree, that the Default properties should have this power, but as you see, they don't.
"then I would call it bad practice from the side of Matlab" - I agree. I cannot provide an answer, which fixes all functions of Matlab's toolboxes.
"I don't see any problem with this idea?" - From a theoretical or practical point of view?
Of course you can fix all GUI functions of Matlab's toolboxes. The next update or upgrade will remove your changes and there are severe problems to share code with others. For me, these restrictions would be a serious problem.
Florian
Florian le 13 Mar 2023
"There is no chance to identify figures, that does not have its own settings defined, magically." - Well, there is: If figure() without any additional parameters is called, then it uses standard settings, if it is called with additional parameters then it overwrites the standard settings.
But I see, we agree on the point what default settings should be made for. The question is, is "questdlg" just the one exception that has this bug? (Which means overwriting that function would totally fix the problem) Or is it just a symptom of a greater problem, that Matlab generally doesn't expect you to change its defaults? If I find time, I might investigate on this a bit more.
Anyway, your approach of overwriting the figure function and setting "my defaults" within the overwritten function is a good idea. I will try this out.

Connectez-vous pour commenter.

Catégories

En savoir plus sur Develop uifigure-Based Apps dans Help Center et File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by