tags:

views:

3380

answers:

3

Is there a possibility to write a GUI from inside a function?

The Problem is: The callback of all GUI-functions work in the global workspace. But functions have their own workspace and can not access variables in the global workspace. Is there a possibility to make the GUI-functions use the workspace of the function?

function myvar = myfunc()
    myvar = true;
    h_fig = figure;

    % create a useless button
    uicontrol( h_fig, 'style', 'pushbutton', ...
                      'string', 'clickme', ...
                      'callback', 'myvar = false' );

    % wait for the button to be pressed
    while myvar
        pause( 0.2 );
    end

    close( h_fig );

    disp( 'this will never be displayed' );
end

This event-loop will run indefinitely, since the callback will not modify myvar in the function. Instead it will create a new myvar in the global workspace.

+1  A: 

You can declare a variable global in your function and global in the GUI code, certainly if the callback is in a separate function rather than inline. I've done this in a little skeleton GUI I use to make quick menu system.

In your code above you may be able to add the global keyword to your initial declaration and also to your inline callback i.e. 'global myvar = false'

Ian Hopkinson
OP will need to change the output variable to something else and create "global myvar" in the base workspace for this to work.
Azim
Is this really the only way? It seems to be kind of blunt to use global variales for the job.
BastiBechtold
It's the best way I've come up with - I agree it's a bit ugly, I don't think the Matlab GUI model is very good. You might want to check the code in the uitable(?) contribution on Mathworks. Site is down at the minute.
Ian Hopkinson
+1  A: 

I found a solution to the problem. The callback-function has to modify the handle-structure of the GUI. This structure can be accessed both from within the callback and from the function without introducing new variables to the global workspace:

function myfunc()
    h_fig = figure;

    % add continue_loop to the GUI-handles structure
    fig_handles = guihandles( h_fig );
    fig_handles.continue_loop = true;
    guidata( h_fig, fig_handles );

    % create a useless button
    uicontrol( h_fig, 'style', 'pushbutton', ...
                      'string', 'clickme', ...
                      'callback', @gui_callback );

    % wait for the button to be pressed
    while fig_handles.continue_loop
     fig_handles = guidata( h_fig ); % update handles
        pause( 0.2 );
    end

    close( h_fig );
    disp( 'callback ran successfully' );
end

% The arguments are the Matlab-defaults for GUI-callbacks.
function gui_callback( hObject, eventdata, handles )
    % modify and save handles-Structure
    handles.continue_loop = false;
    guidata( hObject, handles );
end

note that since the while-loop will only update fig_handles when it is run, you will always have at least 0.2 seconds delay until the loop catches the modification of fig_handles.continue_loop

BastiBechtold
The answer you gave here certainly looks correct, and this is how many people probably handle this sort of problem. It's all about personal preference: some people like to use GUIDE, whereas I always felt I could make things look cleaner with nested functions (it's a little more work, though).
gnovice
I second that. In fact I suffered so much from the GUIDE-generated code, that I wrote fig2cmd - to extract from a .fig file the commands necessary to reproduce it in an m-file.
Ofek Shilon
+1  A: 

I think a better way to do things is to use nested functions for the callbacks. Here's a simple example of how to use nesting to handle your callbacks:

function make_useless_button()

  % Initialize variables and graphics:

  iCounter = 0;
  hFigure = figure;
  hButton = uicontrol('Style','pushbutton','Parent',hFigure,...
                      'String','Blah','Callback',@increment);

  function increment(source,event)
    iCounter = iCounter+1;
    disp(iCounter);
  end

end

When you run this code, the counter displayed should increment by one every time you press the button, because the nested function "increment" has access to the workspace of "make_useless_button".

For your particular problem, you would add your loop and change the callback so it sets your flag variable to false (this should work for your example):

function make_stop_button()

  % Initialize variables and graphics:

  keepLooping = true;
  hFigure = figure;
  hButton = uicontrol('Style','pushbutton','Parent',hFigure,...
                      'String','Stop','Callback',@stop_fcn);

  % Keep looping until button is pressed:

  while keepLooping,
    drawnow;
  end

  % Delete figure:

  delete(hFigure);

  % Nested callback:

  function stop_fcn(source,event)
    keepLooping = false;
  end

end
gnovice