Undocumented Matlab
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT

Using spinners in Matlab GUI

January 25, 2012 18 Comments

One of the few standard Java Swing controls that does not have any Matlab uicontrol counterpart is JSpinner. JSpinner is basically an editbox with two tiny adjacent up/down buttons. Spinners are similar in functionality to a combo-box (a.k.a. drop-down or pop-up menu), where a user can switch between several pre-selected values. They are often used when the list of possible values is too large to display in a combo-box menu. Like combo-boxes, spinners too can be editable (meaning that the user can type a value in the editbox) or not (the user can only “spin” the value using the up/down buttons).
JSpinner uses an internal data model, similarly to JTree, JTable and other complex controls. The default model is SpinnerNumberModel, which defines a min/max value (unlimited=[] by default) and step-size (1 by default). Additional predefined models are SpinnerListModel (which accepts a cell array of possible string values) and SpinnerDateModel (which defines a date range and step unit).
Here’s a basic code snippet showing how to display a simple numeric spinner for numbers between 20 and 35, with an initial value of 24 and increments of 1:

jModel = javax.swing.SpinnerNumberModel(24,20,35,1);
jSpinner = javax.swing.JSpinner(jModel);
jhSpinner = javacomponent(jSpinner, [10,10,60,20], gcf);

jModel = javax.swing.SpinnerNumberModel(24,20,35,1); jSpinner = javax.swing.JSpinner(jModel); jhSpinner = javacomponent(jSpinner, [10,10,60,20], gcf);

The spinner value can be set using the edit-box or by clicking on one of the tiny arrow buttons, or programmatically by setting the Value property. The spinner object also has related read-only properties NextValue and PreviousValue. The spinner’s model object has the corresponding Value (settable), NextValue (read-only) and PreviousValue (read-only) properties. In addition, the model also has the settable Maximum, Minimum and StepSize properties.
To attach a data-change callback, set the spinner’s StateChangedCallback property.
I have created a small Matlab demo, SpinnerDemo, which demonstrates usage of JSpinner in Matlab figures. Each of the three predefined models (number, list, and date) is presented, and the spinner values are inter-connected via their callbacks. The Matlab code is modeled after the Java code that is used to document JSpinner in the official documentation. Readers are welcome to download this demo from the Matlab File Exchange and reuse its source code.

Java's SpinnerDemo
Java's SpinnerDemo
  
My Matlab SpinnerDemo
My Matlab SpinnerDemo

As can be seen from the screenshot, SpinnerDemo also demonstrates how to attach a label to a GUI control with an associated accelerator key (Alt-D in the screenshot example, which sets the focus to the Date control).
An internal component in Matlab, namely com.mathworks.mwswing.MJSpinner, extends javax.swing.JSpinner, but in this particular case I cannot see any big advantage of using the internal MJSpinner rather than the standard JSpinner. On the contrary, using JSpinner will likely improve forward compatibility – MathWorks may well change MJSpinner in the future, but it cannot do anything to the standard Swing JSpinner. In other cases, internal Matlab controls do offer significant advantages over the standard Swing controls, but not here it would seem. In any case, the SpinnerDemo utility uses MJSpinner, but you can safely use JSpinner instead (line #86).
The internal Matlab controls are discussed in detail in Chapter 5 of my Matlab-Java book, and MJSpinner is specifically discussed in section 5.2.1.
Another JSpinner derivative is JIDE’s com.jidesoft.grid.SpinnerCellEditor, which can be used as the cell-editor component in tables. An example of this was shown in the article about Advanced JIDE Property Grids (and section 5.7.5 in the book). You may also be interested in the com.jidesoft.combobox.DateSpinnerComboBox, which presents a control that includes both a date-selection combo-box and a spinner (section 5.7.2):
A property grid with spinner control
A property grid with spinner control
  
JIDE's DateSpinnerComboBox
JIDE's DateSpinnerComboBox

Related posts:

  1. Password & spinner controls in Matlab GUI – Password fields and spinner controls can easily be embedded in Matlab GUIs. ...
  2. Matlab toolstrip – part 5 (icons) – Icons can be specified in various ways for toolstrip controls and the app window itself. ...
  3. Advanced JIDE Property Grids – JIDE property grids can use complex cell renderer and editor components and can signal property change events asynchronously to Matlab callbacks...
  4. Customizing Matlab labels – Matlab's text uicontrol is not very customizable, and does not support HTML or Tex formatting. This article shows how to display HTML labels in Matlab and some undocumented customizations...
  5. JGraph in Matlab figures – JGraph is a powerful open-source Java library that can easily be integrated in Matlab figures. ...
  6. Matlab-Java interface using a static control – The switchyard function design pattern can be very useful when setting Matlab callbacks to Java GUI controls. This article explains why and how....
GUI Internal component Java
Print Print
« Previous
Next »
18 Responses
  1. OrO January 25, 2012 at 18:14 Reply

    Great post, very useful. I did not know this function.

  2. ramiro massol March 8, 2012 at 12:49 Reply

    hi Yair
    great jcontrol. Do you happen to know how to force the jspinner to accept only numeric data and at the same time allow to clear the text-box-like field?

    best

    • Yair Altman March 8, 2012 at 13:00 Reply

      @Ramiro – good to see you here 🙂

      You can force the spinner to accept only numeric data by simply using the SpinnerNumberModel, as I have shown in the article and in my demo application, but then you cannot clear the value because it’s an invalid numeric value. If you think about it, there’s really no reason for a spinner to have no value, because this control is supposed to represent a set of pre-defined values.

  3. Lubos Smolik August 29, 2012 at 05:05 Reply

    Hi, great post!
    I’ve found it very useful and far better (more functional and, of course, better looking) than Spinner (from 2004, I think) I’ve found in official file exchange.

  4. Brittney Marimow June 30, 2015 at 13:42 Reply

    Thank you for the GREAT post. One question, is there a way to place this spinner within a “Parent” box. I am working on a GUI that has 5 different boxes on a single tab and would like to place this spinner in the fourth box.

    THANK YOU

    • Yair Altman June 30, 2015 at 14:04 Reply

      @Brittney – the 3rd (optional) input parameter of the javacomponent function is the parent container handle. It can be the figure handle (default value: current figure or gcf), or a uipanel handle. When you say “box” I assume you mean a uipanel, so simply specify its handle as the 3rd input arg of javacomponent.

      Additional information: http://undocumentedmatlab.com/blog/javacomponent

  5. Xiangrui Li December 16, 2015 at 14:47 Reply

    This is very useful, Yair!

    I am trying to find a way to store hFig into a spinner handle, so I can tell which figure in case of multiple figures. Here is code for the test:

    function spinnerTest
    % create a spinner with callback
    jModel = javax.swing.SpinnerNumberModel(1, 1, 9, 1);
    jSpinner = javax.swing.JSpinner(jModel);
    h = javacomponent(jSpinner);
    set(h, 'StateChangedCallback', @spinnerCallBack);
     
    % add Parent or Ancestor property for matlab gcbo
    schema.prop(h, 'Parent', 'mxArray'); % also tried Ancestor
    h.Parent = gcf;
     
    %% callback
    function spinnerCallBack(h, evt)
    [hSpinner, hFig] = gcbo; % try to get figure handle
    fprintf('Current value: %gn', hSpinner.getValue);
    hFig % this is empty with either Parent or Ancestor property added

    function spinnerTest % create a spinner with callback jModel = javax.swing.SpinnerNumberModel(1, 1, 9, 1); jSpinner = javax.swing.JSpinner(jModel); h = javacomponent(jSpinner); set(h, 'StateChangedCallback', @spinnerCallBack); % add Parent or Ancestor property for matlab gcbo schema.prop(h, 'Parent', 'mxArray'); % also tried Ancestor h.Parent = gcf; %% callback function spinnerCallBack(h, evt) [hSpinner, hFig] = gcbo; % try to get figure handle fprintf('Current value: %gn', hSpinner.getValue); hFig % this is empty with either Parent or Ancestor property added

    I also tried to set datatype as handle, and it seems h.Parent accepts only java handle, not figure/uicontrol handle:

    schema.prop(h, 'Parent', 'handle');
    h.Parent = gcf;
    Parameter must be a handle.

    schema.prop(h, 'Parent', 'handle'); h.Parent = gcf; Parameter must be a handle.

    My current solution is like this in a callback function for both spinner and other uicontrol:

    [h, hFig] = gcbo; % works for matlab uicontrol
    if isempty(hFig) % for java spinner
       hFig = get(h, 'Parent'); % retrieve stored property by schema.prop
    end

    [h, hFig] = gcbo; % works for matlab uicontrol if isempty(hFig) % for java spinner hFig = get(h, 'Parent'); % retrieve stored property by schema.prop end

    Is there a way to let gcbo return figure handle from java spinner? Octave gcbo uses ‘Parent’, and no idea about Matlab gcbo. Maybe I am too greedy 🙂

    Thanks.
    Xiangrui

    • Yair Altman December 16, 2015 at 15:45 Reply
      set(h, 'StateChangedCallback', {@spinnerCallBack, hFig});
      ...
       
      function spinnerCallBack(hSpinner, hEventData, hFig)
         ...
      end

      set(h, 'StateChangedCallback', {@spinnerCallBack, hFig}); ... function spinnerCallBack(hSpinner, hEventData, hFig) ... end

      • Xiangrui Li December 16, 2015 at 20:23

        Oh, yes. Thank you for the simple solution, Yair!

  6. Alain Barraud January 20, 2016 at 22:20 Reply

    hello yair,

    consider the following spinner

    Model = javax.swing.SpinnerNumberModel(125,15,225,.5);
    jSpinner = javax.swing.JSpinner(Model);
    [jhSpinner,hghandle] = javacomponent(jSpinner)
    jhSpinner.Font=java.awt.Font('Serif', 3, 50);

    Model = javax.swing.SpinnerNumberModel(125,15,225,.5); jSpinner = javax.swing.JSpinner(Model); [jhSpinner,hghandle] = javacomponent(jSpinner) jhSpinner.Font=java.awt.Font('Serif', 3, 50);

    At this step all is right!

    Now if I want to come back to non bold non italic

    jhSpinner.Font=java.awt.Font('Serif', 0, 50);

    jhSpinner.Font=java.awt.Font('Serif', 0, 50);

    is without any effect!!?? Any idea?

    Another curious point values appear as 140 140,5 141 … and not 140.5 ??

    Thanks
    Alain

    • Yair Altman January 20, 2016 at 23:07 Reply

      @Alain – apparently the JSpinner component creates its internal text-field using the parent control’s Font property value when it is first rendered, and then ignores changes to that parent property value. Instead, you can modify the text-field’s Font property (and its other property values) directly:

      jhSpinner.getEditor.getTextField.setFont(java.awt.Font('Serif', 0, 20));

      jhSpinner.getEditor.getTextField.setFont(java.awt.Font('Serif', 0, 20));

      • Alain Barraud January 21, 2016 at 11:30

        @yair
        Thanks a lot. It works fine. I had a similar problem with Spinner BackgroundColor and ForegroudColor. Now using for exemple

        val = num2cell([0 0.8 0.8]);
        jhSpinner.getEditor.getTextField.setBackground(java.awt.Color(val{:}));

        val = num2cell([0 0.8 0.8]); jhSpinner.getEditor.getTextField.setBackground(java.awt.Color(val{:}));

        gives the expected result.

        Why a non standard “Text: ‘125,5’” property although the Value property is
        “Value: 1.2550e+02” ?

        jhSpinner.getEditor.getTextField.getFormatter
        ans =
        javax.swing.JSpinner$NumberEditorFormatter@a798948

        jhSpinner.getEditor.getTextField.getFormatter ans = javax.swing.JSpinner$NumberEditorFormatter@a798948

        Is there a way to modify something here?
        Cheers
        Alain

      • Yair Altman January 21, 2016 at 11:46

        @Alain – see my SpinnerDemo (or read JSpinner‘s documentation) to see how to modify the text formatting.

      • Alain Barraud January 21, 2016 at 20:17

        @yair
        I have found the reason. By default the decimal format is local dependent!!
        In France it is , and not .
        Ok I’ll look at your demo.
        However a last point as in your demo, if the user enters a value outside min or max (for example 2030 as year) the spinner is out of use.
        Is there a special callback property to use in order to check this case.

        Alain

  7. Asimov February 20, 2018 at 05:29 Reply

    Hi, Yair!

    I tried to add a StateChangedCallback to the JSpinner, and if the value is modified, it will display the current value on the command line.

    import javax.swing.*
    import java.awt.*
    f = figure;
     
    sm = SpinnerNumberModel(1.1, 0, 9, 0.3); % default, min, max, step
    js = JSpinner(sm);
    [jspinner, mspinner] = javacomponent(js);
    set(jspinner,'StateChangedCallback', 'disp(jspinner.getValue)');

    import javax.swing.* import java.awt.* f = figure; sm = SpinnerNumberModel(1.1, 0, 9, 0.3); % default, min, max, step js = JSpinner(sm); [jspinner, mspinner] = javacomponent(js); set(jspinner,'StateChangedCallback', 'disp(jspinner.getValue)');

    However, I find a strange phenomenon, the callback is sometimes invoked twice.

    The codes above will output someting like this:

        1.4000 (click up)
        1.1000 (click down)
        1.1000
        0.8000 (click down)
        0.8000
        0.5000 (click down)

    1.4000 (click up) 1.1000 (click down) 1.1000 0.8000 (click down) 0.8000 0.5000 (click down)

    Notice that although I only click once, the disp sometimes output two same results.

    So I try to check whether the value is changed at the first time. However, something stranger happens.

    I replace the StateChangedCallback by the line below.

    set(jspinner,'StateChangedCallback', 'disp([jspinner.getPreviousValue jspinner.getValue])');

    set(jspinner,'StateChangedCallback', 'disp([jspinner.getPreviousValue jspinner.getValue])');

    Which outputs:

        1.4000    1.7000 (click up)
        1.1000    1.4000 (click down)
        1.4000    1.7000 (click up)
        1.4000    1.7000

    1.4000 1.7000 (click up) 1.1000 1.4000 (click down) 1.4000 1.7000 (click up) 1.4000 1.7000

    Notice that even the value is changed at line 3, it still disp twice.

    And the jspinner.getPreviousValue seems to not remember the result of the first time.

    So I use a temperate value to check whether the value is the same with the previous. (Why I use a tmp value? Because the jspinner.getPreviousValue failed in the case above.)

    tmp = 0;
    set(jspinner,'StateChangedCallback', ...
        'if tmp~=jspinner.getValue;disp(tmp);tmp = jspinner.getValue; end');

    tmp = 0; set(jspinner,'StateChangedCallback', ... 'if tmp~=jspinner.getValue;disp(tmp);tmp = jspinner.getValue; end');

    This time, the output works fine, and it never outputs two same lines.

    I guess the problem may be something related to the thread of java/matlab events, but I do not hava enough knowledge about it. The problem could be solved by adding a temp value and add an if-end sentence, but it would be better to know the reason.

    Could you please help me find the reason of this problem?

    By the way, your MATLAB-Java book and many blogs has helped me a lot!

    Thank you so much!

    • Yair Altman March 9, 2018 at 01:22 Reply

      @Asimov – Java events often fire twice: first when the event processing starts, and then when the even processing ends. You can process only one of them by adding code in your callback function to prevent re-entrancy. For example:

      % Variant1
      function myCallbackFcn1(hObject,eventData,varargin)
         persistent inCallback
         if ~isempty(inCallback),  return;  end
         inCallback = true;
         try
             % do something useful here
         catch
             % error trapping here
         end
         pause(0.001); drawnow;  % give all other GUI events a chance to bail out above
         inCallback = [];
      end  % myCallbackFcn1

      % Variant1 function myCallbackFcn1(hObject,eventData,varargin) persistent inCallback if ~isempty(inCallback), return; end inCallback = true; try % do something useful here catch % error trapping here end pause(0.001); drawnow; % give all other GUI events a chance to bail out above inCallback = []; end % myCallbackFcn1

      Additional information: http://undocumentedmatlab.com/blog/controlling-callback-re-entrancy

      If you found my book useful, then please post a favorable review of it on Amazon – thanks in advance!

  8. Santiago July 18, 2021 at 05:08 Reply

    Hi Yair.

    Is it possible to modify the spinner’s position (x, y, w, h) after it has been created? I’m trying to do it during a SizeChangedFcn callback.

    Thanks.

    • Yair Altman July 18, 2021 at 11:28 Reply

      yes – assuming you created the spinner using the javacomponent function, you can modify the Position and Units properties of the component’s container handle, which is the second output argument returned by javacomponent. See http://undocumentedmatlab.com/articles/javacomponent for details.

Leave a Reply
HTML tags such as <b> or <i> are accepted.
Wrap code fragments inside <pre lang="matlab"> tags, like this:
<pre lang="matlab">
a = magic(3);
disp(sum(a))
</pre>
I reserve the right to edit/delete comments (read the site policies).
Not all comments will be answered. You can always email me (altmany at gmail) for private consulting.

Click here to cancel reply.

Useful links
  •  Email Yair Altman
  •  Subscribe to new posts (feed)
  •  Subscribe to new posts (reader)
  •  Subscribe to comments (feed)
 
Accelerating MATLAB Performance book
Recent Posts

Speeding-up builtin Matlab functions – part 3

Improving graphics interactivity

Interesting Matlab puzzle – analysis

Interesting Matlab puzzle

Undocumented plot marker types

Matlab toolstrip – part 9 (popup figures)

Matlab toolstrip – part 8 (galleries)

Matlab toolstrip – part 7 (selection controls)

Matlab toolstrip – part 6 (complex controls)

Matlab toolstrip – part 5 (icons)

Matlab toolstrip – part 4 (control customization)

Reverting axes controls in figure toolbar

Matlab toolstrip – part 3 (basic customization)

Matlab toolstrip – part 2 (ToolGroup App)

Matlab toolstrip – part 1

Categories
  • Desktop (45)
  • Figure window (59)
  • Guest bloggers (65)
  • GUI (165)
  • Handle graphics (84)
  • Hidden property (42)
  • Icons (15)
  • Java (174)
  • Listeners (22)
  • Memory (16)
  • Mex (13)
  • Presumed future risk (394)
    • High risk of breaking in future versions (100)
    • Low risk of breaking in future versions (160)
    • Medium risk of breaking in future versions (136)
  • Public presentation (6)
  • Semi-documented feature (10)
  • Semi-documented function (35)
  • Stock Matlab function (140)
  • Toolbox (10)
  • UI controls (52)
  • Uncategorized (13)
  • Undocumented feature (217)
  • Undocumented function (37)
Tags
AppDesigner (9) Callbacks (31) Compiler (10) Desktop (38) Donn Shull (10) Editor (8) Figure (19) FindJObj (27) GUI (141) GUIDE (8) Handle graphics (78) HG2 (34) Hidden property (51) HTML (26) Icons (9) Internal component (39) Java (178) JavaFrame (20) JIDE (19) JMI (8) Listener (17) Malcolm Lidierth (8) MCOS (11) Memory (13) Menubar (9) Mex (14) Optical illusion (11) Performance (78) Profiler (9) Pure Matlab (187) schema (7) schema.class (8) schema.prop (18) Semi-documented feature (6) Semi-documented function (33) Toolbar (14) Toolstrip (13) uicontrol (37) uifigure (8) UIInspect (12) uitable (6) uitools (20) Undocumented feature (187) Undocumented function (37) Undocumented property (20)
Recent Comments
Contact us
Captcha image for Custom Contact Forms plugin. You must type the numbers shown in the image
Undocumented Matlab © 2009 - Yair Altman
This website and Octahedron Ltd. are not affiliated with The MathWorks Inc.; MATLAB® is a registered trademark of The MathWorks Inc.
Scroll to top