I have an external function say "external_func" (seperate .m file)
Inside this function a while loop is called, and this while loop update a variabl named "update_prog"
Now I will pass this value into the GUIDE using
assignin('base', 'update_prog', update_prog); % passing to workspace
I am making this
"update_prog" as global variable and calling it into GUIDE .m file
function pb1_Callback(hObject, eventdata, handles)
global update_prog
% hObject handle to pb1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% else
% set(handles.pb1,'enable','on');
% end
% update_prog first value prints, but it wont updates as the loop in external_func goes on.
drawnow;
set(handles.slider1,'Value',update_prog)
external_func;
so here in the GUIDE .m file I can get the value
"update_prog" but it wont keep up with the while loop. I used "drawnow" but its of no use.
How can I refine this value "update_prog" as the while loop in the "external_func" goes through multiple iterations. [Note: The updated values are there in the callback function of GUIDE, but unless there is a callback the callback function wont update the "update_prog"], so how can I acheive this real-time update inside a call_back function.
[Note: passing the variables through function input is not possible here in my case, so I am looking for alternatives]
Edit1: please consider this link, which has an exampleWhich may clarify you what I am trying to acheive
What I am doing here is
Passing the variable(which is being updated in the while loop of the externel function) into GUI.
I will use this variable to show the progress on the progress bar(Slider).
What is the problem?
1. The variable inside the GUI callback(Consdier I will press a push button and then it will call the function with while loop) will put the updated values into the set(handles.slider,'Value',variable)
By doing this I cant move the slider.
Why?
Callback updates the variable only when I press the push button, next all the updates to the variable will not be updated, so progress bar/slider wont move.
I wouldn't recommend to pass your variable in 3 steps with an intermediate workspace (external->base workspace->GUI). I would rather recommend to pass your variable directly (external->GUI).
Each Matlab figure offers a space to store variables (Application Data) of any type. I would suggest reading the article Share Data Among Callbacks and read the documentation for the 3 functions:
guidata
setappdata
getappdata
This way will offer you much more control over the scope of your variables and you won't need any global declaration.
Below is an example of a simple gui. The gui declare the variable in it's user space (with setappdata), then uses a timer to periodically read this variable (with getappdata).
The external function do whatever you want it to do (just a random number in the example), then to update the variable you use the same setappdata. The only thing you need for that is the handle of the main GUI figure, so in the example I give it as input of the external function.
The GUI also has two buttons to start and stop the update.
The code for the main example GUI 'theGui.m' is :
function h = theGui
%// basic GUI with 2 buttons and 1 slider
h.fig = figure('Position',[433 434 500 100],'Menubar','none','CloseRequestFcn',#my_closefcn) ;
h.sld = uicontrol('Style','Slider','Position',[20 20 460 20]) ;
h.btnStart = uicontrol('Style','pushbutton','String','Start updating','Callback',#btnStart_callback,'Position',[20 50 200 30]);
h.btnStop = uicontrol('Style','pushbutton','String','Stop updating','Callback',#btnStop_callback,'Position',[280 50 200 30],'Max',1,'Min',0);
%// Define the timer
h.t = timer ;
h.t.Period = 0.1 ; %// 0.1s refresh interval
h.t.TimerFcn = {#timer_callback,h.fig} ;
h.t.ExecutionMode = 'fixedSpacing' ;
%// initialise the variable to update in the GUI appdata
update_prog = 0 ;
setappdata( h.fig , 'update_prog' , update_prog ) ;
%// save handles
guidata( h.fig , h );
function btnStart_callback(hobj,~)
h = guidata( hobj ) ; %// retrieve handles
if strcmp('off',h.t.Running) %// Start timer (only if not already running)
start(h.t)
end
function btnStop_callback(hobj,~)
h = guidata( hobj ) ; %// retrieve handles
stop(h.t) %// Stop timer
function timer_callback(~,~,hfig)
update_prog = getappdata( hfig , 'update_prog' ) ; %// retrieve the 'update_prog' variable value
h = guidata( hfig ) ; %// retrieve handles
set(h.sld , 'Value' , update_prog) ; %// update the slider object with the retrieved value
function my_closefcn(hobj,~)
%// this function is only to clean up when the GUI will be closed.
%// It is recommended to delete the timer manually
h = guidata( hobj ) ; %// retrieve handles
stop(h.t) %// Stop timer (in case it is still running)
delete(h.t) ; %// delete the timer
delete(h.fig) ; %// destroy the figure
And the code for external_func.m
function external_func( guiMainFigureHandle )
%// This function will only generate random numbers and push them into the
%// variable 'update_prog' contained in the GUI appdata.
%// This is why this function NEEDS the handle of the gui to be able to
%// access the Application Data space of the gui.
for k = 1:100
randomValue = rand(1) ; %// generate a random value
hfig = ancestor( guiMainFigureHandle , 'figure' ) ; %// make sure the handle provided is the top level figure
setappdata( hfig , 'update_prog' , randomValue) ; %// update the variable value
pause(0.1) ;
end
Edit:
I place this in edit instead of changing the code above because I don't recommend messing with the root object if you don't need to. But in your case it can be a way round your problem.
If your external function doesn't have access to the GUI, it can always update a part of memory which is available for all the programs running in a given Matlab session, namely the root object. The handle for it is reserved and is the same for any program: 0 (although since v2014b there is another way to invoke it : groot, it is still always the same handle for all Matlab).
So in the example above, in theGui.m, use instead:
setappdata( 0 , 'update_prog' , update_prog ) ;
in the main routine, then in the subfunction function timer_callback(~,~,hfig), use:
update_prog = getappdata( 0 , 'update_prog' ) ; %// retrieve the 'update_prog' variable
And your function external_func() doesn't need any extra argument, the update only needs one line:
setappdata( 0 , 'update_prog' , update_prog) ; %// update the variable value
I "suspect" that your update_prog variable in the base workspace is not a global (you must define it to be global in every workspace that you want to use it).
Since your using globals (there are many better ways to do this - but thats not your question) - why don't you simply define the update_prog variable to be global in your external_func function (replace the assign call).
edit put a drawnow in your external_func function. That way when you click on the button it will update.
edit 3
I think I know what you want to do, try this example and see if it does what you want - updated to show how you find the slider object in your code and update inside your loop:
function mygui
% create a figure
f = figure;
% create a uicontrol slider - note that I give it a tag.
uicontrol ( 'style', 'slider', 'Position', [0 200 200 40], 'tag', 'MYSLIDER', 'backgroundcolor', 'white', 'parent', f );
% create a push button which we can press to update
uicontrol ( 'string', 'Press 2 start', 'callback', #(a,b)myLoop(), 'Position', [0 0 200 50] )
end
% This replicates your "external_func" function.
function myLoop()
% since you cant pass in any var -> you need to find the slider object
% you can do this by using findobj and search for the tag of the item
uic = findobj ( 0, 'tag', 'MYSLIDER' );
% find the figure handle (only needed for this demo)
f = ancestor ( uic, 'figure' );
% initialise the value which will be used to update the slider
count = 0;
% create your loop
while ishandle(f)
% incrememt the count variable -> this will end up on the slider value
count = count + 1e-5;
% reset count if > 1 -> so the slider cant go out of range.
if count >= 1
count = 0;
end
set ( uic, 'Value', count );
% initiate a drawnow -> this allows matlab to process GUI events
drawnow();
end
end
The downside of this is you insert a drawnow in your loop -> which could slow it down somewhat.
If this doesn't fix your problem you need to explain better what you want to do... (in my view)
I'm working on a continuously updating slider...
hsl = uicontrol(...); % Slider initializing stuff
vars=struct('hsl', hsl, 'x', x, 'y', y); % A bunch of stuff that my callback needs
set(hsl,'Callback',{#hsl_callback,vars});
addlistener(hsl,'ContinuousValueChange',#hsl_callback);
Here's the problem. If I leave it like that, I get the error "not enough input arguments" for the callback.
If I change the line to this:
addlistener(hsl,'ContinuousValueChange',#(vars)hsl_callback);
then I get the error "too many input arguments."
Is this not possible, or am I getting the syntax wrong?
If it helps, my callback function has this structure:
function hsl_callback(~,~,vars)
k = get(vars.hsl,'Value');
% plot x, y scaled by k
end
First, I would avoid using vars to store your data. The slider handle can be passed directly in your callback, and x and y may be stored in the UserData property. This gives you the ability to change x and y dynamically, if needed. Change your callback method declaration to:
function hsl_callback(hObject,eventdata)
% Retrieve k and vars
k = get ( hObject , 'Value' );
vars = get ( hObject , 'UserData' );
% Plot x,y scaled by k
...
end
Then, I would change slider creation to:
% Define x and y.
vars = struct('x',x,'y',y);
% Create slider, assigning vars to UserData.
hSlider = uicontrol('Sytle','Slider',...,'UserData',vars);
% Assign the callback method, executed whenever the slider is released.
set(hSlider,'Callback',#hsl_callback);
% Assign the listener, executing whenever the slider value changes.
hListener = addlistener(hSlider,'ContinuousValueChange',#(src,eventdata)hsl_callback);
Depending on your version, you may have to use this instead:
hListener = addlistener ( hSlider , 'Value' , 'PostSet' , #(src,eventdata)hsl_callback );
For an user interface I'm programming an uitable. The user chooses an option A,B or C in the first column and the suboption in the second column depends on what was chosen in the first, either A.1,A.2 or A.3 or B.1,B.2 or B.3 or the same for C
The code for the table can be found in Appendix A.
When the user first defines the main option, then automatically the suboptions are reduced accordingly to only valid choices. This is realized by evalulating the CellEditCallback for column 1 and resetting the ColumnFormat for column 2. (function modifySelection in Appendix B)
If the user now realizes he made a mistake and needs to edit a suboption another time, then the ColumnFormat is still set according to the previous edited main option and the valid choices are not available unless he re-chooes the main option another time. (see the blue highlighting in picture).
To resolve this, I also implemented the CellSelectionCallback calling the function justifySelection (in Appendix B), which is checking by selection, which option was chosen in column 1 to offer again the right suboptions for column 2. But as this callback reacts on selection, I need to select twice, one time to trigger the CellSelectionCallback and another to actually get my choices. For large tables, this can be very annoying!
So my question is:
Is there a way to prevent the popup menu in column 2 from popping up, until it found out what's the content of the according column 1, so it immediately offers the valid choices?
Or:
How could I detect a mouse click on a cell and get the row and column-index? But without invoking the following selection and popping up action?
I was already raking all available properties but didn't found anything which could be useful.
Maybe one could do something using the ButtonDownFcn, but how to get the cell indices? What about the BusyAction property, how can that be used for my purpose?
Any ideas?
I'm sorry in advance to bomb you with so much code, it's already the most minimal example, but fully executable, so you can try it out.
Appendix A/B
function fancyUitable
selector_1 = { 'A'; 'B' ; 'C' };
selector_2 = { 'first select first row!' };
h = figure('Position',[200 100 268 120],'numbertitle','off','MenuBar','none');
defaultData = repmat( {'select main option...', 'select suboption...'} ,5,1);
columnname = {'Option ',...
'Suboption '};
columnformat = { {selector_1{:}}, selector_2 };
columneditable = [true true];
t = uitable(h,'Units','normalized','Position',[0 0 1 1],...
'Data', defaultData,...
'ColumnName', columnname,...
'ColumnEditable', columneditable,...
'ColumnFormat', columnformat,...
'RowName',[],...
'CellEditCallback',#modifySelection,...
'CellSelectionCallback',#justifySelection);
set(h,'Tag','Config_figure')
set(t,'Tag','Config_table')
end
% **Appendix B**
% (part of the same function file)
function modifySelection(~,evt_edit)
if evt_edit.Indices(2) == 1
modifyPopup( evt_edit.Indices(1) );
end
end
function justifySelection(~,evt_select)
try %to surpress an unimportant error
if evt_select.Indices(2) == 2
modifyPopup( evt_select.Indices(1) );
end
end
end
and finally the single function modifyPopup which rewrites the Columnformat:
function modifyPopup( row )
id_group_1 = {'A.1';'A.2';'A.3'};
id_group_2 = {'B.1';'B.2';'B.3'};
id_group_3 = {'C.1';'C.2';'C.3'};
id_default = {'select main option first'};
myfigure = findobj('Tag','Config_figure');
config_data = get(findobj(myfigure,'Tag','Config_table'),'Data');
selector = config_data(row,1);
selector = selector{1};
config_format = get(findobj(myfigure,'Tag','Config_table'),'ColumnFormat');
switch selector
case 'A'
config_format{2} = id_group_1';
case 'B'
config_format{2} = id_group_2';
case 'C'
config_format{2} = id_group_3';
otherwise
config_format{2} = id_default;
end
set(findobj(myfigure,'Tag','Config_table'),'ColumnFormat',config_format)
end
Bounty: Why just +50? - I guess it's either not possible or the answer is easy, once one had the right initial idea. I'm not looking a for a complex workaround using java object properties etc. Thank you in advance!
I include the discussion from the comments here to keep the overview:
If you want to try it out, you can copy the code and follow these steps to reproduce the undesired behaviour:
select main option A in the first row.
the suboption in the first row then contains the choices A.1, A.2 and
A.3.
select main option B in the second row, therefore the choices for
the suboption in the second row are B.1, B.2 and B.3
BUT NOW you want to change the suboption in the first row (directly); you would expect to get the choices A.1, A.2 and A.3; but you don't. You get offered B.1, B.2 & B.3; - Because the last main option you selected was B (though in a diffrent row).
It seems that instead of looking for the last option, you should look at the relevant option. So either make sure that clicking on a
suboption does a 'lookup' to see which main option there is,
Thats exactly what I'm looking for! But how could I do that? How to
detect the click, get the column&row indices, set the right
ColumnFormat and then finally let the cell pop up. The only
possibility I see until now is the CellSelectionCallback, but it is
executed after the cell already popped up with the invalid choices.
I'd need a kind of ClickedCallback, like there is for pushbuttons
or make sure that selecting a main option only sets the suboptions for that row.
That's not possible, you can't set a suboption for a certain row as you need to modify ColumnFormat, which affects the whole table and not just one row.
I would not use a uitable; it's just not suited for this sort of thing.
Here's how I would do it:
function GUIdemo
%%// Construct GUI
%// Main figure
mainFig = figure;
set(mainFig, 'Color', get(0, 'DefaultUicontrolBackgroundColor'));
%// Create as many blocks as needed. The only thing you have to do is
%// figure out the "right" positions for each block
popupHandles = create_ui_blocks([
0.00 0.50 1.00 0.35
0.00 0.15 1.00 0.35]);
%// This OK button gathers all selected options, and just prints them.
uicontrol(...
'style' , 'pushbutton',...
'units' , 'normalized',...
'parent' , mainFig,...
'position', [0.4 0.01 0.2 0.1],...
'callback', #(~,~)getData(popupHandles),...
'string' , 'OK'...
);
%%// Helper functions
%// Create control blocks. Each block is composed of:
%// - a uipanel as container
%// - three radio buttons for the main selection
%// - a corresponding popup or the secondary selection
function popupHandles = create_ui_blocks(positions)
%// initialize
numBlocks = size(positions,1);
panels = zeros(numBlocks,1);
groups = zeros(numBlocks,1);
radios = zeros(numBlocks,3);
popups = zeros(numBlocks,1);
%// Build each block
for ii = 1:numBlocks
%// The container
panels(ii) = uipanel(...
'parent' , mainFig,...
'position', positions(ii,:)...
);
%// The radio buttons
groups(ii) = uibuttongroup(...
'parent' , panels(ii),...
'position', [0.05 0.05 0.45 0.9]...
);
radios(ii,1) = uicontrol(...
'style' , 'radio',...
'units' , 'normalized',...
'string' , 'A',...
'parent' , groups(ii),...
'position', [0.05 0.66 0.9 0.25]...
);
radios(ii,2) = uicontrol(...
'style' , 'radio',...
'units' , 'normalized',...
'string' , 'B',...
'parent' , groups(ii),...
'position', [0.05 0.33 0.9 0.25]...
);
radios(ii,3) = uicontrol(...
'style' , 'radio',...
'units' , 'normalized',...
'string' , 'C',...
'parent' , groups(ii),...
'position', [0.05 0.0 0.9 0.25]...
);
%// Initially, nothing's selected
set(groups(ii), 'SelectedObject',[]);
%// The popups
popups(ii) = uicontrol(...
'style' , 'popup',...
'units' , 'normalized',...
'parent' , panels(ii),...
'position', [0.55 0.4 0.4 0.2],...
'string' , 'Select main option',...
'enable' , 'off'...
);
%// On changing radiobutton, correct the string list of the popups
set(groups(ii),'SelectionChangeFcn', #(~,~)selectionChangeCallback(ii));
%// This is needed by the OK button callback
popupHandles = popups;
end
%// What happens when clicking a radio button?
%// NOTE: this is a doubly-nested function
function selectionChangeCallback(num)
switch get(groups(num), 'SelectedObject')
case radios(num,1)
set(popups(num), 'string', {'A.1', 'A.2', 'A.3'}, 'enable', 'on');
case radios(num,2)
set(popups(num), 'string', {'B.1', 'B.2', 'B.3'}, 'enable', 'on');
case radios(num,3)
set(popups(num), 'string', {'C.1', 'C.2', 'C.3'}, 'enable', 'on');
otherwise
%// ...
end
end
end
%// What happens when pressing the OK button?
function data = getData(popupHandles)
data = char(cellfun(#(x,y)x{y}, ...
get(popupHandles, 'String'),...
get(popupHandles, 'Value'),...
'UniformOutput', false)) %#ok<NOPRT> //
end
end
Output in the MATLAB command window when pressing "OK":
data =
A.1
B.1
The layout is of course still crude, but you get the idea. Of course, the radio buttons can also be replaced by a popup (more compact), three pushbuttons, or whatever else you like.
The contents of the popups are not related to each other, which is exactly the problem with the uitable approach. In this GUI, changes in the popup's contents can be instantaneous when changing a radio button, simply because you have better control over how to deal with changes.
A programming note: I personally don't like it when handles of individual components in what I treat as a "block" are floating around in the top-level function, which is why I use doubly-nested functions -- it's kind of like encapsulation. Now, when used outside of classes, this is not everyone's cup of tea, so you might want to convert them. Of course, all nested functions are trivially converted to subfunctions; you just have to manually pass a lot more information around.
With this approach, you lose some functionality (the ability to re-size your UI elements), but you gain intuitive behavior of the GUI controls. When these are the choices, I've been trained to develop towards the latter option. The nice bells and whistles will only impress the end-user the first few times round, but the program's basic functionality will become more and more important with increased usage. As you noted yourself, this buggy behavior gets annoying when you have to use the tool a lot; I'd say, drop the resizability in favor of improved control behavior.
Though I highly appreciate the effort and the solution of Rody Oldenhuis and he definetely deserved the award, his solution would have required a lot of changes in my code, so I kept trying to find a simpler solution. Here it is, finally 99% bug-free.
(all code parts within on function script)
function fancyUitable
close all
%basic properties
line_height = 21.32;
table_height = 6*line_height;
lh = line_height/table_height;
cw = 200; %columnwidth
h = figure('Position',[200 100 2*cw+2 table_height],...
'numbertitle','off','MenuBar','none');
%header
uitable(h,'Units','normalized','Position',[0 1-lh 1 lh],...
'ColumnWidth', {cw cw},...
'ColumnName', {'Option','Suboption'},...
'RowName',[]);
%button (currently no icon) to store table
tbar = uitoolbar(h);
uipushtool(tbar,'ClickedCallback',#store);
% addrow(figurehandle,number of row, percentage lineheight)
% every function call creates a new row, later dynamically
addRow(h,1,lh);
addRow(h,2,lh);
addRow(h,3,lh);
addRow(h,4,lh);
addRow(h,5,lh);
end
function edit(src,evt)
if evt.Indices(2) == 1
modifyPopup( src,evt.Indices(1) );
end
% disables cell selection highlighting, when one jumps to next table,
% a bit laggy though
fh = get(src,'parent');
copyobj(src,fh);
delete(src);
end
function modifyPopup( src,row )
id_group_1 = {'A.1';'A.2';'A.3'};
id_group_2 = {'B.1';'B.2';'B.3'};
id_group_3 = {'C.1';'C.2';'C.3'};
id_default = {'select output file first'};
config_data = get(src,'Data');
selector = config_data(row,1);
selector = selector{1};
config_format = get(src,'ColumnFormat');
switch selector
case 'A'
config_format{2} = id_group_1';
case 'B'
config_format{2} = id_group_2';
case 'C'
config_format{2} = id_group_3';
otherwise
config_format{2} = id_default;
end
config_data = { selector , 'select suboption...' }; %reset column 2
set(src,'Data',config_data);
set(src,'ColumnFormat',config_format);
end
function addRow(fh,k,lhp)
selector_1 = { 'A'; 'B' ; 'C' };
selector_2 = { 'first select first row!' };
defaultData = {'select main option...', 'select suboption...'};
columnformat = { {selector_1{:}}, selector_2};
columneditable = [true true];
th = uitable(fh,'Units','normalized','Position',[0 1-(k+1)*lhp 1 lhp],...
'Data', defaultData,...
'ColumnName', [],...
'ColumnWidth', {200 200},...
'ColumnEditable', columneditable,...
'ColumnFormat', columnformat,...
'RowName',[],...
'Tag','value',...
'UserData',k,...
'SelectionHighlight','off',...
'CellEditCallback',#edit);
end
function store(~,~)
ui = findobj(0,'Type','uitable','Tag','value');
L = numel(ui);
output = cell(L,2);
order = zeros(L,1);
for ii=1:L;
output(ii,:) = get(ui(ii),'Data');
order(ii) = get(ui(ii),'UserData');
end
[~,idx] = sort(order); %as order of handles unequals displayed order
assignin('base','output',output(idx,:));
end
brings up:
The solution is to use the Cell Selection Callback with two UITables in your GUI. Make the first table hold the data of {'a','b','c'} then in the cell selection callback, make the second UITable visible and set its data based on the cell selection properties of the first UITable. This link should have everything you need if you look down at the line 'A little hack not needing findjobj'
http://www.mathworks.com/matlabcentral/newsreader/view_thread/306392