I have this program, which as you can see is pulling random pictures out of a directory, and asking the user to compare them. After setting the value with the slider, the user presses a "Next Trial" button, which resets the slider and the random picture pair. How do I modify the code so that, after a certain number of repetitions (button presses), the program automatically ends (preferably with a "Experiment Ended" message)?
I can't find anything about how to do this in the MATLAB documentation. Do I need to set a variable, so that everytime the button is pressed "1" is added to the value of the variable, so that when it reaches a certain number (say "100") it terminates? Is that the easiest way to do this?
Here's the script:
function trials
files = dir(fullfile('samples','*.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('samples',picture1);
image2 = fullfile('samples',picture2);
subplot(1,2,1); imshow(image1);
subplot(1,2,2); imshow(image2);
uicontrol('Style', 'text',...
'Position', [200 375 200 20],...
'String','How related are these pictures?');
uicontrol('Style', 'text',...
'Position', [50 375 100 20],...
'String','Unrelated');
uicontrol('Style', 'text',...
'Position', [450 375 100 20],...
'String','Closely related');
uicontrol('Style','pushbutton','String','Next Trial',...
'Position', [250 45 100 20],...
'Callback','clf; trials()');
h = uicontrol(gcf,...
'Style','slider',...
'Min' ,0,'Max',50, ...
'Position',[100 350 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
One problem I see here is that the callback for your "Next Trial" button simply calls the function trials again. This is going to generate the combinations of images again, which you only want/need to do once. You should set the callback to be another nested function (like cb) so it can access the already-generated combinations.
Another problem is how you initialize picture1 and picture2. You should do your indexing like so:
picture1 = files(index(1,1)).name; %# Note that index is 2-dimensional!
picture2 = files(index(1,2)).name;
Now, you'll first want to initialize a variable to track the number of trials inside the function trials, as well as a maximum number of trials:
nReps = 1;
maxReps = 100;
Then your "Next Trial" button callback would look something like this:
function newTrial(s, e)
%# I assume you need the slider value for each trial, so fetch it
%# and save/store it here.
%# Check the number of trials:
if (nReps == maxReps)
close(gcf); %# Close the figure window
else
nReps = nReps + 1;
end
%# Get the new images:
picture1 = files(index(nReps, 1)).name;
picture2 = files(index(nReps, 2)).name;
image1 = fullfile('samples', picture1);
image2 = fullfile('samples', picture2);
%# Plot the new images:
subplot(1,2,1);
imshow(image1);
subplot(1,2,2);
imshow(image2);
%# Reset the slider to the default value:
set(h, 'Value', 25);
end
One additional suggestion... instead of displaying the slider value on the screen using FPRINTF, I would create a text object in your GUI and simply update its string value:
hText = uicontrol('Style', 'text', ...
'String', 'Slider value: 25', ... );
%# And in function cb...
set(hText, 'String', sprintf('Slider value: %f', lastVal));
Related
Consider the following example
function [B] = testtag
f = figure(1)
B = 1;
store_x = []
btn1 = uicontrol(f,'Style', 'pushbutton', 'String', 'tagpoints',...
'Position', [5 5 60 20],...
'Callback', #tagdata);
btn2 = uicontrol(f,'Style', 'pushbutton', 'String', 'storeandreturn',...
'Position', [70 5 80 20],...
'Callback', #returnvalue);
y_axis = [randi([0,20],1,100) randi([20,40],1,100) randi([0,20],1,100)];
x_axis = 1:300;
ax = subplot(1,1,1)
hplot = plot(x_axis,y_axis);
n = 1;
function tagdata(source,callbackdata)
[x, ~] = ginput(2); %saving indexs of the x axis or the time stamp of the place clicked
tic;
store_x(n:n + 1) = x(1:2);
n = n +2;
zoom on
end
function returnvalue(source,callbackdata)
B = store_x
close(figure(1))
end
end
It plots some random data and adds two buttons in the end of the plot , when one button is pressed it tags two data points in a pair and store them in a matrix , after that the user can zoom in and tag some more pair points , and the second button stores all the datapoints to the main return variable and closes the figure , now here is my problem the function will obviously return '1' in the output because the main function ends before the callback has been even called but what I want is the function to halt until all the datapoints have been stored in the return variable and the figure closes when second button is pressed without using a while loop ofcourse), is there some way ?
You want to use uiwait to stop the primary function returning.
Add
uiwait(f)
At the end of your main function -> that will wait until the figure closes (or a uiresume(f) is issued) to continue and hence return your data.
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 wrote a code that shows a figure devided to 2 parts;
the first one showing the main image, and the second one is a slider showing the rest of the images.
Now I need to add text to the main part (Like "Help" or "guide" text).
How can I do it?
This is my main sub-code:
%# design GUI
numSubs = 10; % Num of sub-images.
mx = numImgs-numSubs+1;
hFig = figure('Menubar','none');
% The Main Image:
hAx = axes('Position',[0 0.3 1 0.8], 'Parent',hFig);
hMainImg = imshow(img, 'Parent',hAx);
% the slider
hPanel = uipanel('Position',[0 0.04 1 0.26], 'Parent',hFig);
uicontrol('Style','slider', 'Parent',hFig, ...
'Callback',#slider_callback, ...
'Units','normalized', 'Position',[0 0 1 0.04], ...
'Value',1, 'Min',1, 'Max',mx, 'SliderStep',[1 10]./mx);
subImg = zeros(numSubs,1);
for i=1:numSubs
%# create axis, show frame, hookup click callback
hAx = axes('Parent',hPanel, ...
'Position',[(i-1)/numSubs 0 1/numSubs 1]);
% Load img number i
name=frames(i).name;
img=imread(name,'jpg');
subImg(i) = imshow(img, 'Parent',hAx);
value = i;
set(subImg(i), 'ButtonDownFcn',{#click_callback value})
axis(hAx, 'normal')
hold off;
end
Any suggestions?
Thanks in advance.
Use this construction:
hT = uicontrol('style', 'text', 'string', 'HELLO WORLD', 'position', [...])
It will create static text in the figure at position position. You can use all the regular options for uicontrols like 'parent' or 'units'.
However, since your image is in an axis, the better/easier way to do it is using
hT = text(X, Y, 'HELLO WORLD')
with X and Y the desired coordinates of the text in the axes.
You can set additional options via set:
set(hT, 'color', 'r', 'backgroundcolor', 'k', 'fontsize', 10, ...)
You can get a list of all options by issuing set(hT) on a mock text object.
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!