Problem 1917. click away
Write a function that clicks on a GUI button.
Description:
Some times one wishes to use Matlab to automate some time consuming repetitive process that requires filling fields or clicking buttons on the screen. Matlab offers a simple way to control the cursor position (e.g. set(0,'pointerlocation',...), but it is not so clear how one would perform mouse or keyboard clicks.
This problem creates a simple message box:
msgbox('Click the OK button','')
which displays the above message and an 'OK' button.
Your function just needs to actually click on the OK button (or use equivalent keyboard shortcuts) to pass this problem. Simple, right?
Solution Stats
Problem Comments
-
11 Comments
Not sure what you want at this problem, Alfonso, but it is not to simply click on the Ok button. All solutions that I've seen add some sort of delay or hack to solve it, when we press the ok button manually, the figure is deleted, but your test code checks if the figure was deleted (which is the normal behaviour when the ok button is clicked).
PS: Using java for instance indirectly adds a delay, because the required classes must be loaded into memory before running them.
That's code taken from the msgbox.m
%%%%% doKeyPress
function doKeyPress(obj, evd)
switch(evd.Key)
case {'return','space','escape'}
delete(ancestor(obj,'figure'));
end
end
PS: Even the solution from Sean de Wolski, which is the most complex, only works because of the Java virtual machine delay.
@Rafael, this problem simply expects you to click the Ok button. As you can easily see, clicking the Ok button manually on a GUI message box is not the same as executing the associated msbox callback, simply because graphic object callbacks are not executed immediately, they are processed by the graphic engine at a time that depends on many different factors. The testsuite "drawnow" command forces that callback update, so that allows the testsuite to differentiate between solutions that delete the message-box vs. solutions that actually click on the Ok button (either by generating an associated pending callback, or by simulating that effect through clever timing shenanigans). Note: it is not a matter of adding a delay to your function (e.g. pause(1); delete(findall(0,'type','figure')); would not pass the testsuite), or java functions having some inherent delay due to loading libraries, it is a matter of delaying the actual execution of your function to simulate what an actual graphic callback would do (or of course simply figuring out how to generate that callback into the graphic queue)... hope this helps (and no hacking, please)
@Alfonso, I disagree. Clicking on the Ok button is the same thing as calling the callback function because when we click on it with a mouse button, it generates an interrupt request which causes it to be executed almost immediately https://en.wikipedia.org/wiki/Interrupt_request_(PC_architecture). This interruption tells the PC to stop everything that he is doing to handle our request, and unless some higher priority task is running, the mouse or keyboard will take the lead. I imagine that this was created like so to increase response time: so we don't have to wait for some process to finish to interact with our pcs. Rigorously, even calling a function does not guarantee it will be executed immediately, the same way that clicking on a button that calls it will not; however, the latter will probably have a higher priority. That's why we can use shortcut keys like Ctrl+Alt+Del or Command + Option + Esc to stop some slow running process (some function running for way too long). Please, check whenever possible the solutions for your problem, including the leading one. I will try once again to meet your requirements, but they are not the regular operation of a mouse click.
@Rafael, your PC might generate an interrupt when the mouse is clicked, but that will only tell MATLAB that the mouse was clicked, it is then up to MATLAB runtime to decide what to do with it. And Matlab decides to ignore it for now if some code (in this case the testsuite) is already running until a drawnow command is run, or until the code returns to the Matlab prompt, or until the code issues a waitfor command, etc. (see "help drawnow" for more details). That is NOT the same as if the running code itself (in this case the testsuite) calls the callback function, in which case Matlab runtime will decide to execute that function, not the "ignore it for now" behavior you can expect from a mouse click. If in doubt, try the following: "msgbox('Ok'); while 1, end" and then click on the 'Ok' button. Hope this helps clarify
@Alfonso, try doing instead 'x=msgbox('Ok'); while ishandle(x), disp('hello word!'); pause(2); end' and you will seen as soon as you click on the button the loop stops. Hope this helps. Your code does not work because as Matlab sees it, it is running the same script in the main thread, and there is no reason why the mouse button should take precedence over an infinite loop following the execution order. However, if we do use threads for instance it would be possible to close the dialog-box despite the existence of an infinite loop. My code works because I made it check the status of the dialog box since it is on the same thread.
PS: Funny thing, try to click on the X-button from your message box instead of the Ok-button. Some buttons have different priorities, but the mouse click is being executed either way.
@Alfonso And I've read the documentation of drawnow. It is only a refresh function used at several window managers. Since we are not doing animations, I don't see the point. Button's animation is way too small and fast to need a refresh. Even its other task of processing callbacks is doubtful since It may already have occured (and we cannot guaranteee it dind't).
A refresh function only tries to guarantee that after it, everything is already done (and it may or may not be blocking), but it does not guarantee that all callbacks weren't executed before (it may have no work to do).
@Rafael. Sorry I am not being very clear, let me start again. The objective of this problem is that players write some code that effectively "clicks on the 'Ok' button" of a message box. In other words, your clickOK() code should have exactly the same effect as if you magically sat on the same computer running the testsuite, saw that message box being created by the testsuite, and immediately used your mouse to click on the 'Ok' button all before your clickOK() function finishes. When you (magically) click on the 'Ok' button, the button callback function will NOT be run immediately. It will actually not be run at all until after the clickOK() function finishes executing entirely, and even then it will not be run just yet. The testsuite will continue running taking priority over your mouse click callback (just like in the infinite loop example) right until the point when the testsuite executes a "drawnow" command. At that point, if you or your function have in fact clicked on the Ok button, the drawnow command will realize that it has a callback pending to be executed and it will run it (see drawnow documentation), which, in turn, will cause the message box to be closed. After that the testsuite will continue running as normal, it will then check that the message box has been closed (note that it was not closed before the drawnow command), and you will "pass" this problem. That's all. Hope this helps
and btw, your 'x=msgbox('Ok'); while ishandle(x), disp('hello word!'); pause(2); end' code works (meaning that the loop ends when you click on the 'Ok' button) only because of the "pause" command within the loop. Remove that and it will not work at all. Why? you may ask. Well, pause, exactly like drawnow, will force any pending callbacks to be processed (again, this is detailed in "help drawnow"), so, without it, "ishandle" never notices whether you have clicked on the message box button (because the associated button callback function is not yet been executed), or whether you have even closed the entire message box (because the associated figure CloseRequestFcn callback has not yet been executed). Hope this helps clarify
@Alfonso,did you try clicking on the X or red button to close the dialog window from your example?
@Rafael. Assuming you are referring to the "x=msgbox('Ok'); while ishandle(x), disp('hello world'); end" code, then yes, the figure closes and the loop does NOT break, as one could expected (as the CloseRequestFcn callback, containing the "delete(gcbf)" code, is NOT executed so the loop continues forever). The behavior, of course, is different if you add back the "pause(1)" line within the loop. In that case the figure closes AND the loop ends (since, once the pause command is executed after the figure is closed, that will cause the CloseRequestFcn callback to be executed, which in turn will run the "delete(gcbf)" command, which in turn will make "x" a no-longer-valid graphic handle, which will cause the "ishandle(x)" condition to fail when it gets there). Are you observing the same behavior? (I am on R2020b, perhaps some of this is different in pre-R2014b versions?)
Solution Comments
Show commentsProblem Recent Solvers10
Suggested Problems
-
1395 Solvers
-
152 Solvers
-
Back to basics 2 - Function Path
182 Solvers
-
101 Solvers
-
694 Solvers
More from this Author38
Problem Tags
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!