MATLAB: Pushbutton callbacks to update and amend variables and cell arrays - matlab

I am new to making GUIs and this is my first attempt, but I have spent days searching for an answer to a really basic pushbutton question.There was a similar question on here but it doesn't work for me as I need to make multiple changes with a single click.
The GUI consists of a table which lists a number of images along with two blank columbs (the final GUI will have a 1000,50 table). The GUI displays the first image and then has 3 buttons,buttons 1 & 2 insert data into the table and then displays the next image,updating the Row variable within the code so you can move down the list of images, with each button press. Button 3 is different though, it takes the data from the current row and places it in a separate table for rejected data, and then once again updates the row variable. However, I can't get my callback functions to update the Row,Table and Rejected_Data variables. My code is as follows:
%Simple GUI for to show issue
%Table
Table=cell(4,3);
Table(1,1:end)=[{'File'},{'Type'},{'Value'}];
Table(2,1)={'Image1.jpg'};
Table(3,1)={'Image2.jpg'};
Table(4,1)={'Image3.jpg'};
% Rejected Images
Rejected_Data=cell(4,3);
Rejected_Data(1,1:end)=[{'File'} {'Type'} {'Value'}];
% GUI bit
Row=2;
Im=imread(Table{Row,1});
%Figure
hFig=figure;
set(hFig,'Units','Normalized','Position',[0.1 0.1 0.6 0.6]);
%Axes
hAx=axes('Parent',hFig);
set(hAx,'Units','Normalized','Position',[0.2 0.4 0.5 0.5]);
Image=imagesc(Im,'Parent',hAx);
axis off;
%button1
bh1=uicontrol('Style','pushbutton','String','Button1');
set(bh1,'Units','Normalized','Position',[0.2 0.2 0.1 0.05]);
%button2
bh2=uicontrol('Style','pushbutton','String','Button2');
set(bh2,'Units','Normalized','Position',[0.4 0.2 0.1 0.05]);
%button3
bh3=uicontrol('Style','pushbutton','String','Scrap');
set(bh3,'Units','Normalized','Position',[0.6 0.2 0.1 0.05]);
% Callbacks
set(bh1,'Callback',{#buttontest1,Table,Row,hAx});
set(bh2,'Callback',{#buttontest2,Table,Row,hAx});
set(bh3,'Callback',{#buttontest3,Table,Row,hAx,Rejected_Data});
Function for Buttons 1 & 2 is:
function buttontest1(~,~,Table,Row,hAx)
Table(Row,2)=cellstr('Tree');
Table(Row,3)=47;
Row1=Row+1;
evalin('base','Row = Row1');
evalin('base','Table = Table');
Pic=imread(Table{Row1,1});
imagesc(Pic,'Parent',hAx);
end
Function for Button 3:
function buttontest3(~,~,Table,Row,hAx,Rejected_Data)
Rejected_Data(Row,:)=Table(Row,:);
Row1=Row+1;
evalin('base','Row = Row1')
Pic=imread(Table{Row1,1});
imagesc(Pic,'Parent',hAx);
end
I tried using global instead of evalin, but as Row etc, already exists it doesn't work, I have also tried assignin, but I couldn't get that to work either. I don't want to use guide as I need the flexibility of only having a text file, I also don't use nested functions as they require my original GUI to be a function also and the final GUI will we bolted onto some pre-processing that will create the data table inputted.
Any help would be greatly appreciate.
Thanks

Use guidata to store your data. Using evalin for this purpose is not recommended.

Use handles structure to handle data transfer in gui.

Related

Update gramm plot matlab crashing

I have a MATLAB GUI that calls an external function to make a plot (make_ethogram_plot).
The idea would be to have an external figure that is constantly updated with the output value from the figure. Every time the data gets updated it should replot the values, it updates at ~10 Hz. I chose gramm (https://github.com/piermorel/gramm/tree/master/%40gramm) because it is really easy to make a raster plot.
This is the function that gets called. I am having issues to
1) Make it only update in the parent figure with specific name, instead of plotting in the GUI(which is the active figure).
2) Make it not crash. It would open many figures or open or close the same figure at 10 Hz until crashing.
In this configuration, it gives error because it doesn't find g after the first plot. Making g , f, and p1 globals makes it crash (opens every time it gets called)
function make_ethogram_plot(datastructure)
% if the figure doesn't exists create it
if(isempty(findobj(0, 'Name', 'My_gramm_ethogram')))
f=figure('Name', 'My_gramm_ethogram');
p1 = uipanel('Parent',f,'BackgroundColor',[1 1 1],'BorderType','none');
g = gramm('x', datastructure.final_data.frameID, 'color', categorical(datastructure.final_data.behavior));
g.geom_raster();
g.set_parent(p1);
g.draw()
else
% defining f,p1, g here (or having them global) works but crashes
% due to refresh rate
g.update()
end
end
I wrote this code to try to replicate your problem:
function animate_random_data
N = 10000;
data = [cumsum(rand(N,1)),randn(N,1)];
for ii=0:1000
% Plot the data
make_ethogram_plot(data);
drawnow
% Compute new data
data(:,1) = cumsum(rand(N,1));
data(:,2) = randn(N,1);
end
function make_ethogram_plot(data)
fig = findobj(0, 'Name', 'My_gramm_ethogram');
if(isempty(fig))
% If the figure doesn't exists create it
fig = figure('Name', 'My_gramm_ethogram');
ax = axes(fig);
plot(ax,data(:,1),data(:,2));
drawnow
set(ax,'xlimmode','manual','ylimmode','manual');
else
% If it does, update it
line = findobj(fig,'type','line');
set(line,'xdata',data(:,1));
set(line,'ydata',data(:,2));
end
Here, I followed your concept of looking for a named figure window, and creating one if it didn't exist. However, if it does exist, I simply replace the XData and YData property of the line that is already there. This is the fastest way of animating a graph, much faster than deleting the existing plot and creating a new one. After plotting, I use drawnow to update the display. I set XLimMode and YLimMode to manual to prevent re-computation of axes limits and consequent re-drawing of the axes.
The function took 17 seconds to draw all 1000 frames, meaning it's drawing about 60 frames a second. It does not (and should not) crash MATLAB.
You can limit the display rate to 20 frames/sec with drawnow limitrate. It will skip updating the display if the frames come too fast.
I don't know what the gramm/update method does, the class is too complicated to quickly see what is going on, but I dare presume it deletes the axes and creates a new plot from scratch. Not that this should crash MATLAB, it might be worth while to submit a bug report. However, you would probably want to update the figure in the more efficient way, following the method I demonstrated above.
Note that this method can be used to update any of the graphical elements in a plot. For example, I have used this method to animate images.

How to retrieve data from one gui to another gui in matlab?

I have two GUIs. In the first gui I want to plot an input signal (ex: sine signal). My problem is, how can I plot back the same signal in the second GUI after I clicked the 'load' pushbutton in the second GUI? Can somebody help me? I really need help.
Here is some code to get you going about using setappdata and getappdata. This is very basic and there are things which I did not mention (eg using the handles structure or passing variables as input arguments to functions) but using setappdata and getappdata is a safe way to go.
I created 2 programmatic GUIs. The code looks a bit different than when GUIs are designed with GUIDE, but the principle is exactly the same. Take the time to examine it and understand what everything does; that's not too complicated.
Each GUI is composed of one pushbutton and an axes. In the 1st GUI (sine_signal) the sine wave is created and displayed. Pressing the pushbutton opens the 2nd GUI (gui_filtering) and calls setappdata. Once you press the pushbutton of that 2nd GUI, setappdata is called to fetch the data and plot it.
Here is the code for both GUIs. You can save them as .m files and press "run" in the sine_signal function.
1) sine_signal
function sine_signal
clear
clc
%// Create figure and uicontrols. Those will be created with GUIDE in your
%// case.
hFig_sine = figure('Position',[500 500 400 400],'Name','sine_signal');
axes('Position',[.1 .1 .8 .8]);
uicontrol('Style','push','Position',[10 370 120 20],'String','Open gui_filtering','Callback',#(s,e) Opengui_filt);
%// Create values and plot them.
xvalues = 1:100;
yvalues = sin(xvalues).*cos(xvalues);
plot(xvalues,yvalues);
%// Put the x and y values together in a single array.
AllValues = [xvalues;yvalues];
%// Use setappdata to associate "AllValues" with the root directory (0).
%// This way the variable is available from anywhere. You could also
%// associate the data with the GUI itself, using "hFig_sine" instead of "0".
setappdata(0,'AllValues',AllValues);
%// Callback of the pushbutton. In this case it is simply used to open the
%// 2nd GUI.
function Opengui_filt
gui_filtering
end
end
And
2) gui_filtering
function gui_filtering
%// Same as 1st GUI.
figure('Position',[1000 1000 400 400],'Name','sine_signal')
axes('Position',[.1 .1 .8 .8])
%// Pushbutton to load data
uicontrol('Style','push','Position',[10 370 100 20],'String','Load/plot data','Callback',#(s,e) LoadData);
%// Callback of the pushbutton
function LoadData
%// Use "getappdata" to retrieve the variable "AllValues".
AllValues = getappdata(0,'AllValues');
%// Plot the data
plot(AllValues(1,:),AllValues(2,:))
end
end
To show you the expected output, here are 3 screenshots obtained when:
1) You run the 1st GUI (sine signal):
2) After pressing the pushbutton to open the 2nd GUI:
3) After pressing the pushbutton of the 2nd GUI to load/display the data:
That's about it. Hope that helps!

MATLAB: Permanently set "Text Update Function" for Data Cursor in figures

Problem: I use MATLAB for science, and I often need more than 4 significant digits. Every time I use Data Cursor in a figure's GUI, I need to manually right-click on the point, Select Text Update Function... or Edit Text Update Function..., and navigate to the folder where I saved the function whose callback prints more than 4 (e.g. 8) significant figures. This is annoying and there should be a way to automatically change this.
Ideal answer: I want this done permanently for all figures, e.g. in a function that changes default settings in my startup.m file.
Good enough answer: I want a wrapped function to which I give the figure handle and it fixes this for me.
I humbly await SO's infinite wisdom.
The permanent solution
would be, to edit the default_getDatatipText.m function.
You can find it in:
C:\...\MATLAB\R20xxx\toolbox\matlab\graphics\#graphics\#datacursor
There you will find the line:
DEFAULT_DIGITS = 4; % Display 4 digits of x,y position
Edit it as desired, you can't do much harm, but make a backup before if you want.
Alternative solution:
There is also the possibility of custom data tips: Tutorial at Matlab Central
It could finally look like this:
(additional text outside data-tips was post-processed)
And as you're talking about precision. The data-tip always snaps to the closest data-point. It doesn't show interpolated data at the clicked position.
The permanent answer given by thewaywewalk doesn't work anymore in R2015a, and probably later ones. So here I share my solution for both the temporary and permanent solution
Temporary solution (for a single figure):
The following function contains the update function as a nested function. Call datacursorextra to apply it to the current figure, or datacursorextra(fig) to apply it to some figure fig.
function datacursorextra(fig)
% Use current figure as default
if nargin<1
fig = gcf;
end
% Get the figure's datacursormode, and set the update function
h = datacursormode(fig);
set(h,'UpdateFcn',#myupdatefcn)
% The actual update function
function txt = myupdatefcn(~,event)
% Short-hand to write X, Y and if available Z, with 10 digit precision:
lbl = 'XYZ';
txt = arrayfun(#(s,g)sprintf('%s: %.10g',s,g), lbl(1:length(event.Position)), event.Position,'uniformoutput',false);
% If a DataIndex is available, show that also:
info = getCursorInfo(h);
if isfield(info,'DataIndex')
txt{end+1} = sprintf('Index: %d', info.DataIndex);
end
end
end
Permanent solution (apply to all figures by default):
I have not found a way to set a default UpdateFcn for the data cursor, but it is possible to add some code which will be called every time a new figure is created. Add the following line to your startup.m:
set(0,'defaultFigureCreateFcn',#(s,e)datacursorextra(s))
and make sure the datacursorextra function given above is available in your Matlab path.
#caspar's solution works very well.
You can also update the txt{} part of the solution with
if isfield(info,'DataIndex')
DataIndex = [info.DataIndex];
txt{end+1} = sprintf('Index: %d\n', DataIndex(1));
end
This will enable you to update the Index field when you have multiple pointers in the same figure.

MATLAB uitable row generation from user input

I've got a GUI in MATLAB which uses uitables for input. There are a fixed number columns, and each column has a very specific format which I have stored as a cell array, like so:
columnformat = {'text', 'numeric', {#doSomething, inputArg1}, {'Option1' 'Option2'}};
The number of rows is theoretically unlimited; the user could provide as many they like. The back-end is capable of handling arbitrarily many row inputs. Right now, I'm building a large uitable initially, and just assuming the user won't use it all.
Here's the question: I want to set up the table and associated code such that any time the user has selected the final row and presses enter, it creates a new row with the same format as the rest of the table.
I've tried many different approaches, including dynamically setting 'Data', and they all seem to break the custom formatting dictated by the cell array. I'm sure someone has done this before. Thanks for your help!
This solution works on GUI created using MATLAB GUIDE. I think it's true that MATLAB GUI shows odd behaviour, but I have seen most of the odd behaviour when debugging MATLAB callbacks using something like keyboard and not properly exiting from them using dbquit. So, my advice would be to stay away from using keyboard related commands for MATLAB GUIs created with GUIDE.
Now, back to solving your problem, follow these steps.
Step 1: Add this at the start of GUINAME__OpeningFcn:
handles.row_col_prev = [1 1];
Step 2: Click on the properties of the table in context and click on CellSelectionCallback. Thus, if the tag of the table is uitable1, it would create a function named - uitable1_CellSelectionCallback.
Assuming the figure of the GUI has the tag - addrows_figure
Add these in it:
%%// Detect the current key pressed
currentkey = get(handles.addrows_figure,'CurrentCharacter')
%%// Read in previous row-col combination
prev1 = handles.row_col_prev
%%// Read in current data. We need just the size of it though.
data1 = get(handles.uitable1,'Data');
%%// Main processing where a row is appended if return is pressed
if numel(prev1)~=0
if size(data1,1)==prev1(1) & currentkey==13 %%// currentkey==13 denotes carriage return in ascii
data1(end+1,:) = repmat({''},1,size(data1,2)); %%// Append empty row at the end
set(handles.uitable1,'Data',data1); %%// Save it back to GUI
end
end
%%// Save the current row-col combination for comparison in the next stage
%%// when selected cell changes because of pressing return
handles.row_col_prev = eventdata.Indices;
guidata(hObject, handles);
Hope this works out for you!
I couldn't think of a possibility to achieve what you want with a certain key, I think it would be possible with any key (KeyPressFcn). But I'd rather recommend to introduce a toolbar with a pushbutton:
h = figure(...
u = uitable(h, ...
set(u,'Tag','myTable')
tbar = uitoolbar(h);
uipushtool(tbar,'ClickedCallback',#addRow);
In your callback function then you need to get your data, add a row and write it back:
function addRow(~,~)
u = findobj(0,'Type','uitable','Tag','myTable');
data = get(u,'Data');
%// modify your data, add a row ...
set(src,'Data',data);
end
Sorry if everything is a little simple and untested, but a good answer would require a considerable effort, I don't have time for. The tag matlab-uitable can give you a lot of further ideas.

Drag pattern in uitable matlab

I want to know if it is possible to drag pattern values in matlab uitable. In a spreadsheet, to enter values from 1 to 50, you need to enter 1,2,3 and select the cells and drag. Please can this be done in matlab uitable? Regards.
It can be done. But not as far as comfortable as with excel.
Play around a bit with the following code, you can try to improve it or change it to your needs. I think it is a good starting point for you.
function fancyTable
defaultData = randi(99,25,2);
h = figure('Position',[300 100 402 455],'numbertitle','off','MenuBar','none');
uitable(h,'Units','normalized','Position',[0 0 1 1],...
'Data', defaultData,...
'Tag','myTable',...
'ColumnName', [],'RowName',[],...
'ColumnWidth', {200 200},...
'CellSelectionCallback',#cellSelect);
end
function cellSelect(src,evt)
try
index = evt.Indices;
data = get(src,'Data');
L = size(index,1);
rows = index(:,1);
column = index(1,2);
start = data(rows(1),column);
newdata = start:(start+L-1);
data(rows,column) = newdata';
set(src,'Data',data);
end
end
It creates a table with two columns:
You can select data and your desired drag pattern is applied immediately according to the first value.
The code is just to insert an increasing series of values at the first point of selection based on the according value. The hardest part will be to detect the pattern! I just evaluated the first data value start = data(rows(1),column); you could also require a minimal selection of 3: start = data(rows(1:3),column);. You probably need to work with a lot of try/catch structures to skip all unexplained cases. Or you use switch/case structures from the beginning to evaluate the length of the selection and evaluate the pattern.
All in all it is a heavy task, I'm not sure if it's worth it. But it can be done.
In uitable you insert data (usually a matrix) to be displayed in a table. So unlike Excel the uitable function is merely a form of displaying your data instead of a tool to manipulate it. See for more information here. However, if you want to set up a row for instance running from 1 until 10 you could use the following steps:
So say one would like to display a matrix of size 10x10, e.g.
A=magic(10);
You can now set up a table t to display this matrix by
t=uitable('Data',A);
In your case if you want a row to be, e.g., 1 till 10, just change the matrix A containing your data to hold this row using
A(1,1:10)=1:10;
And re-execute the former command to bring up your table t.