How can I relate a pushbutton to position?

Hi! I'm making a piano on matlab & can't figure out how to have matlab recognize if the user pushed on a key based on the position of the key itself. Basically, if the user clicks on the right key I want a "Good Job" message to come up & "Error" message to come up if they click the wrong one. I'm thinking to use an if statement but everything I try isnt working. Here is my code so far:
%Paino Figure w/ pushbuttons
function PianoImage_HBraslawsce()
f=figure();
set(f, 'MenuBar','none'); %Remove menu bar
set(f,'NumberTitle','off');
set(f,'Name', 'Note Learner'); %Change name
%Each key as a pushbutton
KEYC = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.15 .15 .1 .45]);
KEYD = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.25 .15 .1 .45]);
KEYE = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.35 .15 .1 .45]);
KEYF = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.45 .15 .1 .45]);
KEYG = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.55 .15 .1 .45]);
KEYA = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.65 .15 .1 .45]);
KEYB = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.75 .15 .1 .45]);
KEYCHI = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.85 .15 .1 .45]);
KEYCSH = uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized','position',[.20 .3 .08 .3]);
KEYEb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized','position',[.30 .3 .08 .3]);
KEYFSH=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized','position',[.5 .3 .08 .3]);
KEYAb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized','position',[.60 .3 .08 .3]);
KEYBb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized','position',[.70 .3 .08 .3]);
%Keys callback
set(KEYC,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYCSH,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYD,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYE,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYEb,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYF,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYFSH,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYG,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYAb,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYA,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYBb,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYB,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
set(KEYCHI,'Callback',{@keypressed,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI})
end
function keypressed(x,y,KEYC,KEYCSH,KEYD,KEYE,KEYEb,KEYF,KEYFSH,KEYG,KEYA,KEYAb,KEYB,KEYBb,KEYCHI)
x;
y;
KEYC;
KEYCSH;
KEYD;
KEYE;
KEYEb;
KEYF;
KEYFSH;
KEYG;
KEYA;
KEYAb;
KEYB;
KEYBb;
KEYCHI;
KEYC='C';
KEYCSH='C#';
KEYD='D';
KEYE='E';
KEYEb='Eb';
KEYF='F';
KEYFSH='F#';
KEYG='G';
KEYA='A';
KEYAb='Ab';
KEYB='B';
KEYBb='Bb';
KEYCHI='C(high)';
'C'=='position',[.15 .15 .1 .45];
'C#'=='position',[.20 .3 .08 .3];
'D'=='position',[.25 .15 .1 .45];
'E'=='position',[.35 .15 .1 .45];
'Eb'=='position',[.30 .3 .08 .3];
'F'=='position',[.45 .15 .1 .45];
'F#'=='position',[.5 .3 .08 .3];
'G'=='position',[.55 .15 .1 .45];
'Ab'=='position',[.60 .3 .08 .3];
'A'=='position',[.65 .15 .1 .45];
'Bb'=='position',[.70 .3 .08 .3];
'B'=='position',[.75 .15 .1 .45];
'C(high)'=='position',[.85 .15 .1 .45];
end

2 commentaires

Christophe
Christophe le 28 Avr 2020
Modifié(e) : Christophe le 28 Avr 2020
I do not understand what you are trying to do.
The code at the bottom from the line
C'=='position',[.15 .15 .1 .45];
is not correct.
Anyway, in your x variable, you have the uicontrol handle. You have access to all the uicontrol properties of the button you push.
Han
Han le 28 Avr 2020
I'm going to have the letters pop up on the screen and the user has to click the correct key based off of that letter. I honestly have no clue what I'm doing and thought relating the position of the button to the letter would be correct.

Connectez-vous pour commenter.

 Réponse acceptée

Geoff Hayes
Geoff Hayes le 28 Avr 2020
Han - try using the UserData property of the pushbutton to store the key that the button represents. That way, when the button is pressed and the callback fires, you can use (get) this property to compare against the letters that have popped up on screen. Your code could look something like
function PianoImage_HBraslawsce()
f=figure();
set(f, 'MenuBar','none'); %Remove menu bar
set(f,'NumberTitle','off');
set(f,'Name', 'Note Learner'); %Change name
%Each key as a pushbutton
KEYC = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.15 .15 .1 .45], 'UserData', 'C','Callback',@keypressed);
KEYD = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.25 .15 .1 .45], 'UserData', 'D','Callback',@keypressed);
KEYE = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.35 .15 .1 .45], 'UserData', 'E','Callback',@keypressed);
KEYF = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.45 .15 .1 .45], 'UserData', 'F','Callback',@keypressed);
KEYG = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.55 .15 .1 .45], 'UserData', 'G','Callback',@keypressed);
KEYA = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized','position',[.65 .15 .1 .45], 'UserData', 'A','Callback',@keypressed);
KEYB = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.75 .15 .1 .45], 'UserData', 'B','Callback',@keypressed);
KEYCHI = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.85 .15 .1 .45], 'UserData', 'C(high)','Callback',@keypressed);
KEYCSH = uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.20 .3 .08 .3], 'UserData', 'C#','Callback',@keypressed);
KEYEb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.30 .3 .08 .3], 'UserData', 'Eb','Callback',@keypressed);
KEYFSH=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.5 .3 .08 .3], 'UserData', 'F#','Callback',@keypressed);
KEYAb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.60 .3 .08 .3], 'UserData', 'Ab','Callback',@keypressed);
KEYBb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.70 .3 .08 .3], 'UserData', 'Bb','Callback',@keypressed);
function keypressed(hObject, eventdata)
fprintf('You have pressed key %s\n', get(hObject, 'UserData'));
end
end
I've nested the callback within the main function for convenience but also just in case you need to access those variables declared there. This avoids the need to pass the button handles into the callback function whose signature has now been reduced to simply
function keypressed(hObject, eventdata)
In your callback you were overwriting the pushbutton handle inputs with strings (this isn't something you really want or should do as you are changing the purpose of the variable from a function handle to a string). These strings are now stored in the UserData property for each button. We can then use the get function to retrieve that key string.
Note that when comparing this string against whatever note you've displayed, you will want to use the strcmp function (using == with strings will generally only work if the strings being compared have the same number of characters).

14 commentaires

@ Geoff Hayes, Hi
Clean, compact and nicely explained.
Han
Han le 28 Avr 2020
Oh okay! I understand now. If you could answer another question that would be great! I have an excel file which contains each possible level of the game. I then used xlsread to transfer the data over to Matlab and then used ranperm to randomly select a letter possibility. For example level one could be letters {'C D E F'}. I want these letters to pop up above the piano figure and have the user click the corresponding letters. I'm not sure how to have Matlab know whether the user is clicking on the correct key or not.
The following code will show you how to check whether the correct key is pressed or not. The following piece just sets up the array of keys and prints them out to the command line. This code should be put in the main function just after the buttons have been created.
randomKeysToPress = {'C', 'D', 'F#'};
fprintf('Please press the following keys: ');
for key = randomKeysToPress
fprintf(' %s', char(key));
end
fprintf('\n');
atKey = 1;
We have two variables - randomKeysToPress and atKey. The latter is an index into the former and we will use that to see if the user pressed the correct key or not. Since our callback is nested, it has access to these two variables.
function keypressed(hObject, eventdata)
fprintf('You have pressed key %s\n', get(hObject, 'UserData'));
if strcmp(randomKeysToPress{atKey}, get(hObject, 'UserData')) == 1
fprintf('You have selected the correct key!\n');
atKey = atKey + 1;
if atKey > length(randomKeysToPress)
fprintf('Game over!!!\n');
end
else
fprintf('You have selected the wrong key! :( \n');
end
end
Every time the user selects the correct key, we increment atKey index so that we can validate the next key press.
Han
Han le 28 Avr 2020
This helps me out so much!! Thank you again
Han
Han le 28 Avr 2020
Actually I have one more qestion, is there any way I can display the random letters above the figure?
Geoff Hayes
Geoff Hayes le 29 Avr 2020
Yes, you can use a text style uicontrol.
So my teacher told me that what I'm using is global variables and we aren't allowed to use those. Is there any way I can do this code with more programmer defined functions. Here is my code so you can see what I have.
%Paino Figure w/ pushbuttons
function PianoImage_HBraslawsce()%<SM:PDF_CALL>
f=figure();
set(f, 'MenuBar','none'); %Remove menu bar
set(f,'NumberTitle','off');
set(f,'Name', 'Note Learner'); %Change name
%Each key as a pushbutton
%<SM:PLOT>
KEYC = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.15 .15 .1 .45], 'UserData', 'C','Callback',@keypressed);
KEYD = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.25 .15 .1 .45], 'UserData', 'D','Callback',@keypressed);
KEYE = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.35 .15 .1 .45], 'UserData', 'E','Callback',@keypressed);
KEYF = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.45 .15 .1 .45], 'UserData', 'F','Callback',@keypressed);
KEYG = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.55 .15 .1 .45], 'UserData', 'G','Callback',@keypressed);
KEYA = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.65 .15 .1 .45], 'UserData', 'A','Callback',@keypressed);
KEYB = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.75 .15 .1 .45], 'UserData', 'B','Callback',@keypressed);
KEYCHI = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],'units','normalized',...
'position',[.85 .15 .1 .45], 'UserData', 'C(high)','Callback',@keypressed);
KEYCSH = uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.20 .3 .08 .3], 'UserData', 'C#','Callback',@keypressed);
KEYEb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.30 .3 .08 .3], 'UserData', 'Eb','Callback',@keypressed);
KEYFSH=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.5 .3 .08 .3], 'UserData', 'F#','Callback',@keypressed);
KEYAb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.60 .3 .08 .3], 'UserData', 'Ab','Callback',@keypressed);
KEYBb=uicontrol('style','pushbutton','backgroundcolor',[0 0 0],'units','normalized',...
'position',[.70 .3 .08 .3], 'UserData', 'Bb','Callback',@keypressed);
%MATLAB read data
[~,~,Array]=xlsread('Notes'); %<SM:READ>
%slices out notes only
Notes=Array(1:1,2:16); %<SM:SLICE>
Choices=Notes(1:1,1:15); %slices out notes only
NoteOptions=numel(Choices); %randomizes outputted notes
set1=randperm(NoteOptions); %<SM:RANDUSE>
x=Choices(set1(1:1));
splitb=split(x);%cuts out each note from string
random1 = splitb
for key = random1 %<SM:FOR>
sprintf(' %s', char(key));
end
newline;
atKey1=1;
function keypressed(hObject, eventdata) %callback for key clicked
if strcmp(random1{atKey1}, get(hObject, 'UserData'))==1 %<SM:ROP>
(msgbox('Good Job!'));
atKey1 = atKey1 + 1; %<SM:RTOTAL>
if atKey1 > length(random1)
(msgbox('Good Job! You have completed the round. Start over to keep on learning!'));
end
else
msgbox('ERROR. Please try again.');
end
end
end
Geoff Hayes
Geoff Hayes le 30 Avr 2020
Han - i would argue that the above code does not use global variables but variables that are shared between parent and nested function. Is the concern with the atKey and random1 variables?
Han
Han le 30 Avr 2020
Yes, because they come up light blue in my code and I guess this is not allowed.
I supposed you could use guidata which is a function that allows you to set and retrieve data that is associated with a figure (in your case, the GUI). This function is used in GUIDE-based GUIs but can be used for GUIs that are created programmatically as well. What you need to do is to create a structure that will manage the data that you want to share with your callback
splitb=split(x);%cuts out each note from string
sharedData.random1 = splitb
for key = sharedData.random1 %<SM:FOR>
sprintf(' %s', char(key));
end
newline;
sharedData.atKey1=1;
guidata(f, sharedData); % <---- "add" this shared data to the figure
The next step would be that you need to have your callback be aware of the figure so that you can get the shared data. One way to do this would be to pass the figure handle as an input to the callback
KEYC = uicontrol('style','pushbutton','backgroundcolor',[1 1 1],...
'units','normalized','position',[.15 .15 .1 .45], 'UserData', 'C',...
'Callback',{@keypressed, f});
Note how we set the Callback property with a cell array of the callback function handle and the figure handle. You will need to do this for each control/button. The callback signature will now change too (and you will need to place the callback outside of the parent function...so it is no longer nested).
function keypressed(hObject, eventdata, hFig) %callback for key clicked
sharedData = guidata(hFig);
if strcmp(sharedData.random1{sharedData.atKey1}, get(hObject, 'UserData'))==1 %<SM:ROP>
(msgbox('Good Job!'));
sharedData.atKey1 = sharedData.atKey1 + 1; %<SM:RTOTAL>
if sharedData.atKey1 > length(sharedData.random1)
(msgbox('Good Job! You have completed the round. Start over to keep on learning!'));
end
else
msgbox('ERROR. Please try again.');
end
end
Note how we use guidata to get the sharedData using the handle to the figure. Try this out and see what happens!
Han
Han le 30 Avr 2020
Yes, this worked perfectly! Thank you so much for your help!
Han
Han le 30 Avr 2020
Actually, I am having one issue. Matlab recognizes when I click the first button and it shows a 'Good Job' message but when I go to hit the next button it says that it is incorrect (even though I know I'm pressing the right button. I made sure the function wasn't nested so I'm not sure what the issue is.
Yes, I forgot a line (in the callback) to update the sharedData structure
function keypressed(hObject, eventdata, hFig) %callback for key clicked
sharedData = guidata(hFig);
if strcmp(sharedData.random1{sharedData.atKey1}, get(hObject, 'UserData'))==1 %<SM:ROP>
(msgbox('Good Job!'));
sharedData.atKey1 = sharedData.atKey1 + 1; %<SM:RTOTAL>
if sharedData.atKey1 > length(sharedData.random1)
(msgbox('Good Job! You have completed the round. Start over to keep on learning!'));
end
else
msgbox('ERROR. Please try again.');
end
guidata(hFig, sharedData); % <---- need to update the sharedData structure
end
Han
Han le 1 Mai 2020
Thank you so much! It works now

Connectez-vous pour commenter.

Plus de réponses (0)

Catégories

En savoir plus sur MATLAB Parallel Server dans Centre d'aide et File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by