My program creates 5 pushbuttons, and I need to change their color according to the values in a matrix A. So I define A in the main, and then at the end of main I call the function create_maze(A).
The following is create_maze(A):
function create_maze(A)
A
% create 5X5 pushbuttons
scr = get(0, 'screensize');
f1 = figure(1);
set(f1, 'menubar', 'none');
set(f1, 'position', [scr(1) scr(2) scr(3) scr(4)]);
h1 = uicontrol('Style', 'pushbutton',...
'ForegroundColor', 'blue',...
'Position', [200 200 100 100],...
'Callback', pushbutton1_Callback);
h2 = uicontrol('Style', 'pushbutton',...
'ForegroundColor', 'blue',...
'Position', [300 200 100 100],...
'Callback', pushbutton1_Callback);
h3 = uicontrol('Style', 'pushbutton',...
'ForegroundColor', 'blue',...
'Position', [400 200 100 100],...
'Callback', pushbutton1_Callback);
h4 = uicontrol('Style', 'pushbutton',...
'ForegroundColor', 'blue',...
'Position', [500 200 100 100],...
'Callback', pushbutton1_Callback);
h5 = uicontrol('Style', 'pushbutton',...
'ForegroundColor', 'blue',...
'Position', [600 200 100 100],...
'Callback', pushbutton1_Callback);
function [L] = pushbutton1_Callback(h0object, A)
% here I put the pushbuttons together in an array
L=[h1, h2, h3, h4, h5];
for i = 1:size(A,1)
if i == 0
set(L{i},'Backgroundcolor','w');
elseif i == 1
set(L{i},'Backgroundcolor','b');
elseif i == 2
set(L{1},'Backgroundcolor','g');
elseif i == 2
set(L{i},'Backgroundcolor','y');
end
end
end
end
Unfortunately I get:
Error using create_maze/pushbutton1_Callback (line 164)
Not enough input arguments.
Could anybody help me with this loop?
Thank you!
You need to think about the scope of functions
Any variables defined within a function that aren't output by it will be cleaned up (deleted) when the function finishes executing.
In the case of graphics objects a link to the push button handles will still actually exist somewhere as a child of the main figure window, but it will be mush easier if you output the handles somehow from 'create_maze'
it is safer to define the line
L=[h1, h2, h3, h4, h5];
at the end of create_maze, rather than trying to be tricky with nested functions which will give you limited advantage here, and possibly slight overhead.
Check out the mathworks website for more info on the scope of variables in nested functions, but to be on the safe side, be sure that all of functions could run without relying on the other having defined variables.
Ie. make sure functions only need their input variables
Related
I've created a Matlab project where an image is read from file and then converted to binary using a predefined threshold value. This for loop is repeated for the entire folder of similar images.
I'm trying to implement a slider so that I can change the threshold value in realtime using the slider, which will in turn adjust the binary image. I'm having problems working out where to define the slider's uicontrol and also where / how to call the slider from within the loop.
I have the slider's uicontrol:
uicontrol(...
'tag', 'fff',...
'style', 'slider',...
'callback', #ui_slider_Callback,...
'position', [20 20 200 50],...
'tooltipstring', 'Colormap scaling relative to actual slice',...
'Max', 250,...
'Min', 0,...
'value', 230,...
'SliderStep', [0.002, 0.002]);
I also know somewhere I need to have this line, but I'm unsure whether I need to define a slider function:
thresholdValue = get(hObject,'Value');
A simplified version of my code follows:
function
yourfolder=path name;
d=dir([yourfolder '\*.jpg']);
files={d.name};
for q=1:numel(files);
I = imread(files{q});
J = rgb2gray(I);
thresholdValue = 230;
binaryImage = J < thresholdValue;
imshow(binaryImage);
drawnow;
end
end
Admittedly I have very limited knowledge of functions and calling them but any help would be greatly appreciated.
You need to define the callback function. One method is to define it in the same *.m file as your GUI, which MATLAB calls a local function.
A small functional example:
function testcode
% Initialize GUI
h.fig = figure('MenuBar', 'none', 'ToolBar', 'none');
h.ax = axes( ...
'Parent', h.fig, ...
'Units', 'Normalized', ...
'Position', [0.1 0.15 0.8 0.8] ...
);
h.slide = uicontrol( ...
'tag', 'fff', ...
'style', 'slider', ...
'Units', 'Normalized', ...
'position', [0.05 0.03 0.9 0.05], ...
'tooltipstring', 'Colormap scaling relative to actual slice', ...
'Max', 250, ...
'Min', 0, ...
'value', 230, ...
'SliderStep', [0.002, 0.002] ...
);
% Dummy image
I = imread('ngc6543a.jpg');
h.img = rgb2gray(I);
thresholdValue = get(h.slide, 'Value');
binaryImage = h.img < thresholdValue;
imshow(binaryImage, 'Parent', h.ax);
% Need to set callback after all our elements are initialized
set(h.slide, 'Callback', {#ui_slider_Callback, h});
end
function ui_slider_Callback(~, ~, h)
thresholdValue = get(h.slide, 'Value');
binaryImage = h.img < thresholdValue;
imshow(binaryImage, 'Parent', h.ax);
drawnow
end
By default, callbacks are always passed two variables, the handle of the invoking object and an eventdata structure, whose contents vary. As explained in the callback documentation, you can pass additional inputs to your callback by wrapping everything into a cell array. One thing to note is that the value of your variable being passed to the callback is its value as it exists when you define the callback. Because of this, you will see I have defined the callbacks once we have initialized all of our graphics objects.
I've used the slider handle explicitly rather than using hObj to get the threshold value. This is purely personal preference, either method will work fine.
I want to write a GUI program in Matlab and make 3 sliders on it with uicontrol and then write callbacks to use their 3 values in one command. I found a way to write a function for one slider as you can see in my program. Can you help me how use these 3 callbacks? (I use R2014a)
sld = uicontrol('Style', 'slider',...
'Min',0,'Max',255,'Value',0,...
'Position', [400 20 120 20],...
'Callback', #Blue);
sld = uicontrol('Style', 'slider',...
'Min',0,'Max',255,'Value',0,...
'Position', [400 60 120 20],...
'Callback', #Green);
sld = uicontrol('Style', 'slider',...
'Min',0,'Max',255,'Value',255,...
'Position', [400 100 120 20],...
'Callback', #Red);
function Red(source,~)
R = get(source,'Value');
end
function Green(source,~)
G = get(source,'Value');
end
function Blue(source,~)
B = get(source,'Value');
end
RGB = cat(3,R,G,B); %??????
Error: Undefined function or variable "R".
Persistent data must be stored somewhere that's accessible to the callback function. One common technique is to use the parent figure's UserData field. In the example above, once a slider has been moved, the current colour can be found in the RGB field of figure fh's UserData. Also, here only one callback is used, and the UI item is identified via its Tag.
fh = figure(1);
clf
%// Initialize figure's UserData
set(fh, 'UserData', struct('RGB', [0 0 0]));
sld_b = uicontrol('Style', 'slider',...
'Min',0,'Max',255,'Value',0,...
'Position', [400 20 120 20],...
'Callback', #colourHandler, 'Tag', 'blue');
sld_g = uicontrol('Style', 'slider',...
'Min',0,'Max',255,'Value',0,...
'Position', [400 60 120 20],...
'Callback', #colourHandler, 'Tag', 'green');
sld_r = uicontrol('Style', 'slider',...
'Min',0,'Max',255,'Value',255,...
'Position', [400 100 120 20],...
'Callback', #colourHandler, 'Tag', 'red');
%// This is in a separate file, colourHandler.m
function colourHandler(source, ~)
%// Find which slider triggered us
if strcmpi(get(source, 'Tag'), 'red')
ind = 1;
elseif strcmpi(get(source, 'Tag'), 'green')
ind = 2;
else
ind = 3;
end
%// update UserData
ud = get(get(source, 'Parent'), 'UserData');
ud.RGB(ind) = get(source, 'Value');
set(get(source, 'Parent'), 'UserData', ud);
end
Alternatively you can just store the handles of your graphics objects and use those to obtain the values in other functions.
For example:
function testcode
% Initialize sample GUI
h.fig = figure( 'MenuBar', 'none', 'ToolBar', 'none');
h.sld(1) = uicontrol( ...
'Parent', h.fig, ...
'Style', 'slider',...
'Min', 0, 'Max', 255, 'Value', 0, ...
'Units', 'Normalized', 'Position', [0.1 0.65 0.4 0.1], ...
'Tag', 'Red' ...
);
h.sld(2) = uicontrol( ...
'Parent', h.fig, ...
'Style', 'slider', ...
'Min', 0, 'Max', 255, 'Value', 0, ...
'Units', 'Normalized', 'Position', [0.1 0.45 0.4 0.1], ...
'Tag', 'Green' ...
);
h.sld(3) = uicontrol( ...
'Parent', h.fig, ...
'Style', 'slider', ...
'Min', 0, 'Max', 255, 'Value', 255, ...
'Units', 'Normalized', 'Position', [0.1 0.25 0.4 0.1], ...
'Tag', 'Blue' ...
);
% Use an axes object as a color display box
% Get starting RGB values for the color display, normalized so 0 <= x <= 1
startRGB = [get(h.sld(1), 'Value'), get(h.sld(2), 'Value'), get(h.sld(3), 'Value')]/255;
h.ax = axes( ...
'Parent', h.fig, ...
'Units', 'Normalized', 'Position', [0.6 0.36 0.3 0.3], ...
'XTickLabels', '', 'YTickLabels', '', ...
'Color', startRGB ...
);
% Need to set callback after all our elements are initialized
nsliders = length(h.sld);
set(h.sld, {'Callback'}, repmat({{#slidercallback, h}}, nsliders, 1));
end
function slidercallback(~, ~, h)
% Update background color of our axes object every time the slider is updated
RGB = [get(h.sld(1), 'Value'), get(h.sld(2), 'Value'), get(h.sld(3), 'Value')]/255;
set(h.ax, 'Color', RGB');
end
When callbacks execute they are passed 2 inputs by default, the invoking object and a structure of event data. As explained in the callback documentation, you can pass additional inputs to your callback by wrapping everything into a cell array. One thing to note is that the value of your variable being passed to the callback is its value as it exists when you define the callback. In other words, if we set the callback for our sliders at the same time we create them, when the callback for Red is fired h will only contain a handle to our figure, when the callback for Green is fired h will contain a handle to our figure and to the Red slider, and so on.
Because of this, you will see I have defined the callbacks once we have initialized all of our graphics objects. Using the curly brackets to set properties of multiple objects is explained in MATLAB's documentation for set. I use repmat so the size of the cell array is the same size as our array of slider objects.
I have a displayable image which I load via uigetfile. I want to allow the user to choose which portion of the image he wants to load by keying in the pixel coordinates of the top-left pixel and the bottom-right pixel into editable boxes. The problem is that I'm having some serious issues with the handles structure used to store data and don't quite understand how to use it.
Here is my code. I can easily load the 4 pixels in the topleft corner of the image (that's the default setting), but I fail to load anything else when the editable box values are changed. Is there something I'm missing here?
function mygui
%%
%Initialise GUI and set up editable boxes and push buttons
f = figure('Visible', 'off', 'Position', [360 500 450 285]);
handles.data.topleft1 = 1; %x-axis position of topleft pixel
handles.data.topleft2 = 1; %y-axis position of topleft pixel
handles.data.botright1 = 2; %x-axis position of bottom right pixel
handles.data.botright2 = 2; %y-axis position of bottom right pixel
hloader = uicontrol('Style', 'pushbutton', 'String', 'Load File', 'Position', [8 5 50 20], 'Callback', {#loadbutton_Callback, handles});
htopleft1 = uicontrol('Style', 'edit', 'String', handles.data.topleft1, 'Position', [25 40 15 10], 'Callback', {#topleft1_Callback, handles});
htopleft2 = uicontrol('Style', 'edit', 'String', handles.data.topleft2, 'Position', [40 40 15 10], 'Callback', {#topleft2_Callback, handles});
hbotright1 = uicontrol('Style', 'edit', 'String', handles.data.botright1, 'Position', [25 30 15 10], 'Callback', {#botright1_Callback, handles});
hbotright2 = uicontrol('Style', 'edit', 'String', handles.data.botright2, 'Position', [40 30 15 10], 'Callback', {#botright2_Callback, handles});
set([f, hloader, htopleft1, htopleft2, hbotright1, hbotright2], 'Units', 'normalized');
movegui(f, 'center')
set(f, 'Visible', 'on', 'toolbar', 'figure');
%%
%Loader pushbutton
function loadbutton_Callback(source, eventdata, handles)
[filename, pathname, filterindex] = uigetfile('*.jpg'); %Choose mario picture here from the directory you downloaded it from
picture = imread(strcat(pathname,filename));
topleft1 = handles.data.topleft1;
topleft2 = handles.data.topleft2;
botright1 = handles.data.botright1;
botright2 = handles.data.botright2;
picture = picture([topleft1:botright1], [topleft2:botright2], :); %Trim picture dimensions according to editable box inputs
imagesc(picture)
end
%%
%Editable boxes
function topleft1_Callback(source, eventdata, handles)
%Get new input from editable box; Save it into guidata handles structure thingy
topleft1 = str2double(get(source, 'String'));
handles.data.topleft1 = topleft1;
guidata(source, handles)
end
%(Repeat 3 more times for topleft2, botright1 and botright2)
end
And as usual, here's the picture which I'm trying to trim:
(source: gawkerassets.com)
I can suggest a solution with some changes that might be not as efficient, bu they'll work. I would do this kind of passing data between callbacks simply using the fact that Your whole GUI is a nested function, so all the callbacks can acces handles without even running the guidata function:
Do achieve this just change the way boxes are calling their callbacks from
(... 'Callback', {#topleft1_Callback, handles})
to:
(... 'Callback', #topleft1_Callback)
Now adjust arguments taken by Your callbacks, so the don't take three but two:
function myCallback(source,eventdata)
although none of those will be used, so You could simply write:
function myCallback(~,~)
as You MATlab will probably suggest. And You don't need the
guidata(source, handles);
line in any of Your callbacks anymore, since handles can be accesed anyway just by its name.
I am having a really hard time understanding the appropriate code/format for creating a datafile in MATLAB. For some reason this particular task just really confuses me.
So I have this script:
function semjudge
SubNum = ('Subject Number: ','s');
files = dir(fullfile('pictures','*.png'));
nFiles = numel(files);
combos = nchoosek(1:nFiles, 2);
index = combos(randperm(size(combos, 1)), :);
picture1 = files(index(1)).name;
picture2 = files(index(2)).name;
image1 = fullfile('pictures',picture1);
image2 = fullfile('pictures',picture2);
subplot(1,2,1); imshow(image1); title(picture1);
subplot(1,2,2); imshow(image2); title(picture2);
uicontrol('Style', 'text',...
'Position', [200 45 200 20],...
'String','How related are these pictures?');
uicontrol('Style', 'text',...
'Position', [50 45 100 20],...
'String','Unrelated');
uicontrol('Style', 'text',...
'Position', [450 45 100 20],...
'String','Closely related');
uicontrol('Style','pushbutton','String','Next Trial',...
'Position', [250 350 100 20],...
'Callback','clf; semjudge()');
h = uicontrol(gcf,...
'Style','slider',...
'Min' ,0,'Max',50, ...
'Position',[100 20 400 20], ...
'Value', 25,...
'SliderStep',[0.02 0.1], ...
'BackgroundColor',[0.8,0.8,0.8]);
set(gcf, 'WindowButtonMotionFcn', #cb);
lastVal = get(h, 'Value');
function cb(s,e)
if get(h, 'Value') ~= lastVal
lastVal = get(h, 'Value');
fprintf('Slider value: %f\n', lastVal);
end
end
end
Pretty simple little script. It pulls two random pictures from a folder, and the user is asked to compare them. All I want is a data file labelled by the Subject Number, something like:
fid = fopen(strcat('data','_',SubNum,'.txt'),'a');
The datafile itself I want to contain the title of each picture, and the values assigned to it by the slider. So, when the user presses the 'Next Trial' button, it saves title(picture1) and title(picture2) as well as lastVal.
I realize this is a very basic question, but the MathWorks documentation on datafiles I find to be very confusing, and I don't understand how to do it.
If I understand your problem correctly it should be something like this (check FPRINTF documentation for details):
fid = fopen(strcat('data','_',SubNum,'.txt'),'a');
fprintf(fid, '%s\t%s\t%f\n', picture1, picture2, lastVal)
fclose(fid);
Based on your code the file name will be ... a little weird. Like 'data_Subject Number: s.txt' (I hope s in 2nd line actually will be variable number), but it's up to you to change it.
If you want to print each variable as a single line, you can substitute \t with \n.
I have a script this MATLAB script:
function semjudge
clc;
name = input('Name: ','s');
snum = input('Subject #: ','s');
files = dir(fullfile('pictures','*.png'));
index = randperm(length(files));
picture1 = files(index(1)).name;
picture2 = files(index(2)).name;
image1 = fullfile('pictures',picture1);
image2 = fullfile('pictures',picture2);
subplot(1,2,1); imshow(image1); title(picture1);
subplot(1,2,2); imshow(image2); title(picture2);
uicontrol('Style', 'text',...
'Position', [200 45 200 20],...
'String','How related are these pictures?');
uicontrol('Style', 'text',...
'Position', [50 45 100 20],...
'String','Unrelated');
uicontrol('Style', 'text',...
'Position', [450 45 100 20],...
'String','Closely related');
uicontrol('Style','pushbutton','String','Next Trial',...
'Position', [250 350 100 20],...
'Callback','next');
h = uicontrol(gcf,...
'Style','slider',...
'Min' ,0,'Max',50, ...
'Position',[100 20 400 20], ...
'Value', 25,...
'SliderStep',[0.02 0.1], ...
'BackgroundColor',[0.8,0.8,0.8]);
set(gcf, 'WindowButtonMotionFcn', #cb);
lastVal = get(h, 'Value');
function cb(s,e)
if get(h, 'Value') ~= lastVal
lastVal = get(h, 'Value');
fprintf('Slider value: %f\n', lastVal);
end
end
end
This pulls up two random images from a directory onto the screen, with a scroll bar and instructions to determine the level of relatedness/similarity between the two pictures with a position on the scroll bar. That's all well and good.
What I want, though, is to set it so that when the "Next Trial" button is pressed, the screen will reset, with two NEW random pictures, and the scroll bar back in the middle. How do I do this? I can't find any instructions on how to do this online.
What about something like this:
uicontrol('Style','pushbutton','String','Next Trial','Position', [250 350 100 20],'Callback','clf; semjudge()');
clf to clear the figure window: http://www.mathworks.de/help/techdoc/ref/clf.html;
and afterwards your function is simply called again which will than plot into the SAME window!