Numeric entry only in edit text MATLAB GUI - matlab

I have an edit text in a MATLAB GUI. I want the user to be able to write only numerals and whenever they write a text character, this last character is immediately deleted. Moreover, I don't know in which kind of function to put this code(callback, keypress, etc.).

This is impossible without resorting to Java. That is because MATLAB has no way to access a uicontrol's typed string; you can only access its current string (i.e., after pressing Enter or changing focus).
Below is an imperfect workaround. It uses two identical edit boxes, one on top of the other, but the topmost box is initially hidden. The KeyPressFcn of the visible edit box:
filters the keypresses on numeric-only
accumulates valid keypresses in a string with global storage
sets that string to the current string of the invisible edit box
Makes that invisible edit box visible, so that it occludes the one you're typing in
Its CallBack function then
Takes the string of the normally-invisible box
Sets the always-visible box' string equal to that string
Hides the normally-invisible box again
Here's the implementation (liberally borrowed from here):
function GUI_tst
% Create new GUI
G.fh = figure('menubar' , 'none',...
'units' , 'normalized', ...
'position', [.4 .4 .2 .2]);
% The actual edit box
G.eh1 = uicontrol('style' , 'edit',...
'units' , 'normalized', ...
'position' , [.1 .4 .8 .2],...
'string' , '',...
'KeyPressFcn', #kpr,...
'Callback' , #cll);
% The "fake" edit box
G.eh2 = copyobj(G.eh1, G.fh);
set(G.eh2, 'Visible', 'off');
% Its string (global)
G.eh_str = '';
guidata(G.fh, G);
end
% "Real" edit box' KeyPressFcn()
function kpr(~, evt)
if isempty(evt.Character)
return; end
G = guidata(gcbf);
% Occlude the "real" editbox with the "fake" one
set(G.eh2, 'visible', 'on');
% Accumulate global string if keys are numeric
if strcmp(evt.Key,'backspace')
G.eh_str = G.eh_str(1:end-1);
elseif isempty(evt.Modifier) && ...
any(evt.Character == char((0:9)+'0') )
G.eh_str = [G.eh_str evt.Character];
end
% Set & save new string
set(G.eh2, 'string', G.eh_str);
guidata(gcbf,G);
end
% "Real" edit box' CallBack()
function cll(~,~)
G = guidata(gcbf);
% Set the "real" box' string equal to the "fake" one's,
% and make the "fake" one invisible again
set(G.eh1, 'String', get(G.eh2, 'String'));
set(G.eh2, 'visible', 'off');
end
This works reasonably well, but it has some drawbacks:
because you're typing somewhere you can't see, the cursor is hidden
selecting text and pressing backspace/delete does not work
it's not very resource efficient
Although it is possible using Java (see this post by MATLAB-god Yair Altman), the simpler and more common way to do it is to just accept that the user is typing invalid input, and only check/correct it in the Callback function (i.e., after pressing Enter).

Related

Make a panel with values visible when I press a radiobutton

I have a uibuttongroup with radio buttons defined in it. I have uipanels defined with their corresponding properties. What I want to do is to be able to click one radio button and have one uipanel appear, and then click my other radio button to have the other uipanel appear. Here are snippets of my code:
operation_type_1 = uibuttongroup(S.Test, 'Title', 'Operation Type', 'position', [0 0.3 panel_w/2 0.15]);
uicontrol('Parent',operation_type_1, 'Style', 'radiobutton',...
'String', 'invisible',...
'position', [0 0 0 0], 'Tag', 'invisibutton');
uicontrol('Parent',operation_type_1,'Style','radiobutton',...
'String', 'Time Operation',...
'Position', 100*[0.1 flooring(3.5, 'tp') 1.2 0.15], 'Tag', 'timeop1');
uicontrol('Parent',operation_type_1,'Style','radiobutton',...
'String', 'Volume Operation',...
'Position', 100*[0.1 flooring(2.5, 'tp') 1.2 0.15], 'Tag', 'volumeop1');
This defines my button group and the two radio buttons.
Then I have code which creates a volume panel:
As well as a Time Panel:
These are in the same position. What I want is to be able to click on the "Time Operation" radio button and have the time panel be visible, and when I click on the "Volume Operation" radio button, the volume panel is visible.
I've tried doing switch case statements. I don't get errors, but I don't get results either. For example, my case statements for the time and volume panels are:
switch str
case 'timeop1'
if U.Value; S.result_panel_time1.Visible = 'On';
else S.result_panel_time1.Visible = 'Off';
end
case 'volumeop1'
if U.Value; S.result_panel_volume1.Visible = 'On';
else S.result_panel_volume1.Visible = 'Off';
end
How do I get this to work? I'm not using GUIDE, just coding a MATLAB GUI.
UPDATE
I've tried implementing the callback suggested below, but I get a "Function definition is misplaced or improperly nested." error. I use the following function:
function button_callback(U, varargin{2})
switch get(get(operation_type_1, 'SelectedObject'), 'Tag')
case 'timeop1'
if U.Value; S.result_panel_time1.Visible = 'On';
else S.result_panel_time1.Visible = 'Off';
end
case 'volumeop1'
if U.Value; S.result_panel_volume1.Visible = 'On';
else S.result_panel_volume1.Visible = 'Off';
end
end
end
And I've added the callbacks "...'callback', {#pb_call, S}" to my timeop1 and volumeop1. (Because all of the other function I have are in a .m file called pb_call.m). The function appears to be nested fine but the error points at the exact one.
It seems to me you did not define callback for your RadioButton. For example, set callback for volumeop1:
uicontrol('Parent',operation_type_1,'Style','radiobutton',...
'String', 'Volume Operation',...
'Position', 100*[0.1 0.3 1.2 0.15], 'Tag', 'volumeop1', ...
'Callback', #switchPanel);
Then in function switchPanel, you will set corresponding panel visible, while set others invisible.
This is trying to answer your questions, but it seems to me what you want is uitab.
My partner ended up fixing it:
The callback was {callback, S} and S, U, and str were:
S = varargin{3}; %main figure handle
U = varargin{1}; %current uicontrol
str = char(U.String);
The problem occurred in the radiobutton creation, since the result panels were being created after the radiobuttons could be triggered, thus nothing was made invisible/visible and an error would occur.
However, it would be highly convenient if callbacks could affect all GUI parts, not just previously defined ones. I've tried using guidata in the past, but I had to use other, less straighforward methods to accomplish my goals. I'll try using working samples and building upon those in the future, but currently I am working on another part of the project and will get back to that later.
But using either guidata/setappdata or something related would work here as well as my own solution, which is making sure that the objects you are trying to change are already defined before the button triggering the callback.
(He also posted this answer to where I asked this same question in MATLAB Answers.)

How to get handle to active object MATLAB GUI

Im trying to create a calendar using MATLAB GUI.
I have two Edit Text objects - edittext1 and edittext2.
I want to do this one:
I put cursor at edittext1 then select date at calendar and it pits into text field of edittext1.
And the same for edittext2: if I put cursor into edittext2 and select date it puts into edittext2 Edit Text.
I know I can use callback for calendar this way.
Question:
How can I put into Callback function handler to ACTIVE edit text object?
How to get handle to object where cursor is now?
About the focus question, there is no active text box when you click a date on the java calendar, because the active component at this time is the java calendar.
To know which text box was active last, you simply need to keep track of it. One way is to add a callback to the edit box, which will update a variable (stored in the appdata) with the handle of the latest active text box.
Armed with that, the callback of the calendar will just retrieve the date, then place it in the last active text box.
Note: The ButtonDownFcn event of the text box will only fire on left and right click if the text box 'enable' property is 'off' or 'inactive'. (if it is 'on', then only the right click is detected). That is why I declared the text boxes as inactive. That does not prevent you to update the text programmatically so I didn't think it was a problem.
Code for testcalendar.m:
function testcalendar
handles.f = figure;
commonEditProperties = {'Style', 'edit', 'String', '', ...
'Units', 'Normalized', ...
'Enable','inactive' , ...
'callback',#EditBoxFcn , ...
'ButtonDownFcn',#EditBoxFcn } ;
handles.ledit = uicontrol( commonEditProperties{:}, 'Position', [0.1 0.1 0.3 0.1], 'Tag','ledit' );
handles.redit = uicontrol( commonEditProperties{:}, 'Position', [0.5 0.1 0.3 0.1], 'Tag','redit' );
% preallocate a variable to hold the active text box handle
setappdata(handles.f,'activeTextBox',[]) ;
com.mathworks.mwswing.MJUtilities.initJIDE;
% Put calendar to my figure
handles.jPanel = com.jidesoft.combobox.DateChooserPanel;
[handles.hPanel,handles.hContainer] = javacomponent(handles.jPanel,[100,100,200,200], handles.f);
juiFunHandle = handle(handles.jPanel, 'CallbackProperties');
set(juiFunHandle, 'MousePressedCallback', ...
#(src, evnt)CellSelectionCallback(src, evnt, handles));
set(juiFunHandle, 'KeyPressedCallback', ...
#(src, evnt)CellSelectionCallback(src, evnt, handles));
% store gui handles in application data
guidata(handles.f , handles)
end
function EditBoxFcn(hobj,~)
handles = guidata(hobj) ;
ActiveTextBox = get(hobj,'Tag') ;
setappdata( handles.f , 'activeTextBox', handles.(ActiveTextBox) ) ;
end
function CellSelectionCallback(~, ~, handles)
% retrieve the handle of the active text box
ActiveTextBox = getappdata(handles.f,'activeTextBox') ;
% assign a default active text box if none was selected before
if isempty(ActiveTextBox) ; ActiveTextBox = handles.ledit ; end
numRetry = 10 ;
for k=1:numRetry
pause(0.1)
dateString = char( javaMethodEDT('getSelectedDate', handles.jPanel) ) ;
if ~isempty(dateString) ; break ; end
end
set(ActiveTextBox , 'String' , dateString ) ;
end
See it in action:
Edit
There is no pure Matlab way to have your Matlab edit box fully editable an reacting (firing an event) to a single click of any mouse button.
You can get this functionality by using the text box underlying java object. This java object exposes a lot of events and you can just pick the one you need.
The catch:
To get the handle of the underlying java object, you need to use the almighty findjobj utility by Yair Altman. You can download the latest version from the file exchange here: findjobj
Once you have that saved in your Matlab path, just replace the few first line of code defining the edit boxes given in the example above by:
commonEditProperties = {'Style', 'edit', 'String', '', 'Units', 'Normalized', 'Enable','on' } ;
handles.ledit = uicontrol( commonEditProperties{:}, 'Position', [0.1 0.1 0.3 0.1] );
handles.redit = uicontrol( commonEditProperties{:}, 'Position', [0.5 0.1 0.3 0.1] );
% preallocate a variable to hold the active text box handle
setappdata(handles.f,'activeTextBox',[]) ;
% Find the java underlying object for the text boxes
ledit = findjobj(handles.ledit) ;
redit = findjobj(handles.redit) ;
% assign a callback to the java object (which CAN detect single click)
set(ledit,'MouseClickedCallback',#(h,e) setappdata( handles.f , 'activeTextBox', handles.ledit ) ) ;
set(redit,'MouseClickedCallback',#(h,e) setappdata( handles.f , 'activeTextBox', handles.redit ) ) ;
And you can completely comment or remove the sub-function EditBoxFcn as the callback action is done directly.

How to check if a uicontextmenu is visible or active

What I have:
In a Matlab-GUI I have a uicontextmenu connected to a plot (=axes). If I "activate" this via a mouse-click (right-button), I can use the usual "Callback" to do something, like highlighting the plot. If the user then selects one of the uimenu-elements of the menu, I can use the Callback of this uimenu-element and reset the highlighting.
But there is a problem, if the user does not select an element. The context-menu disappears and I cannot find a way to find out, if this happens. In my example, the highlighted plot stays highlighted.
What I tried so far:
Besides reading the docs, I appended listeners to the properties to some of the uimenu-elements, e.g.:
addlistener(mymenu_element, 'Visible', 'PostSet', #mytest);
but this property, as well as others, seems not to be changed or touched at any time - what suprises me a bit :o
So the question is:
Is there a way to execute a function after a uicontextmenu is executed (or however you call it, when a context-menu "disappears")? In other words: if the user does not select an element of a context-menu, how can this be identified?
Since you cant listen to these items (I've run a few tests and come to the same conclusion) you can work around this by creating and managing your uicontextmenu in a different way:
function yourFunction
% create a figure
hFig = figure;
% add a listener to the mouse being pressed
addlistener ( hFig, 'WindowMousePress', #(a,b)mouseDown(hFig) );
end
function mouseDown(hFig)
% react depening on the mouse selection type:
switch hFig.SelectionType
case 'alt' % right click
% create a uicontext menu and store in figure data
hFig.UserData.uic = uicontextmenu ( 'parent', hFig );
% create the menu items for the uicontextmenu
uimenu ( 'parent', hFig.UserData.uic, 'Label', 'do this', 'Callback', #(a,b)DoThis(hFig) )
% assign to the figure
hFig.UIContextMenu = hFig.UserData.uic;
% turn visible on and set position
hFig.UserData.uic.Visible = 'on';
hFig.UserData.uic.Position = hFig.CurrentPoint;
% uicontext menu will appear as desired
% the next mouse action is then to either select an item or
% we will capture it below
otherwise
% if the uic is stored in the userdata we need to run the clean up
% code since the user has not clicked on one of the items
if isfield ( hFig.UserData, 'uic' )
DoThis(hFig);
end
end
end
function DoThis(hFig)
% Your code
disp ( 'your code' );
% Clean up
CleanUp(hFig);
end
function CleanUp(hFig)
% delete the uicontextmenu and remove the reference to it
delete(hFig.UserData.uic)
hFig.UserData = rmfield ( hFig.UserData, 'uic' );
end

Creating an unknown amount of uicontrols in matlab

So for a self project I'm creating a gui minesweeper like game in matlab and want to create an adjustable pushbutton grid however I'm not sure on how to do so. this is what I have got so far.
function createField()
xAmount = str2double(inputdlg('enter row length'));
yAmount = str2double(inputdlg('enter column length'));
for i = 1:xAmount
for j = 1:yAmount
%create buttons
end
end
end
One solution might be:
function create_field(hparent, nx, ny, width, padding)
% Test arguments
if ~test_parent_handle(hparent)
error('Parent must be a single valid graphic handle.');
elseif ~test_positive_integer(nx)
error('Number of buttons on X direction must be a scalar positive integer.');
elseif ~test_positive_integer(ny)
error('Number of buttons on Y direction must be a scalar positive integer.');
elseif ~test_positive_integer(width) ...
|| (width >= 100)
error('Button width must be a scalar positive integer smaller than 100.');
elseif ~test_positive_integer(padding) ...
|| (padding >= 20)
error('Button padding must be a scalar positive integer smaller than 20.');
end;
% Resize the parent to fit the button grid
set(hparent, 'Units', 'pixels');
ppos = get(hparent, 'Position');
ppos(3) = nx*width + (nx-1)*padding;
ppos(4) = ny*width + (ny-1)*padding;
set(hparent, 'Position', ppos);
% Create button grid
for p = 1:nx
for q = 1:ny
bpos = [ % Button spec:
(p-1)*(width+padding) % - X
(q-1)*(width+padding) % - Y
width % - W
width % - H
];
uicontrol( ...
'Units', 'pixels', ...
'Tag', sprintf('X%dY%d',p,q), ...
'Style', 'pushbutton', ...
'Parent', hparent, ...
'Position', bpos ...
);
end;
end;
% ----- NESTED FUNCTIONS -----
function tf = test_parent_handle(value)
tf = isscalar(value) ...
&& ishandle(value);
end
function tf = test_positive_integer(value)
tf = isscalar(value) ...
&& isreal(value) ...
&& isfinite(value) ...
&& (value > 0) ...
&& (fix(value) == value);
end
end
For a figure with 15 x 10 square buttons, each having the side 25 pixels with a padding of 3 pixels between the buttons, call:
create_field(figure(), 15, 10, 20, 3);
As with most problems there are many different approaches, having written something similar I'll give you the same prompts that I used when writing my helper function.
Your code is going to act based on what button is pressed so each button will need its own unique ID and properties. Depending on the MATLAB version used, each of your graphics elements will have a handle. Starting in R2014b, graphics objects can be addressed directly as objects rather than needing to utilize a numeric ID as a pointer.
Start with the figure window and check out the figure properties:
h.mainfig = figure; % Arbitrary figure window
get(h.mainfig); % Dump properties to command window
Right now we're probably most interested in the Units and Position properties of the main figure window, which you can use in a helper function to figure out how to size and space the buttons as you create them.
If we create a pushbutton graphics object with uicontrol() we're going to get mostly the same properties.
h.randombutton = uicontrol('Parent', h.mainfig, 'Style', 'pushbutton');
get(h.randombutton);
Again, we're interested in the Units and Position properties. We're also going to be interested in the Callback property, which is the function that executes when we interact with the button. Another good one is the Tag property, which can be used to set a unique string tag for each button for use with later logic.
You've probably noticed I'm using a structure array to store my graphics handles. This is similar to how MATLAB generates its object data when creating a GUI with GUIDE and has the huge advantage of a tidy data package to pass around our function. A great thing about structure arrays is that you can nest data structures, allowing us to easily generate and address graphics objects without needing to get clever with dynamic field references or eval() (yuck). Instead of having to do something like button_1, button_2, etc, we can do:
h.button(1) = uicontrol('Parent', h.mainfig, 'Style', 'pushbutton');
h.button(2) = uicontrol('Parent', h.mainfig, 'Style', 'pushbutton');
...
h.button(n) = uicontrol('Parent', h.mainfig, 'Style', 'pushbutton');
Now we know how to generate an arbitrary number of buttons programmatically and easily address them later on.
Other than the button generation we have another key function that I mentioned earlier, the button callback. Callback functions follow a slightly different syntax in that they natively pass along two arguments, the handle of the object whose callback is executing and the event data structure (see the documentation for more info). Because the function knows what UI object invoked it, we can make a pretty generic function.
Hopefully this helps!
Have you considered creating the ui in Java - undocumented matlab has some examples. Java would provide you nice LayoutManagers that will take care about resizing and more.

how to prevent uimenu (MATLAB) from disappearing when checked

I have added uicontextmenu to the line object. uicontextmenu includes 3 check boxes. whenever I check any of them uicontextmenu disappears. I want uicontextmenu visible for sometime so that i can check multiple boxes and see the change (same as a button group but in uicontextmenu). Is there any solution to this or some other approach?
cmenu=uicontextmenu;
set(he,'uicontextmenu',cmenu);
item1=uimenu(cmenu,'label','Data A','checked','off','callback',#func_a);
item2=uimenu(cmenu,'label','Data B','checked','off','callback',#func_b);
item3=uimenu(cmenu,'label','Data C','checked','off','callback',#func_c);
basically, he is the line object created by plot(x,y) and func_a, func_b, func_c are function to convert property 'checked' to on|off.
This example is greatly inspired by Benoit_11 solution, but a bit refined. I was also under the impression that the 3 different functions in your callback were doing different things so I made the 3 different menus change different properties of the line (instead of changing the same property with different values).
I made the uimenu callback in one single nested function. It decides what to do based on the parameter what2do supplied at the uimenu definition (but feel free to keep 3 separate functions). However, note that the function that toggle the check mark is the same for all uimenu (you don't need a separate function for each of them).
function hf = TestUiContext2
%// Extension of Benoit_11 solution
clear ; clc ; close all
hf = figure ; %// return the handle of the figure
hax = axes; %// Create axes and save handle
plot(rand(20,3)); %// Plot three lines
hcmenu = uicontextmenu; %// Define a context menu; it is not attached to anything
%// Define the context menu items and install their callbacks
item1 = uimenu(hcmenu, 'Label','Bold line' , 'Callback' , {#uiCallback,'bold'} );
item2 = uimenu(hcmenu, 'Label','Dotted line' , 'Callback' , {#uiCallback,'dots'} );
item3 = uimenu(hcmenu, 'Label','Markers on' , 'Callback' , {#uiCallback,'mark'} );
hlines = findall(hax,'Type','line'); %// Locate line objects
for line = 1:length(hlines) %// Attach the context menu to each line
set(hlines(line),'uicontextmenu',hcmenu)
end
function uiCallback(obj,~,what2do)
hline = gco ;
switch what2do
case 'bold'
toggle_bold_line(hline)
case 'dots'
toggle_dotted_line(hline)
case 'mark'
toggle_markers(hline)
end
%// reposition the context menu and make it visible
set(hcmenu,'Position',get(gcf,'CurrentPoint'),'Visible','on')
toggle_checkmark(obj) %// toggle the checkmark
end
function toggle_checkmark(obj)
if strcmp(get(obj,'Checked'),'on')
set(obj,'Checked','off')
else
set(obj,'Checked','on')
end
end
function toggle_bold_line(hline)
if get(hline,'LineWidth')==0.5
set(hline,'LineWidth',2)
else
set(hline,'LineWidth',0.5)
end
end
function toggle_dotted_line(hline)
if strcmpi(get(hline,'LineStyle'),':')
set(hline,'LineStyle','-')
else
set(hline,'LineStyle',':')
end
end
function toggle_markers(hline)
if strcmpi(get(hline,'Marker'),'none')
set(hline,'Marker','o')
else
set(hline,'Marker','none')
end
end
end
Now you can enjoy ticking all your menu in one go ;)
Here is a workaround which might do the trick for you. That's not too elegant but it seems to work.
The trick is to set the menu 'Visible' property to 'on' in every callback you have (i.e. #func_a, #funct_b and #funct_c). When I run the following example (based on the demo on the Mathworks website) the menu does not disappear when the selection is changed. Notice that I created separate functions for each callback.
Here is the code:
function TestUiContext( ~)
%// Based on example from The Mathworks
%// http://www.mathworks.com/help/matlab/ref/uicontextmenu.html
clear
clc
close all
%// Create axes and save handle
hax = axes;
%// Plot three lines
plot(rand(20,3));
%// Define a context menu.
hcmenu = uicontextmenu;
%// Define the context menu items and install their callbacks
item1 = uimenu(hcmenu,'Label','dashed','Callback',#(s,e) hcb1);
item2 = uimenu(hcmenu,'Label','dotted','Callback',#(s,e) hcb2);
item3 = uimenu(hcmenu,'Label','solid','Callback',#(s,e) hcb3);
%// Locate line objects
hlines = findall(hax,'Type','line');
%// Attach the context menu to each line
for line = 1:length(hlines)
set(hlines(line),'uicontextmenu',hcmenu)
end
%// In the callback of every item/option, set the menu property 'Visible' to 'on'.
function hcb1
set(gco,'LineStyle','--');
set(hcmenu,'Visible','on')
end
function hcb2
set(gco,'LineStyle',':');
set(hcmenu,'Visible','on')
end
function hcb3
set(gco,'LineStyle','-');
set(hcmenu,'Visible','on')
end
end
And 2 screenshots to show what it looks like:
And moving the cursor down:
So as I said, not perfect but hopefully it will do the job for you!