Issue in Refresh CData of HGimage - matlab

Initially I am create a figure in GUI with two button (btnStart , btnNext ) and one axes (P_axes). For above two buttons I am using callback function.
In first button callback Function
function btnStart_callback(hObject,eventdata ,handles)
load MRI;
d = D(:,:,1);
handles.Img = imshow(d,'Parent' , P_axes);
setappdata(handles.figure1 , 'Indx' , 1)
setappdata(handles.figure1 , 'Data' , D)
end
In second button callback Function
function btnNext_callback(hObject,eventdata ,handles)
indx = getappdata(handles.figure1 , 'Indx');
D= getappdata(handles.figure1 , 'Data');
d = D(:,:,indx+1);
set(handles.Img , 'CData',d);
setappdata(handles.figure1 , 'Indx' , indx+1);
end
In the second callback function i got one in the line set(handles.Img , 'CData',d);
error is "Invalid or deleted object. "
why this error is occur and how to solve it ?

As mentioned by Rattus Ex Machina that's hard to debug without seeing the rest of your code. If that could be of any help, here is a simple GUI which does what you seem to be after. Take the time to play around with it to see what might have caused the error in your code. I suspect that's a basic issue but I think it comes form elsewhere in the code than the part you have shown.
function LoadMRIGUI
clc
clear all
handles.figure1 = figure('Position',[100 100 400 400],'Units','normalized');
P_axes = axes('Units','normalized','Position',[.2 .2 .6 .6]);
handles.ButtonStart= uicontrol('Style','push','String','Start','Position',[40 350 50 30],'Callback',#(s,e) btnStart_callback);
handles.ButtonStop= uicontrol('Style','push','String','Next','Position',[100 350 50 30],'Callback',#(s,e) btnNext_callback);
%// === NEW === \\%
%// text box to see current index
handles.IdxTitle = uicontrol('Style','text','String','Index','Position',[160 350 50 20]);
handles.Idxbox = uicontrol('Style','text','String','1','Position',[220 350 50 20]);
function btnStart_callback
%// === NEW === \\%
S = load('mri');
d = S.D(:,:,1);
handles.Img = imshow(d,'Parent' , P_axes);
setappdata(handles.figure1 , 'Indx' , 1)
setappdata(handles.figure1 , 'Data' , S.D)
end
function btnNext_callback
indx = getappdata(handles.figure1 , 'Indx');
D= getappdata(handles.figure1 , 'Data');
d = D(:,:,indx+1);
set(handles.Img , 'CData',d);
setappdata(handles.figure1 , 'Indx' , indx+1);
set(handles.Idxbox,'String',num2str(indx+1));
end
end
Sample screenshot:
Hope that helps!

Without seeing the context, it's tough to be absolutely sure what you're up to. Importantly, are these functions defined in the same, or different, files? There does seem to be an obvious problem which could cause the error you are seeing:
function btnStart_callback(hObject,eventdata ,handles)
load MRI;
d = D(:,:,1);
handles.Img = imshow(d,'Parent' , P_axes);
setappdata(handles.figure1 , 'Indx' , 1)
setappdata(handles.figure1 , 'Data' , D)
end
In the above, handles is passed in, modified, and then discarded when the function ends. If the functions are defined in different files, that value you store in .Img will never be seen again, which is why your second callback is throwing an error when you try to use it.
You are using the appdata approach for sharing data between the two functions. This will certainly work, but if you are using that approach you need also to share handles.Img.
An alternative approach, that I would favour, would be to place both of these callbacks as nested functions inside the main file representing your "application" (which creates the GUI, etc.). That way, they can share data at the file scope level (variables defined in the root function are visible in nested functions) and you don't need all the calls to appdata functions.
Your application would take this form:
function myapp
% define a variable here
my_handle = [];
function callback1(h, e)
% and it is visible here
my_handle = gcf;
end
function callback2(h, e)
% and also here
set(my_handle, 'monkeys', 'maximum');
end
end

Related

Create an array that grows at a regular interval on Matlab

I have been thinking about how to create an array that grows at a regular interval of time (for instance every 5 seconds) on Matlab.
I figured out 2 ways, either using tic/ toc or timer function. Later this program will be complexified. I am not sure which way is the best but so far I am trying with using timer.
Here is what I have tried :
clc;
period=5;%period at which the file should be updated
freq=4;
l=freq*period;
time=[0];
a = timer('ExecutionMode','fixedRate','Period',period,'TimerFcn',{#time_append, time,l,freq},'TasksToExecute',3 );
start(a);
function [time]=time_append(obj,event,time,l,freq)
time_append=zeros(l,1);
last_time=time(end)
for i=1:1:l
time_append(i)=last_time+i/freq;
end
time=[time;time_append];
end
After compiling this code, I only get a time array of length 1 containing the value 0 wheras it should contain values from 0 to 3x5 =15 I think it is a stupid mistake but I can't see why. I have tried the debug mode and it seems that at the end of the line time=[time;time_append], the concatenation works but the time array is reinitialised when we go out of the function. Also I have read that callback function can't have output. Does someone would know how I could proceed? Using globals? Any other suggestion?
Thank you for reading
You can do this by using nested functions. Nested functions allow you to access "uplevel variables", and you can modify those. Here's one way to do it:
function [a, fcn] = buildTimer()
period=5;%period at which the file should be updated
freq=4;
l=freq*period;
time=0;
function time_append(~,~,l,freq)
time_append=zeros(l,1);
last_time=time(end);
for i=1:1:l
time_append(i)=last_time+i/freq;
end
time=[time;time_append];
end
function out = time_get()
out = time;
end
fcn = #time_get;
a = timer('ExecutionMode','fixedRate',...
'Period',period,...
'TimerFcn',{#time_append,l,freq},...
'TasksToExecute',3 );
start(a);
end
Note that the variable time is shared by time_append and time_get. The timer object invokes time_append, and updates time. You need to hand out the function handle time_get to retrieve the current value of time.
>> [a,fcn] = buildTimer; size(fcn()), pause(10); size(fcn())
ans =
21 1
ans =
61 1

Function 'subsindex' is not defined for values of class 'struct'

I am using the matlab to solve a simple model in economics. But I came across an error
Function 'subsindex' is not defined for values of class 'struct'
when I run the last line of code.
omega=mkt_share(Par,w,Grid);
It appears wired to me as I call this function repeatedly in the code before reaching the last line, it works fine. Could anyone inform me how to solve the problem? Thanks!
I post the full code of my program as following
clear all
Par.theta = 1.5;
Par.gamma = 6;
Par.beta = 0.1;
Par.zeta = 15;
Par.n = 100;
Grid.q = sort( gprnd(1/Par.zeta,1/Par.zeta,1,Par.n,1));
Grid.q = Grid.q./Grid.q(1);
w0=0.0001;
We0=tot_mkt_share(Par,w0,Grid);
mkt_share=mkt_share(Par,w0,Grid);
w1=0.01;
We1=tot_mkt_share(Par,w1,Grid);
while(We0*We1>0)
if We0<0
w0=w0*0.5;
We0=tot_mkt_share(Par,w0,Grid);
end
if We1>0
w1=w1*1.5;
We1=tot_mkt_share(Par,w1,Grid);
end
end
iconv2=0;
tol2=0.0000001;
maxit2=1000;
it2=1;
while(iconv2==0 && it2<=maxit2)
w=(w0+w1)/2;
We=tot_mkt_share(Par,w,Grid);
if abs(We)<tol2
iconv2=1;
disp('wage has converged in')
it2
else
if We*We1>0
w1=w;
else
w0=w;
end
it2=it2+1;
end
end
if it2>=maxit2
disp('Warning: Zero profit condition not satisfied')
end
omega=mkt_share(Par,w,Grid);
The code for function mkt_share
function omega=mkt_share(Par,w0,Grid)
omega=w0;
for i=2:Par.n
rel_q=Grid.q(i);
fcn=#(omega) (rel_q)^(-Par.gamma)*(omega/w0)^(1-Par.beta*Par.gamma)*((1-
((1-w0)/Par.gamma+w0/Par.theta))/(1-
((1omega)/Par.gamma+omega/Par.theta)))^(Par.gamma-1)-1;
omega_i=fsolve(fcn,w0);
omega=[omega',omega_i]';
end
The code for function tot_mkt_share, which calls function mkt_share
function tot_mkt_share=tot_mkt_share(Par,w0,Grid)
tot_mkt_share=sum(mkt_share(Par,w0,Grid))-1;
When you do:
mkt_share=mkt_share(Par,w0,Grid);
you create a variable with the same name as the function. From this point on, the function is no longer accessible, it is shadowed. The last line attempts to index into this variable, rather than call the function as you intend, because the function is shadowed.

MATLAB programmatic GUI listbox value and data it refers to

I am making a GUI programmatically and have run into a small question regarding the uicontrol listbox and the data each 'value' or 'string' refers to. I feel this question will best be illustrated with some code. The code at the end of this question illustrates my question.
If you run this example, select all 5 files from the left listbox and press 'Button1', you will see them go into the right listbox. You can then select 1, or several of the files in the right list box and press 'Button2' and MATLAB will output the correct files names. All is fine here.
If you close and re run the program, and just select file1, file3, and file5, pressing Button1 will make them go into the right list box again. This is where my problem is: If you select all of the files now in the right list box (file1, file3, file3), and press Button2, matlab outputs file1, file2, and file3... not file1, file3 and file5 as I would like.
Now I understand why this is happening, because in a list box its value property starts at 1 and increases, and the underlying data in the Cell DataSet remains ordered file1 to file5... So value 1 2 3 refers to DataSet{1} DataSet{2} DataSet{3}...
What would be the best way to overcome this problem? I would like to add that for my actual GUI the file names might not always be so obviously named.
classdef example < handle
properties
Figure;
Button1;
Button2;
ListBox1;
ListBox2;
DataSet = {};
end
methods
function obj = example()
create(obj);
makeUpData(obj);
end
function create(obj)
obj.Figure = figure('Position',[300 300 640 640]);
obj.Button1 = uicontrol('Style','pushbutton','String','Button1',...
'Position',[240 260 80 40],'Callback',#obj.button1CB);
obj.Button2 = uicontrol('Style','pushbutton','String','Button2',...
'Position',[510 260 80 40],'Callback',#obj.button2CB);
obj.ListBox1 = uicontrol('Style','listbox','String','',...
'Position',[50 250 145 100],'Max',3);
obj.ListBox2 = uicontrol('Style','listbox','String','',...
'Position',[350 250 145 100],'Max',3);
end
function makeUpData(obj)
obj.DataSet = {'file1' 'file2' 'file3' 'file4' 'file5'};
obj.ListBox1.String = obj.DataSet;
end
function button1CB(obj,hObject,eventdata)
CurrentNum = get(obj.ListBox1,'Value');
NameListBox1 = get(obj.ListBox1,'String');
NewName = obj.ListBox2.String;
for i = 1:numel(CurrentNum)
NewName{end+1} = [NameListBox1{CurrentNum(i)}];
end
obj.ListBox2.String = NewName;
end
function button2CB(obj,hObject,eventdata)
CurrentNum = get(obj.ListBox2,'Value')
for i = 1:numel(CurrentNum)
obj.DataSet{CurrentNum(i)}
end
end
end
end
For this application I would recommend storing your data in a structure rather than a cell array. This will allow you to utilize dynamic field references to access your data and not worry about converting from a file name to an index in your cell array.
I've modified your properties definition:
properties
Figure;
Button1;
Button2;
ListBox1;
ListBox2;
DataSet = struct;
end
Your makeUpData definition:
function makeUpData(obj)
dummydata = {'file1' 'file2' 'file3' 'file4' 'file5'};
for ii = 1:length(dummydata)
obj.DataSet.(dummydata{ii}) = dummydata{ii};
end
obj.ListBox1.String = fieldnames(obj.DataSet);
end
And your button2CB definition:
function button2CB(obj,hObject,eventdata)
listboxvalues = get(obj.ListBox2, 'String');
CurrentNum = get(obj.ListBox2,'Value');
for i = 1:numel(CurrentNum)
obj.DataSet.(listboxvalues{CurrentNum(i)})
end
end
Now your selection returns:
ans =
file1
ans =
file3
ans =
file5
As expected.
You can change callback function of Button2 as follows, using ismember to get the corresponded index in the obj.DataSet:
function button2CB(obj,hObject,eventdata)
selected = cellstr(get(obj.ListBox2, 'String'));
referred = ismember(obj.DataSet, selected);
obj.DataSet(referred)
obj.DataSet{referred}
end

Get the handle of the contex menu caller

In matlab if I've a context menu with handle cxmenu_Options which is linked to different three uicontrol objects.
Inside the context menu callback function:
Code Demo:
function demoOnContextMenus
hFigure = figure;
hControl = uicontrol( ...
'Parent' , hFigure , ...
'Style' , 'Edit' , ...
'Position' , [200 200 180 40] , ...
'Tag' , 'IamControl' , ...
'String' , 'UI-Control');
hCxMenu = uicontextmenu( ...
'Tag' , 'IamMenu' , ...
'Callback',#CxMenuCallback);
set(hControl,'UIContextMenu',hCxMenu);
function CxMenuCallback(objectHandle,eventData)
tag = get(gcbo,'tag');
helpdlg(tag);
end
end
How Can I get the handle of the uicontrol which the context menu has been called from ?
There are two ways to access the handle:
gco returns the handle of the currently selected object. Thus tag = get(gco,'tag') will return IamControl.
Alternatively, you can pass the handle directly to the callback (in case the hierarchy becomes more complicated, since gco will only give you the top-level handle of the eventual chain):
handleToPass = hControl;
hCxMenu = uicontextmenu( ...
'Tag' , 'IamMenu' , ...
'Callback',#(oh,evt)CxMenuCallback(oh,evt,handleToPass));
set(hControl,'UIContextMenu',hCxMenu);
function CxMenuCallback(objectHandle,eventData,handleOfCaller)
tag = get(handleOfCaller,'tag');
helpdlg(tag);
end
Using Matlab's guide environment I have found another way to determine the caller.
The command gco (get current object) simply did the job.
In my case the context menu provides the option to open a path specified in an "Edit Text" object in Windows Explorer.
function open_in_browser_Callback(hObject, eventdata, handles)
cur_obj=gco;
cur_path=get(cur_obj,'String')
if(~isempty(cur_path))
winopen(cur_path);
end
With this solution I was able to use the same context menu for two "Edit Text" objects.

How to export data from Matlab to excel for a loop?

I have a code for "for loop"
for i=1:4
statement...
y=sim(net, I);
end
now i need to export the value of y to excel sheet. for that i used..
xlswrite('output_data.xls', y, 'output_data', 'A1')
but my problem is that the ID of excel i.e. "A1" should change according to each iteration... in my case for iteration 1-> A1, iteration-> A2 and so on..
anybody please help me out ..
thanks in advance. for any assistance.. or suggestion..
You can store sim outputs in a vector (y(ii)) and save in the sheet with a single write. This is also more efficient since you perform a single bulk-write instead of many small writes.
Specify the first cell and y will be written starting from there.
last = someNumber;
for i=1:last statement... y(i)=sim(net, I); end
xlswrite('output_data.xls', y', 'output_data', 'A1');
If you prefer specify the range write ['A1:A',num2str(last)] instead of A1.
If you really want to write within the loop try:
for ii=1:last
...
y=sim(net, I);
xlswrite('output_data.xls', y, 'output_data', sprintf('A%d',ii));
end
You can also do for yourself what xlswrite does internally, which is interact using COM. I prefer to do this when I have a frequently used excel template or data file, because it allows for more control (albeit with more lines of code).
Excel = actxserver('Excel.Application');
Workbook = Excel.Workbooks.Open('myExcelFile.xlsx');
MySheet = Excel.ActiveWorkBook.Sheets.Item(1);
set( get(MySheet,'Range','A1:A10'), 'Value', yourValues);
...
invoke(Workbook, 'Save');
invoke(Excel, 'Quit');
delete(Excel);
This would allow you to save new data to new ranges without re-opening excel each time.
Even better would be to define an oncleanup function (as does xlswrite) to prevent lost file locks (especially when you're doing things like exiting out of debug mode):
...
myWorkbook = Excel.Workbooks.Open(filename,0,true);
cleanUp = onCleanup(#()xlsCleanup(Excel, filename));
function xlsCleanup(Excel,filepath)
try
Excel.DisplayAlerts = 0; %// Turn off dialog boxes
[~,n,e] = fileparts(filepath); %// Excel API expects just the filename
fileName = [n,e];
Excel.Workbooks.Item(fileName).Close(false);
end
Excel.Quit;
end
You can put xlswrite after for loop.You just want to do is save you result in a matrix.This function can write a matrix.
also,you can use [] to combine string to change the range.
>> for i=1:4
Range=['A' num2str(i)]
end
Range =
A1
Range =
A2
Range =
A3
Range =
A4
But,this is a bad way.You should open and write Excel file every time.