Interacting with graphs with context menus

| categories: plotting | View Comments

interactive_graph_contextmenu

Contents

Interacting with graphs with context menus

John Kitchin

In Post 1489 we saw how to interact with a contour plot using mouse clicks. That is helpful, but limited to a small number of click types, e.g. left or right clicks, double clicks, etc... And it is not entirely intuitive which one to use if you don't know in advance. In Post 1513 , we increased the functionality by adding key presses to display different pieces of information. While that significantly increases what is possible, it is also difficult to remember what keys to press when there are a lot of them!

Today we examine how to make a context menu that you access by right-clicking on the graph. After you select a menu option, some code is run to do something helpful. We will examine an enthalpy-pressure diagram for steam, and set up functions so you can select one a few thermodynamic functions to display the relevant thermodynamic property under the mouse cursor.

See the video.

function main

The XSteam module makes it pretty easy to generate figures of the steam tables. Here we make the enthalpy-pressure graph.

clear all; close all
P = logspace(-2,3,500); %bar

temps = [1 50 100 150 200 300 400 500 600 700 800]; % degC
figure; hold all
for Ti=temps
    Hh = @(p) XSteam('h_PT',p,Ti);
    H = arrayfun(Hh,P);
    plot(H,P,'k-')
    % add a text label to each isotherm of the temperature
    text(H(end),P(end),sprintf(' %d ^{\\circ}C',Ti),'rotation',90);
end

% add the saturated liquid line
hsat_l = arrayfun(@(p) XSteam('hL_p',p),P);
plot(hsat_l,P)

% add the saturated vapor line
hsat_v = arrayfun(@(p) XSteam('hV_p',p),P);
plot(hsat_v,P)

xlabel('Enthalpy (kJ/kg)')
ylabel('Pressure (bar)')
set(gca,'YScale','log') % this is the traditional view
% adjust axes position so the temperature labels aren't cutoff.
set(gca,'Position',[0.13 0.11 0.775 0.7])

Now we add a textbox to the graph where we will eventually print the thermodynamic properties. We also put a tooltip on the box, which will provide some help if you hold your mouse over it long enough.

textbox_handle = uicontrol('Style','text',...
    'String','Hover mouse here for help',...
     'Position', [0 0 200 25],...
     'TooltipString','Right-click to get a context menu to select a thermodynamic property under the mouse cursor.');

% we will create a simple cursor to show us what point the printed property
% corresponds too
cursor_handle = plot(0,0,'r+');

setup the context menu

hcmenu = uicontextmenu;

Define the context menu items

item1 = uimenu(hcmenu, 'Label', 'H', 'Callback', @hcb1);
item2 = uimenu(hcmenu, 'Label', 'S', 'Callback', @hcb2);
item3 = uimenu(hcmenu, 'Label', 'T', 'Callback', @hcb3);

Now we define the callback functions

    function hcb1(~,~)
        % called when H is selected from the menu
        current_point = get(gca,'Currentpoint');
        H = current_point(1,1);
        P = current_point(1,2);
        set(cursor_handle,'Xdata',H,'Ydata',P)

        % We read H directly from the graph
        str = sprintf('H   = %1.4g kJ/kg', H);
        set(textbox_handle, 'String',str,...
            'FontWeight','bold','FontSize',12)
    end

    function hcb2(~,~)
        % called when S is selected from the menu
        current_point = get(gca,'Currentpoint');
        H = current_point(1,1);
        P = current_point(1,2);
        set(cursor_handle,'Xdata',H,'Ydata',P)

        % To get S, we first need the temperature at the point
        % selected
        T = fzero(@(T) H - XSteam('h_PT',P,T),[1 800]);
        % then we compute the entropy from XSteam
        str = sprintf('S   = %1.4g kJ/kg/K',XSteam('s_PT',P,T));
        set(textbox_handle, 'String',str,...
            'FontWeight','bold','FontSize',12)
    end

    function hcb3(~,~)
        % called when T is selected from the menu
        current_point = get(gca,'Currentpoint');
        H = current_point(1,1);
        P = current_point(1,2);
        set(cursor_handle,'Xdata',H,'Ydata',P)

        % We have to compute the temperature that is consistent with
        % the H,P point selected
        T = fzero(@(T) H - XSteam('h_PT',P,T),[1 800]);
        str = sprintf('T   = %1.4g C',  T);
        set(textbox_handle, 'String',str,...
            'FontWeight','bold','FontSize',12)
    end

set the context menu on the current axes and lines

set(gca,'uicontextmenu',hcmenu)

hlines = findall(gca,'Type','line');
% Attach the context menu to each line
for line = 1:length(hlines)
    set(hlines(line),'uicontextmenu',hcmenu)
end
end

% categories: plotting
% tags: interactive, thermodynamics
Read and Post Comments

Interacting with graphs with keypresses

| categories: plotting | View Comments

interactive_graph_keypress

Contents

Interacting with graphs with keypresses

John Kitchin

In Post 1489 we saw how to interact with a contour plot using mouse clicks. That is helpful, but limited to a small number of click types, e.g. left or right clicks, double clicks, etc... And it is not entirely intuitive which one to use if you don't know in advance. Today we examine how to make key presses while the mouse is over the graph do something useful. We will examine an enthalpy-pressure diagram for steam, and setup functions so you can press u, s, p, h, or t to display the relevant thermodynamic property under the mouse cursor.

Watch the video here: http://screencast.com/t/nxAiiBXr

function main

The XSteam module makes it pretty easy to generate figures of the steam tables. Here we make the enthalpy-pressure graph.

clear all; close all
P = logspace(-2,3,500); %bar

temps = [1 50 100 150 200 300 400 500 600 700 800]; % degC
figure; hold all
for Ti=temps
    Hh = @(p) XSteam('h_PT',p,Ti);
    H = arrayfun(Hh,P);
    plot(H,P,'k-')
    % add a text label to each isotherm of the temperature
    text(H(end),P(end),sprintf(' %d ^{\\circ}C',Ti),'rotation',90);
end

% add the saturated liquid line
hsat_l = arrayfun(@(p) XSteam('hL_p',p),P);
plot(hsat_l,P)

% add the saturated vapor line
hsat_v = arrayfun(@(p) XSteam('hV_p',p),P);
plot(hsat_v,P)

xlabel('Enthalpy (kJ/kg)')
ylabel('Pressure (bar)')
set(gca,'YScale','log') % this is the traditional view
% adjust axes position so the temperature labels aren't cutoff.
set(gca,'Position',[0.13 0.11 0.775 0.7])

Now we add a textbox to the graph where we will eventually print the thermodynamic properties. We also put a tooltip on the box, which will provide some help if you hold your mouse over it long enough.

textbox_handle = uicontrol('Style','text',...
    'String','',... % start out empty
     'Position', [0 0 200 25],...
     'TooltipString','Press u,s,p,h or t to get the thermodynamic property under the mouse cursor. Ctrl-q to close the figure.');

% we will create a simple cursor to show us what point the printed property
% corresponds too
cursor_handle = plot(0,0,'r+');

Call back functions

we need to define two callback functions that will get called when the mouse moves over the figure, and when a key is pressed.

    function mouse_motion(~,~)
        % since we don't use (gcbo,event) the input arguments, we
        % replace them by ~
        current_point = get(gca,'Currentpoint');
        H = current_point(1,1);
        P = current_point(1,2);
        if (P > 0.01 && P < 1000) && (H > 0.01 && H < 4500)
            set(cursor_handle,'XData',H,'YData',P)
        end
    end

    function keypress_callback(gcbo,event)
        % We get the point under the cursor.
        H = get(cursor_handle,'XData');
        P = get(cursor_handle,'YData');

        % solve  for T
        T = fzero(@(T) H - XSteam('h_PT',P,T),[1 800]);

        % now define a string for each type of key press.
        str = ''; % default string to display
        switch event.Key
            case 'u'
                % compute internal energy for the P,T under cursor
                str = sprintf('U   = %1.4g kJ/kg', XSteam('u_PT',P,T));
            case 's'
                % compute entropy for the P,T under cursor
                str = sprintf('S   = %1.4g kJ/kg/K',XSteam('s_PT',P,T));
            case 'p'
                str = sprintf('P   = %1.4g bar',P);
            case 'h'
                str = sprintf('H   = %1.4g kJ/kg', H);
            case 't'
                str = sprintf('T   = %1.4g C',  T);
            case 'uparrow'
                if strcmp(event.Modifier,'control')
                    set(cursor_handle,'YData', P + 10)
                else
                    set(cursor_handle,'YData', P + 100)
                end
            case 'downarrow'
                if strcmp(event.Modifier,'control')
                    set(cursor_handle,'YData', P - 10)
                else
                    set(cursor_handle,'YData', P - 100)
                end
            case 'leftarrow'
                if strcmp(event.Modifier,'control')
                    set(cursor_handle,'XData', H - 10)
                else
                    set(cursor_handle,'XData', H - 100)
                end
            case 'rightarrow'
                if strcmp(event.Modifier,'control')
                    set(cursor_handle,'XData', H + 10)
                else
                    set(cursor_handle,'XData', H + 100)
                end
            case 'q'
                % quit. We use a dialog box to make sure we really
                % want to quit, in case q was accidentally pressed.
                choice = questdlg('Are you sure you want to quit?','','yes','no','no');
                switch choice
                    case 'yes'
                        close
                        return
                    case 'no'
                        str = '';
                end
            otherwise
                str = '';
        end

        % and put the string in our text box
        set(textbox_handle, 'String',str,...
            'FontWeight','bold','FontSize',12)
    end

finally, attach the callback functions to the current figure.

set(gcf,'KeyPressFcn',@keypress_callback)
set(gcf,'WindowButtonMotionFcn',@mouse_motion)
end

% categories: plotting
% tags: interactive, thermodynamics
% post_id = 1513; %delete this line to force new post;
% permaLink = http://matlab.cheme.cmu.edu/2011/12/07/interacting-with-graphs-with-keypresses/;
Read and Post Comments

Interacting with your graph through mouse clicks

| categories: plotting | View Comments

interactive_graph_click

Contents

Interacting with your graph through mouse clicks

John Kitchin

This post continues a series of posts on interacting with Matlab graphs. In Post 1484 we saw an example of customizing a datatip. The limitation of that approach was that you can only click on data points in your graph. Today we examine a way to get similar functionality by defining a function that is run everytime you click on a graph.

You will get a better idea of what this does by watching this short video: http://screencast.com/t/tTm0NlI0eI

function main
close all

Simple contour graph

[X,Y] = meshgrid(-2:.2:2,-2:.2:3);
Z = X.*exp(-X.^2-Y.^2);
[C,h] = contour(X,Y,Z); clabel(C,h);

we can define a function that does something when we click on the graph. We will have the function set the title of the graph to the coordinates of the point we clicked on, and we will print something different depending on which button was pressed. We also want to leave some evidence of where we clicked so it is easy to see. We will put a red plus sign at the point we clicked on.

add an invisible red plus sign to the plot

hold on
cursor_handle = plot(0,0,'r+ ','visible','off')
cursor_handle =

  212.0385

define the callback function

this function will be called for every mouseclick in the axes.

  function mouseclick_callback(gcbo,eventdata)
      % the arguments are not important here, they are simply required for
      % a callback function. we don't even use them in the function,
      % but Matlab will provide them to our function, we we have to
      % include them.
      %
      % first we get the point that was clicked on
      cP = get(gca,'Currentpoint');
      x = cP(1,1);
      y = cP(1,2);
      % Now we find out which mouse button was clicked, and whether a
      % keyboard modifier was used, e.g. shift or ctrl
      switch get(gcf,'SelectionType')
          case 'normal' % Click left mouse button.
              s = sprintf('left: (%1.4g, %1.4g) level = %1.4g',x,y, x.*exp(-x.^2-y.^2));
          case 'alt'    % Control - click left mouse button or click right mouse button.
              s = sprintf('right: (%1.4g, %1.4g level = %1.4g)',x,y, x.*exp(-x.^2-y.^2));
          case 'extend' % Shift - click left mouse button or click both left and right mouse buttons.
              s = sprintf('2-click: (%1.4g, %1.4g level = %1.4g)',x,y, x.*exp(-x.^2-y.^2));
          case 'open'   % Double-click any mouse button.
              s = sprintf('double click: (%1.4g, %1.4g) level = %1.4g',x,y, x.*exp(-x.^2-y.^2));
      end
      % get and set title handle
      thandle = get(gca,'Title');
      set(thandle,'String',s);
      % finally change the position of our red plus, and make it
      % visible.
      set(cursor_handle,'Xdata',x,'Ydata',y,'visible','on')
  end

% now attach the function to the axes
set(gca,'ButtonDownFcn', @mouseclick_callback)

% and we also have to attach the function to the children, in this
% case that is the line in the axes.
set(get(gca,'Children'),'ButtonDownFcn', @mouseclick_callback)

Now, click away and watch the title change!

end

% categories: plotting
% tags: interactive
Read and Post Comments