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

Scrollable GUI panels

July 25, 2018 3 Comments

Matlab enables two types of GUI container types, via the Units property: fixed-size ('pixels', 'chars', etc.) and flexible ('normalized'). In many cases, we need something in between: a panel that expands dynamically when its container grows (i.e., flexible/normalized), and displays scroll-bars when the container shrinks (i.e., fixed size, with a scrollable viewport). This functionality is relatively easy to achieve using a bit of undocumented magic powder. Today’s post will show how to do this with legacy (Java-based) figures, and next week’s post will do the same for web-based (JavaScript) uifigures.

Scrollable Matlab GUI panel
Scrollable Matlab GUI panel

Technical description

The basic idea is that in HG2 (Matlab release R2014b onward), uipanels are implemented using standard Java JPanel components. This enables all sorts of interesting customizations. For the purposes of today’s discussion, the important thing to note is that the underlying JPanel object can be re-parented to reside inside a Java JScrollPanel.
So, the idea is to get the Matlab panel’s underlying JPanel object reference, then embed it within a new JScrollPanel object that is placed at the exact same GUI coordinates as the original panel. The essential Matlab code snippet is this:

% Create the Matlab uipanel in the GUI
hPanel = uipanel(...); drawnow
% Get the panel's underlying JPanel object reference
jPanel = hPanel.JavaFrame.getGUIDEView.getParent;
% Embed the JPanel within a new JScrollPanel object
jScrollPanel = javaObjectEDT(javax.swing.JScrollPane(jPanel));
% Remove the JScrollPane border-line
jScrollPanel.setBorder([]);
% Place the JScrollPanel in same GUI location as the original panel
pixelpos = getpixelposition(hPanel);
hParent = hPanel.Parent;
[hjScrollPanel, hScrollPanel] = javacomponent(jScrollPanel, pixelpos, hParent);
hScrollPanel.Units = 'norm';
% Ensure that the scroll-panel and contained panel have linked visibility
hLink = linkprop([hPanel,hScrollPanel],'Visible');
setappdata(hPanel,'ScrollPanelVisibilityLink',hLink);

% Create the Matlab uipanel in the GUI hPanel = uipanel(...); drawnow % Get the panel's underlying JPanel object reference jPanel = hPanel.JavaFrame.getGUIDEView.getParent; % Embed the JPanel within a new JScrollPanel object jScrollPanel = javaObjectEDT(javax.swing.JScrollPane(jPanel)); % Remove the JScrollPane border-line jScrollPanel.setBorder([]); % Place the JScrollPanel in same GUI location as the original panel pixelpos = getpixelposition(hPanel); hParent = hPanel.Parent; [hjScrollPanel, hScrollPanel] = javacomponent(jScrollPanel, pixelpos, hParent); hScrollPanel.Units = 'norm'; % Ensure that the scroll-panel and contained panel have linked visibility hLink = linkprop([hPanel,hScrollPanel],'Visible'); setappdata(hPanel,'ScrollPanelVisibilityLink',hLink);

Note that this code will only work with panels created in legacy figures, not web-based uifigures (as I mentioned above, a similar solution for uifigures will be presented here next week).
Also note that the new scroll-panel is created with javaObjectEDT, in order to avoid EDT synchronization problems
We also want to link the visibility of the scroll-panel and its contained Matlab panel (hPanel), so that when the panel is set to be non-visible (hPanel.Visible='off'), the entire scroll-panel (scrollbars included) will become invisible, and vice-versa. We can do this by linking the Visible property of the Matlab panel and the scroll-panel container (hScrollPanel) using the linkprop function at the bottom of the script above. Note that we must persist the resulting hLink otherwise it becomes defunct – this is done by using setappdata to store the link in the panel (this way, when the panel is deleted, so does the link).

Resizing the container

The scroll-panel is created with a specific pixelpos location and size, and then its container is made to have normalized units. This ensures that when the container (hParent) grows, the scroll-panel grows as well, and no scrollbars appear (since they are not needed). But when the container shrinks in the X and/or Y direction, corresponding scrollbars appear as-needed. It sounds complicated, but it’s actually very intuitive, as the animated image above shows.
When the container resizes, the displayed viewport image may “jump” sideways. To fix this we can attach a simple repaint callback function to the scroll-panel’s SizeChangedFcn property:

% Attach a repaint callback function
hScrollPanel.SizeChangedFcn = @repaintScrollPane;
% Define the callback function:
function repaintScrollPane(hScrollPanel, varargin)
    drawnow
    jScrollPanel = hScrollPanel.JavaPeer;
    offsetX = 0; %or: jScrollPanel.getHorizontalScrollBar.getValue;
    offsetY = 0; %or: jScrollPanel.getVerticalScrollBar.getValue;
    jOffsetPoint = java.awt.Point(offsetX, offsetY);
    jViewport = jScrollPanel.getViewport;
    jViewport.setViewPosition(jOffsetPoint);
    jScrollPanel.repaint;
end

% Attach a repaint callback function hScrollPanel.SizeChangedFcn = @repaintScrollPane; % Define the callback function: function repaintScrollPane(hScrollPanel, varargin) drawnow jScrollPanel = hScrollPanel.JavaPeer; offsetX = 0; %or: jScrollPanel.getHorizontalScrollBar.getValue; offsetY = 0; %or: jScrollPanel.getVerticalScrollBar.getValue; jOffsetPoint = java.awt.Point(offsetX, offsetY); jViewport = jScrollPanel.getViewport; jViewport.setViewPosition(jOffsetPoint); jScrollPanel.repaint; end

Scrollbars automatically appear as-needed during resize
Scrollbars automatically appear as-needed during resize

Viewport position/offset

It would be convenient to have an easy-to-use ViewOffset property in the hScrollPanel object, in order to be able to programmatically query and set the current viewport position (i.e., scrollbars offset). We can add this property via the addprop function:

% Add a new Viewoffset property to hSCrollPanel object
hProp = addprop(hScrollPanel, 'ViewOffset');
hProp.GetMethod = @getViewOffset;  %viewOffset = getViewOffset(hScrollPanel)
hProp.SetMethod = @setViewOffset;  %setViewOffset(hScrollPanel, viewOffset)
% Getter method for the dynamic ViewOffset property
function viewOffset = getViewOffset(hScrollPanel, varargin)
    jScrollPanel = hScrollPanel.JavaPeer;
    jPoint = jScrollPanel.getViewport.getViewPosition;
    viewOffset = [jPoint.getX, jPoint.getY];
end
% Setter method for the dynamic ViewOffset property
function setViewOffset(hScrollPanel, viewOffset)
    jPoint = java.awt.Point(viewOffset(1), viewOffset(2));
    jScrollPanel = hScrollPanel.JavaPeer;
    jScrollPanel.getViewport.setViewPosition(jPoint);
    jScrollPanel.repaint;
end

% Add a new Viewoffset property to hSCrollPanel object hProp = addprop(hScrollPanel, 'ViewOffset'); hProp.GetMethod = @getViewOffset; %viewOffset = getViewOffset(hScrollPanel) hProp.SetMethod = @setViewOffset; %setViewOffset(hScrollPanel, viewOffset) % Getter method for the dynamic ViewOffset property function viewOffset = getViewOffset(hScrollPanel, varargin) jScrollPanel = hScrollPanel.JavaPeer; jPoint = jScrollPanel.getViewport.getViewPosition; viewOffset = [jPoint.getX, jPoint.getY]; end % Setter method for the dynamic ViewOffset property function setViewOffset(hScrollPanel, viewOffset) jPoint = java.awt.Point(viewOffset(1), viewOffset(2)); jScrollPanel = hScrollPanel.JavaPeer; jScrollPanel.getViewport.setViewPosition(jPoint); jScrollPanel.repaint; end

This enables us to both query and update the scroll-panel’s view position – [0,0] means top-left corner (i.e., no scroll); [12,34] mean scrolling 12 to the right and 34 down:

>> offset = hScrollPanel.ViewOffset   % or: get(hScrollPanel,'ViewOffset')
offset =
     0     0
>> offset = hScrollPanel.ViewOffset   % or: get(hScrollPanel,'ViewOffset')
offset =
    12    34
% Scroll 30 pixels right, 50 pixels down
>> hScrollPanel.ViewOffset = [30,50];   % or: set(hScrollPanel,'ViewOffset',[30,50])

>> offset = hScrollPanel.ViewOffset % or: get(hScrollPanel,'ViewOffset') offset = 0 0 >> offset = hScrollPanel.ViewOffset % or: get(hScrollPanel,'ViewOffset') offset = 12 34 % Scroll 30 pixels right, 50 pixels down >> hScrollPanel.ViewOffset = [30,50]; % or: set(hScrollPanel,'ViewOffset',[30,50])

attachScrollPanelTo utility

I have prepared a utility called attachScrollPanelTo (downloadable from the Matlab File Exchange), which encapsulates all of the above, plus a few other features: inputs validation, Viewport property in the output scroll-pane object, automatic encasing in a new panel for input object that are not already a panel, etc. Feel free to download the utility, use it in your program, and modify the source-code to fit your needs. Here are some usage examples:

attachScrollPanelTo();  % display the demo
attachScrollPanelTo(hPanel) % place the specified hPanel in a scroll-panel
hScroll = attachScrollPanelTo(hPanel);
hScroll.ViewOffset = [30,50];  % set viewport offset (30px right, 50px down)
set(hScroll, 'ViewOffset',[30,50]);  % equivalent alternative

attachScrollPanelTo(); % display the demo attachScrollPanelTo(hPanel) % place the specified hPanel in a scroll-panel hScroll = attachScrollPanelTo(hPanel); hScroll.ViewOffset = [30,50]; % set viewport offset (30px right, 50px down) set(hScroll, 'ViewOffset',[30,50]); % equivalent alternative

If you’d like me to add flare to your Matlab GUI, don’t hesitate to contact me on my Consulting page.

Related posts:

  1. Tab panels – uitab and relatives – This article describes several undocumented Matlab functions that support tab-panels...
  2. Syntax highlighted labels & panels – Syntax-highlighted labels and edit-boxes can easily be displayed in Matlab GUI - this article explains how. ...
  3. Customizing Matlab uipanels – Matlab uipanel controls can be customized using Java in ways that are impossible with plain Matlab. ...
  4. Transparent uipanels – Matlab uipanels can be made transparent, for very useful effects. ...
  5. Builtin PopupPanel widget – We can use a built-in Matlab popup-panel widget control to display lightweight popups that are attached to a figure window. ...
  6. Editbox data input validation – Undocumented features of Matlab editbox uicontrols enable immediate user-input data validation...
GUI Java uipanel Undocumented feature
Print Print
« Previous
Next »
3 Responses
  1. David Sampson August 4, 2018 at 11:24 Reply

    See also uix.ScrollingPanel in GUI Layout Toolbox. This works for figures (not uifigures) from R2014b onwards.

  2. Eivind October 7, 2020 at 21:38 Reply

    Hi,

    Thanks for a great function! I was trying to put the main panel of my figure into a scrollable panel with the goal of zooming into the panel using shift+scroll and scrolling up and down using normal scroll. I realised quickly however, that the zoom function I added to the WindowScrollWheelFcn property of my figure does not execute on scrolling because the scrolling is captured by the scroll panel.

    I’m not familiar enough with java to find out how to capture the scroll events and “re-route” them to my figure if the shift button is down, but I thought of an easier solution, i.e to detach the main panel from the scroll panel whenever I press the shift button and reattach it when I release the shift button. However, I got stuck here too, as I have no idea what to reparent the “jParent” to in what would be the reverse of this:

    jPanel = hPanel.JavaFrame.getGUIDEView;
    jParent = jPanel.getParent;
    jScrollPanel = javaObjectEDT(javax.swing.JScrollPane(jParent));

    jPanel = hPanel.JavaFrame.getGUIDEView; jParent = jPanel.getParent; jScrollPanel = javaObjectEDT(javax.swing.JScrollPane(jParent));

    I guess what I am trying to write is a detachScrollPanel function (similar to the decorateFig/undecorateFig functions). Would appreciate any help!

    • Yair Altman October 8, 2020 at 12:28 Reply

      jPanel1.setParent(jPanel2) will reparent jPanel1 (and similarly any Java component/container) inside jPanel2 (any Java container).
      While this might work, it seems to me to be an overly-complex solution. A much simpler one is to set your jScrollPanel’s MouseWheelMovedCallback callback as explained here: http://undocumentedmatlab.com/articles/uicontrol-callbacks

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