Monday, July 2, 2012

A simple example of multiple GUIs working together

In this post, I will go over the implementation of a sample MATLAB application based on the comment left by a reader. A summary of the application's description is provided below, and the complete details can be found here. 

In this particular MATLAB application, there are three GUIs, namely, GUI-1, GUI-2 and GUI-3. A screenshot of the three GUIs are provided below, and the "starter code", which will be worked throughout this post, can be download here.

GUI-1 is the 'main' program and it serves as the gateway to the application. It provides a description of the application and has a push button, which when clicked on, will open up GUI-2 and close itself.

On GUI-2, there are multiple edit textboxes for accepting user inputs, there is also a push button, which when clicked on, will open up GUI-3 (if it hasn't been opened yet) and have the user inputs (or some results  computed based on the user inputs) pushed over to GUI-3 for display/presentation.

Note that once the GUI-3 is opened, any change in the user inputs on GUI-2 should trigger an update of the results displayed on GUI-3. There is also a push button on GUI-3, which when clicked on, will close GUI-2 and itself . 

Figure 1. Screenshot of the application 
Based on description above, the challenges faced in having these three GUIs working with each other can be posed as the follow three questions: 
1. How to open another GUI in an existing GUI?
2. How to close another GUI in an existing GUI?
3. How to pass data from one GUI to another GUI?

Opening GUI-2 from GUI-1


To be able to open GUI-2 by clicking a push button on GUI-1, simply call the m-file associated with GUI-2 in the callback of the push button on GUI-1. Using the sample code provided, find the callback function pb_open_Callback in GUI_1.m and add the following code. 

GUI_2_handle = GUI_2; % open GUI_2 and save the handle

To have GUI_1 close itself immediately after opening GUI_2, add the following code after the line above.

delete(get(hObject, 'parent')); % close GUI_1

Note that get(hObject, 'parent') returns the handle of GUI_1, since it is the parent of the push button, which is referred to as the  hObject in its callback. 


Now, save and run GUI_1. When you click on the push button, you should be able to see GUI_2 pops up and GUI_1 closes itself immediately after GUI_2. The updated code can be found here.



Opening GUI-3 from GUI-2, and closing GUI-2 from GUI-3

Opening GUI-3 with the push button on GUI-2 is very similar to what has been done above. However, the one key difference here is that there will be on-going interactions between GUI-2 and GUI-3, and thus, there are issues to be taken care of. For example, if the push button is clicked again after GUI-3 opens, it shouldn't be opening another instance of GUI-3, also, since GUI-3's push button is responsible for closing the whole application, the close functions  (executed when you clicked on the 'x' button at the upper right corner)  in GUI-2 and GUI-3 should not close themselves. 

1. In the opening function of GUI-2, initialize an empty field in the handles structure, which is to be used to hold the handle to GUI-3. The reason why we need to put it into the handles structure is that we will need to access the variable in other sub functions of GUI_2 (see step 2).


handles.GUI_3_handle = [];

2. In the callback of the push button on GUI-2, check to see if GUI-3 is already open, and if it is not, opens it the same way we did for GUI-2.

if isempty(handles. GUI_3_handle )
   % create GUI-3 
   handles. GUI_3_handle  = GUI_3; 
end

3. To be able to close GUI-2 within GUI-3, GUI-3 must have the handle to GUI-2 so that when the push button on GUI-3 is clicked, it will be able to use the delete function to first close GUI-2 and then GUI-3. There are many ways in which you can pass the handle (or any kind of data) from one GUI to another. Since this is a relatively simple example, it is acceptable to not to think too much about re-usability. Below shows the extra code that is responsible for passing the handle of GUI-2 to GUI-3.

if isempty(handles.GUI_3_handle)
   % create GUI-3 
   handles.GUI_3_handle = GUI_3;
   % obtain data in GUI-3
   GUI_3_data = guidata(handles.GUI_3_handle); 
   % store handle to GUI-2 as data in GUI-3
   GUI_3_data.GUI_2_handle = get(hObject,'parent');
   % save the GUI-3 data 
   guidata(handles.GUI_3_handle, GUI_3_data);

   % save the handles structure as data of GUI-2
   guidata(get(hObject,'parent'), handles);

end

4. To make sure the close function does not close the corresponding GUIs, replace the delete function with a dialog box that informs the user about the proper way to close the application. Note that to populate the close function in the m file, open the GUI using GUIDE. Under the figure property, click on the CloseRequestFcn (as shown in Figure 2 below).  

Figure. Creating the CloseRequestFcn in the figure property
In the CloseRequestFcn in the m-file (for both GUI-2 and GUI-3), replace the delete function, with the follow code. (You may want to pick a more appropriate dialog box.)

warndlg('Please click Close Application button on GUI-3','!! Warning !!')

If you try clicking the 'x' on the window to close the GUI, the warning dialog box will pop up and the GUI should stay opened.

5. To have the GUIs closed after the 'Close application' button on GUI-3 is clicked, simply put the following line in the call back of the push button.

delete(handles.GUI_2_handle);  % close GUI-2 
delete(get(hObject,'parent')); % close GUI-3 

Now, the opening and closing of all the GUIs in the application should function properly. The latest code can be downloaded here.

Updating results on GUI-3 from GUI-2


Based on the description of the application, when GUI-3 is opened from GUI-2, the results should be computed and then shown on GUI-3. In the callback of the push button on GUI-2, after the if-end block in which we open GUI-3 if it hasn't been created yet, we will add more code to take care of the computation of the results and the update of the results on GUI-3. The added code is highlighted in red below. 


if isempty(handles.GUI_3_handle)
   % create GUI-3 
   handles.GUI_3_handle = GUI_3; 
   % obtain data in GUI-3
   GUI_3_data = guidata(handles.GUI_3_handle); 
   % store handle to GUI-2 as data in GUI-3
   GUI_3_data.GUI_2_handle = get(hObject,'parent');
   % save the GUI-3 data 
   guidata(handles.GUI_3_handle, GUI_3_data);

   %save the handles structure as data of GUI-2
   guidata( get(hObject,'parent'), handles);

end
% GUI-3 must exist at this point. 

% obtain data stored in GUI_3 
GUI_3_data = guidata(handles.GUI_3_handle); 
% compute result based on input 1 on GUI_2 and update result 1 on GUI_3
input1 = str2double( get(handles.txt_input1, 'string'));
set(GUI_3_data.lbl_res1, 'string', num2str( input1 * rand));
% compute result based on input 2 on GUI_2 and update result 2 on GUI_3
input2 = str2double( get(handles.txt_input2, 'string'));
set(GUI_3_data.lbl_res2, 'string', num2str( input2 * rand));


For simplicity's sake, I compute the results as random numbers generated based on the inputs. Regardless of how complicated the computation can get, it is irrelevant to how the update is done. You can simply apply the set function on the static text fields on GUI-3, which are lbl_res1 and lbl_res2, to change the strings based on the "computed" results. Of course, it doesn't have to be static text fields, it can be an axes, it can be a list, as long as you can use the set function to modify the property of the uicontrol as a way to present the result, this approach would work. 


At this point, any time you click on the "Show results" button on GUI-2 , you should be able to see the results being updated on GUI-3. Next, we can work on the edit text on GUI-2, so that whenever a new input is entered (triggered by the focus leaving the text box or hitting the enter key in the text box), the corresponding result will be updated in GUI-3. First, go to the callback of input1's edit box, and add the following code.

% obtain data stored in GUI_3 
GUI_3_data = guidata(handles.GUI_3_handle); 
% compute result based on input 1 on GUI_2 and update result 1 on GUI_3
input1 = str2double( get(handles.txt_input1, 'string'));
set(GUI_3_data.lbl_res1, 'string', num2str( input1 * rand));

Then, for the callback of input2's edit box, add the following code

% obtain data stored in GUI_3 
GUI_3_data = guidata(handles.GUI_3_handle); 
% compute result based on input 2 on GUI_2 and update result 2 on GUI_3
input2 = str2double( get(handles.txt_input2, 'string'));
set(GUI_3_data.lbl_res2, 'string', num2str( input2 * rand));


At this point, if you update the inputs individually, you will see the corresponding results being updated on GUI-3 as well. The updated code can be downloaded here.


Things to consider

Note that what has been present here is a quick and easy way of getting things done. I would recommend this approach only if you have full knowledge of all the GUIs in the application and you don't plan to re-use any of the GUIs in the same or different application.

to be continued

Related links:
"Making Multiple GUIs Work Together" on mathworks.com

Wednesday, June 27, 2012

Sharing data among MATLAB GUIs: Part 3

In this post, I am going to share some of my principles in designing the data sharing among GUIs and use an example in the comment left by Anonymous to demonstrate how they work. 

The design procedure is fairly straightforward, simply following the four steps below should give you a clear picture as to how the data is communicated among all the GUIs in the application. 

1. Draw out all the GUIs. 
2. Connect the GUIs that will share data.
3. Determine the flow of the data for each connection.
4. Determine the possible action (push and/or pull) associated with the flow of data. 


Let's try it with the sample problem below:

I am working on 3 GUIs (using GUIDE). GUI-1 will call GUI-2 (used to collect user inputs, process the input data) and then output the results to GUI-3. 

Based on the description of the problem, the diagram in Figure 1 depicts the process of the design. Step 1 involves drawing there blocks for GUI-1, GUI-2, and GUI-3. Since it is stated that GUI-1 will call GUI-2 and then outputs the results to GUI-3, so connections are drawn between GUI-1 and GUI-2, GUI-1 and GUI-3. For the same reason, the flow of the data can be easily determined as from GUI-2 to GUI-1, and from GUI-1 to GUI-3. In Step 4, since it was stated that GUI-1 will call GUI-2, so I assign the pull action to the flow of data from GUI-2 to GUI-1, and since the data is processed by GUI-1 and output to GUI-3, I assign a push action to the flow of data from GUI-1 to GUI-3. 

Figure 1. A sample design process
In the next post, we will look at how to implement the data sharing based on the design. 

Monday, May 14, 2012

Drawing path taken in MATLAB

In this entry, I am showing a way of plotting path in MATLAB, which is often seen in pattern recognition and statistical modelling technique, for example, the Needleman-Wunsch algorithm and the Hidden Markov Models. Provided that the length of the output sequence is relatively short, and there are limit number of states, it may be beneficial to be able to visualize the path.

Below is an example based on the Needleman-Wunsch algorithm for sequence alignment. Note that all the cells are shown in the plot as text objects, and the available path is shown as thin lines while the optimal path obtained from the back tracking process is shown as thicker lines.
Note that even though the lines appear as broken segments in the plot, they are not drawn as such. It is rather difficult to figure out the starting and ending points of the line to produce a consistent and nice-looking plot. Instead, the lines are drawn between the centers of the text boxes. It looks like segments because there are solid white circles drawn on top of the lines, effectively, we are covering parts of the lines using white circles. Note that you can use rectangle(), which is a MATLAB function, to draw the circles, and you can set their facecolor and edgecolor to white, which will give the circles with solid white background.

The trick is to know that all the graphic objects are being placed on differently layers (or stacks). Usually, the objects that are added to the figure last will always appear on the top of everything else. So, depending on the order of how you draw things, the plot may appear different if there is any overlap between the objects.

To generate the above plot, plot the lines first, and then the white circle, and finally the text boxes. Note that in the case where the lines have to be drawn last, you can always use to uistack function to send the lines back to the bottom layer of the figure.

This rather messy m-file contains the code that produces the above plot.

Saturday, April 21, 2012

Detecting the type of mouse click

There are many ways for a user to interact with application using a mouse, there are normal click (single left click) that often selects the object, single right click that often opens up a context menu, and double-left click that often opens up the object. The ability to detect the type of the mouse click will come in handy when you are developing a truly interactive GUI. Fortunately, such information can be easily obtained by extracting the information stored in the SelectionType property of the GUI, which is a figure object.

1. Create a blank GUI and save the files.

2. Create a function to be attached to the WindowButtonDownFcn property of the GUI.


function winButtonDownFcn(hObject, eventData) 
m_type = get(hObject, 'selectionType');
if strcmp(m_type, 'normal')
    fprintf('left mouse click\n');
elseif strcmp(m_type, 'alt')
    fprintf('right mouse click\n'); 
elseif strcmp(m_type, 'open')
    fprintf('double-click\n');
end

3. In the opening function of the GUI, attach the function to the WindowButtonDownFcn property of the GUI.

set(hObject, 'windowButtonDownFcn', @winButtonDownFcn);

4. Running the GUI and try out different type of mouse clicks, the corresponding detection should be printed in the command window.

Note that there are other mouse clicks and/or key pressed may result in the same values in the selectionType property, for example, Control+left click will show up as a right click. If you would like to implement the more complicated mouse clicks (with key pressed), be sure to check the table in the documentation.

Example can be download here.

Monday, April 16, 2012

Making multiple objects drag-and-droppable

To continue on with the drag and drop topic, we now look at a way to make multiple objects in the gui drag-and-droppable. The idea is simple, rather than focusing on one object, we will be creating a cell array that stores all the handles to the objects that we would like to move, so that depending on where the mouse is, different objects will be the target of the drag-and-drop action. If we don't consider the case where multiple objects overlap, making multiple objects movable is a fairly straightforward procedure. However, to account for the fact that the different objects may overlap, the decision as to which object should be considered first, as well as the implementation of such decision make this problem interesting.

Note that I am making things a little complicated here because my starting point is windowMouseMotionFcn instead of the buttonDownFcn of the specific object. The former detects mouse motion while the latter detects mouse click on the specific object. I could easily start at buttonDownFcn and have the gui behave almost the same way, except that you won't be able to tell if an object is movable unless you click on it, in other words, there won't be any indicator (a hand-shape figure pointer) to indicate that the object is movable. Starting from the windowMouseMotionFcn is, in a way, a walk-around to the missing mouseOver fucntion, which allows you to implement the 'movable indicator'. That said, if you don't care about this functionality, check out selectmoveresize function, which is a MATLAB function that allows you to select/move/resize axes and uicontrol graphic objects.

Key procedures:

1. Create three static text fields and name their tags as lbl_target_1, lbl_target_2, and lbl_target_3. Change their unit to pixel. You can also change their background color to green and fontsize to 16.

2. In the opening function of the gui, create a cell array that consists of all the handles to the static text fields. The order of the handles in the cell array should reflect the order of corresponding uicontrols in the gui, specifically, if you wish to have a particular uicontrol appearing on top of another when they overlap, make sure its handle comes before the handle of the other uicontrol in the array.

movables = {handles.lbl_target_1, handles.lbl_target_2, handles.lbl_target_3};



3. Using uistack function to re-arrange the order of the uicontrols. First move the uicontrol to the top level, then move them down by an order that is equal to their position in the array minus 1. For example, for the third uicontrol, move it to the top first, then move it down by 2.

for i = 1:length(movables)
    cur_target = movables{i};
    uistack(cur_target, 'top'); 
    uistack(cur_target, 'down', i-1);
end


4. Attach drag_and_drop function to the windowMouseMotionFcn, with the argument movables as the cell array that contains the handles to desired uicontrols.

set(hObject, 'WindowButtonMotionFcn', {@drag_and_drop, movables});
5. In the drag_and_drop function, which is called whenever the mouse is moved on top of the gui, use a for-loop to go through all the targets and find out whether the mouse is on top of any of the uicontrol in the supplied handles array. Any time the mouse is found on top of a uicontorl, record the index of the uicontrol in the array and break the loop. Note that since the uicontrols have been sorted based on their "stack order", if there are multiple uicontrols overlap, the one at the top will always be detected.

target_id = [];
% find out which one the mouse is on top of right now 
for i = 1:length(target_handles)
    % get position information of the uicontrol
    lbl_target = target_handles{i};
    bounds = get(lbl_target,'position');
    lx = bounds(1); ly = bounds(2);
    lw = bounds(3); lh = bounds(4);
    if (x >= lx && x <= (lx + lw) && y >= ly && y <= (ly + lh))
        target_id = i;
        break;
    end
end

6. Once the index of the uicontrol is known, use another for-loop to attach grab function to the corresponding buttonDownFcn of the target uicontrol. A for-loop is needed because some uicontrols may require 'resets' if they are shadowed by another higher order uicontrol. .

for i = 1:length(target_handles)
    lbl_target = target_handles{i};
    if target_id == i 
        % set enable to off so that the whole static text field is hotspot
        set(lbl_target, 'enable', 'off');
        set(lbl_target, 'string', 'IN');
        set(lbl_target, 'backgroundcolor', 'red');
        set(lbl_target, 'ButtonDownFcn', @grab);
        setfigptr('hand', handles.fig_mouse);
    else
        % re-enable the uicontrol
        set(lbl_target, 'enable', 'on');
        set(lbl_target,'string', 'OUT');
        set(lbl_target, 'backgroundcolor', 'green');
    end
end
if isempty(target_id)
    setfigptr('arrow', handles.fig_mouse);
end

Video demo:



Files for download

Note: 
1. in the functions attached to the different callbacks of mouse actions in the gui, make sure you are not referring to other uicontrols in the gui by their tags, doing so will limit the use of the functions to the specific gui only. My drag_and_drop function will be a good example of this bad practices, since I  refer to the labels that reflect mouse action/position using their tags. 
2. Static text field isn't really a good choice as the uicontrol to be move around. In future posts in the series, I will switch to axes object instead. 

Monday, April 9, 2012

Sharing data among MATLAB GUIs: Part 2

In Part 1 of the series, I have shown that how data can be shared between the guis by writing sub-functions in the gui to take over the responsibility. Even though the idea is simple, the actual implementation takes some skills and understanding of how MATLAB guis work.

Often time, you will see the use of global variables proposed as a quick and easy fix to the problem. If the design of the application is simple, I can see why it may be the preferred approach. However, the main issue I have with the use of global variables is its awkward use.

When you use global variables, you are forced to assign/read data to/from global variables instead of push/pull data. Why does it matter, you may wonder. Well, it is awkward. When you have two guis running at the same time, the user expects that the action performed in one gui should be sufficient to complete the data sharing, rather than interacting with two guis. For example, in the example I gave in Part 1, depending on which button the user clicks, the data will either be pulled or pushed to the target gui and displayed in the text box. If you were to use global variable, you will need to first save the string in a global variable and then load the string from the global variable in the target gui, which requires two actions from the user. Isn't that awkward for data sharing? That's just for two guis, if you have five guis, you will have to cycle through all five of them to have the most up-to-date version of the data in all five of the guis.

Not only more actions is required from the user, the order of the actions is also important. If the order is not followed properly, the results in the gui may not be what's expected. For example, if you read from the global variable in the target gui before assigning updated value to the global variable in the source gui, then the target gui has the outdated values.

Finally, if you wish to expand the existing application or combine it with other guis, there may be conflicts as to how the global variables are used. I can't imagine how much of a pain that is going to be (Really, I haven't done it that way, so I have no idea).

The above are just my opinions on why using global variable is not the appropriate mean to address the data sharing among MATLAB guis. That said, I am not an expert, so if anyone thinks I am talking non-sense here, please do let me know.



Sunday, April 8, 2012

Sharing data among MATLAB GUIs: Part 1

When there are multiple GUIs in your MATLAB application, the need to let the GUIs communicate with each other is inevitable. While sharing data between the different callbacks within the same GUI may already turn some people off, the sharing of data among different GUIs is not as difficult as one may think.

In short, you need to write functions in the different GUIs to deal with establishing communication links and data exchange, and when the time comes to pull/push data to/from a GUI, simply call the appropriate functions via the GUI's functions to have the data updated.

When two GUIs are sharing data, one of them must be providing the data while the other must be receiving the data. For simplicity'sake, I am going to call the one that sends the data as the source gui and the one that receives the data as the target gui.

In the source gui, you need to have a function that returns the desired data when it is called, say, get_data(). Similarly, in the target gui, you need to have a function that read in (or use) the data, say, set_data().

Depending on where you initiate the data exchange between the two guis, the sequence of actions is different. If the communication is initiated at the source gui, you are pushing data to the target gui; on the other hand, if it is initiated at the target gui, you are pulling data from the source gui. The former case will call the set_data function in the target gui to have the data pushed over, while the latter case will call the get_data function in the source gui to have the data pulled from the source gui.

Getting down to the actual implementation, there are basically two steps. In the first step, you need to make the guis aware of each other's existence and know what their responsibility is; in the second step, you need to specify the detail/protocol of the communication. You can think of the first step as the laying down the necessary hardware and the second step as implementing software that utilize the hardware in place.

Step 1: Setting up the communication links between the guis

To share data between guis, they have to be aware of each other. In the simplest cases where you have a source gui and a target gui, you will need to have the handle of the target gui in the source gui, and the handle of the source gui in the target gui. To accomplish that, you can have a set_target function in the source gui and a set_source function in the target gui. When the functions are called, they will take the handle of the other gui and store it as an item in the handles structure for later use. Below is a sample set_target function in the source gui.


function set_target(source_handle, target_handle)
% retrieve handles structure in the source gui
handles = guidata(source_handle);  
% assign target handle as an item in the handles structure 
handles.target = target_handle;
% save the handles s
guidata(source_handle, handles);


With the set_target and set_source functions properly written in the source gui and target gui, running the following code will have the communication link established.

% This will be your main script 
% start up the two guis 
source_handle = source_gui;
target_handle = target_gui; 
% establish the link between the two 
source_gui('set_target', source_handle, target_handle); 
target_gui('set_source', target_handle, source_handle);

Step 2: Setting up the communication protocol (well, in a way)

Once the links are established, you will need to implement rules as to what and how the data is shared. During the design, you need to make sure the guis make no assumption about what is going on in the other gui or what variables are being used. Knowing exactly what your intention is with each gui should be able to help you set up your rules properly.

As an example, I have two guis with text boxes and push buttons in them. The push button in the source gui is labelled 'Send', while the one in the target gui is labelled 'Retrieve'. The interaction is fairly straightforward, write whatever you can think of in the text boxes, once a push button is clicked, you will see the text in the source gui copied over to the text box in the target gui. That said, clicking the Send button will push the data, while clicking the Retrieve button will pull the data. Same results, different approach though. The set_data and get_data functions are posted below. Nothing fancy, they either set the string property or get the string from the text box.

function set_data(handles, s) %  in the target gui 
set(handles.txt_tgt,'string', s);


function s = get_data(handles) % in the source gui 
s = get(handles.txt_src,'string');

Note that to be able to call these functions from a different guis, you need to first `publish' the functions by adding the handle to the function as an item in the handles structure. To do that, in the opening function of the gui (before the handles structure is saved), add

handles.set_data = @set_data;

to the target gui, and add

handles.get_data = @get_data; 

to the source gui. Doing so will allow you to access these functions from a different gui.

As the final step, we just need to set up the callbacks of the push buttons, so that the data exchange will occur when the button is clicked. And it is in these functions, we will call the set_data and get_data function we prepared earlier.

% in the target gui 
% --- Executes on button press in pb_retrieve.
function pb_retrieve_Callback(hObject, eventdata, handles)
% get the handle to the source gui 
source_handle = handles.source; 
% get the handles structure of the target gui 
s_handles = guidata(source_handle); 
% update string to the one returned by the get_data function 
set(handles.txt_tgt, 'string', s_handles.get_data(s_handles));

% in the source gui 
% --- Executes on button press in pb_send.
function pb_send_Callback(hObject, eventdata, handles)
% get the handle to the target gui 
target_handle = handles.target; 
% get the handles structure of the target gui 
t_handles = guidata(target_handle); 
% call the set_data function 
t_handles.set_data(t_handles, get(handles.txt_src, 'string'));


Now, if you run the 'main' script to have the two guis running at the same time and have their link established, you will be able to observe the string passing from the source to the target. The example can be downloaded here. Run the test_script.m file.

While the example used is a very simple two-gui with one-way data flow, the same concept can be expanded and applied to cases where you want one-to-many or many-to-many guis with two-way communications. As long as you can have a clear design before diving into the coding, things should be relatively straightforward.

Saturday, April 7, 2012

The reason why uicontextmenu is not working with imshow()

When you ask why the uicontextmenu is not working in an axes plotted with an image, most people will say that it is the hittest property of the image object. If the hittest property of the image object is set to 'on', any time you click the mouse within the axes, the image object, which covers the entire axes, will take over the click action and deal with it. Since you probably don't have anything associate with the image object, it will appear doing nothing at all. But at the same time you are expecting the uicontextmenu from the axes to do something. I know, drives you crazy.

If you create the image with the image() or imagesc() function, setting the hittest property of the image object to 'off' should fix the problem. Effectively, it allows you to click through the image and get to the axes.

However, if you create the image with the imshow() function, you are faced with the same problem, the uicontextmenu on the axes doesn't do anything. You can spend hours searching for a logical explanation on the Internet, and yet nothing turns up.

Well, hopefully the few hours I wasted can save you some time.

The reason is actually quite simple. One obvious differences between the images created from image() and imshow() is the ticks on the axes, while image() shows it, imshow() hides it. Now, think of the ways you can use to get rid of the ticks, you can set xtick and ytick to '[]' using set function, or you can just set the entire axes to invisible. Guess what MATHWORKS did? THEY SET THE ENTIRE AXES TO INVISIBLE, so even if you click through the image object, you won't be able to click on the axes BECAUSE IT IS INVISIBLE.

So here is the fix.
Besides setting the hittest property of the image object to 'off', you need to set 'visible' property of the axes to 'on'. Once you do that, the xticks and yticks will appear, but you can easily make them disappear by setting them to [].

Friday, April 6, 2012

Interactive drawing in MATLAB: Part 2

In the previous post, we look at how we can draw different shapes on an axes using context menu and the MATLAB build-in function such as imline, imrect, etc.

The nice thing about the build-in functions is that they are fully implemented, meaning that they can be resized, dragged, and they even have context menu on their own which allows one to change the color and some other properties.

However, one thing I hate about the functions is the little rectangle boxes appear as the end points. I understand they are there to show you where you can click to move the points, but they should at least provide an option to turn the rectangle boxes visible or invisible. I fail to see any benefits of having the rectangle boxes on the final print out. I tried and failed to find any ways to remove the rectangle boxes, so in this post, we are going to code our own function for drawing the different shapes, without the annoying rectangles. (TAKE THAT!!! MATHWORKS).

The context menu created in the previous post can be re-used here. Instead of calling the imline or imrect functions when the corresponding menu items are selected, we are just going to create our own function to draw the desired shapes. Note that at this point, we will not be worry about all the fancy stuff, such as the figure pointer, line color, ability to resize, etc.

Design of the action sequence:
1. User selects "Line" from the context menu of the axes, which activates the line drawing mode.
2. Whenever the button is pressed in the line drawing mode, a line object is created on the axes.
3. As the user moves the mouse on the axes while holding the button down, the line object is continuously updated with the current location as the end point.
4. Whenever the users release the button, the line object is finalized and the line drawing mode ends.

The draw_line function shown at the bottom can fulfill the action sequence described above. Note that we are using WindowButtonDownFcn, WindowButtonMotionFcn and WindowButtonUpFcn to track the various mouse actions, and the corresponding GUI response is realized using nested/sub functions. You can try out this function easily using the code "plot(rand(1,10)); draw_line(fh,ah);". To implement functions that draw other shapes, the same structure can be followed, and changes only have to be made for the drawing of the shapes, which can be complicated at times depending on what you want to draw. 

Video demo



The sample GUI can be downloaded here.


function draw_line(fh, ah)
% DRAW_LINE(FH, AH) draws a line on an existing axes interactively. 

% Input: 
%   fh - handle of the figure that contains the axes 
%   ah - handle of the axes to be drawn on 
%
% Wei Shang 
% wei.shang@unb.ca 
% University of New Brunswick 


% get the boundaries on the axes 
x_bounds = get(ah, 'xlim');  y_bounds = get(ah, 'ylim');
% enter line drawing mode by attaching buttonDown function
set(fh, 'WindowButtonDownFcn', @buttonDown);


    function buttonDown(fh, dummy)
        % This function is called when the mouse button is pressed. 
        % It will create a line object based on the location of where the
        % button pressed action occured. 
        
        % get the location of the action 
        p = get(ah,'currentpoint');
        x1 = p(1,1); y1 = p(1,2);
        % create a line object that starts and ends at the same point. 
        lh = line([x1,x1], [y1,y1], 'linestyle', ':');
        % atttach buttonMove function 
        set(fh,'WindowButtonMotionFcn',@buttonMove);
        % attach buttonUp function
        set(fh,'WindowButtonUpFcn', @buttonUp);
        
        function buttonMove(fh, dummy)
            % This function is called when the mouse button moved while
            % being pressed. 
            
            % obtain end points of the line 
            X = get(lh,'XData'); Y = get(lh,'YData');
            
            % obtain new points as the new mouse pointer location 
            p = get(ah,'currentpoint');
            x2 = p(1,1); y2 = p(1,2);
            
            % check and make sure they are within the bounds, if not force
            % the end point to be within the bound 
            if x2 < x_bounds(1)
                x2 = x_bounds(1);
            elseif x2 > x_bounds(2);
                x2 = x_bounds(2);
            end
            
            if y2 < y_bounds(1)
                y2 = y_bounds(1);
            elseif y2 > y_bounds(2)
                y2 = y_bounds(2);
            end
            
            % update the line object with the 
            X(2) = x2;   Y(2) = y2;
            set(lh, 'XData', X, 'YData', Y);
            
        end % ends buttonMove function 
        
        function buttonUp(fh, dummy)
            % This function finalize the line object and ends line drawing
            % mode. 
            
            % set line object's line style to solid line 
            set(lh,'linestyle','-');
            
            % set various mouse actions to null
            set(fh, 'WindowButtonMotionFcn', '');
            set(fh, 'WindowButtonUpFcn', '');
            set(fh, 'WindowButtonDownFcn', '');
            
        end % ends buttonUp function 
        
    end % ends buttonDown function 


end % ends draw_line function


Interactive drawing in MATLAB: Part 1

Being able to draw in the plots/images in MATLAB GUI can be very useful at times. 

MATLAB has a collection of Utilities for Interactive Tools which allows you to place points, lines, rectangles, ellipses, etc on to an axes, so be sure to check them out first to see if they meet your need. 


There are many ways to incorporate/initiate/activate the various drawing tools in MATLAB GUI. You can simply call the functions in the callback of the various uicontrols such as push buttons, drop-down menu, etc. 


In the example that I am about to show, I attach a context menu to the axes, which allows the user to select the desired shape and begin drawing.

1. Create a new MATLAB GUI using the GUI with Axes and Menu template.

2. In the GUIDE window, bring up the Menu editor by Clicking Tools -> Menu Editor.

3. By default, the menu window is in Menu Bar mode, select Context Menus tab at the button of the menu editor.

4. Create the menu items as follows
    a. create a top level menu and name* it cm_axes, this menu item is to be attached to the axes
    b. under the cm_axes menu, create a new menu item and name it cm_draw, this menu item acts as a container of all the possible shapes that you can draw.
    c. under the cm_draw menu, create a new menu item and name it cm_draw_line, once selected, this menu item should allow the user to draw a line
    d. again, under the cm_draw menu, create a new menu item and name it cm_draw_rect, once selected, this menu item should allow the user to draw a rectangle.
    e. repeat d to implement another other shapes that you would like to use.

* By naming a item, I meant changing the item's tag, not labels. The labels of the items can be anything as long as it makes sense.

5. Once the context menu is created, the next step is to attach the context menu to the axes. To do that, simply open up the property editor of the axes, find UIContextMenu, and select cm_axes from the drop down menu.

6. After saving the file, the various callbacks for the menu item should appear at the bottom of your m-file editor. For the menu items cm_draw_line and cm_draw_rectangle, simply insert the two functions imline and imrect, respectively.

7. Save all file, and run the GUI, right click anywhere on the axes to bring up the context menu.

Video walkthrough



Source File

Download the files here.


Tuesday, February 28, 2012

MATLAB: Making a zoom-in plot on top of a plot

Sometimes, it is helpful to provide a zoom-in of a portion of the original plot. While it can be easily done by making them two separate plots using the subplot function, there are times when you wish you can just make the zoom-in plot appear on top of the original plot. The good news is that it is possible, the bad news, well, it requires a work around, since there isn't a nice matlab function which you can simply call to have the job done for you.

The walk around itself is a very simple idea, you can create another axes on top of the original plot and have the zoom-in data copied over to the new axes. If you are doing it for one time only or the plots are very similar, then it may make sense just to hard code everything in and move on. However, in the case where the plots vary quite a bit, a function is probably needed to make it less of a pain.

FUNCTION DESIGN
Input(s):
  ah:  handle of the existing axes
  source_pos: the position of the portion to be zoomed in
  target_pos:  the position where the zoom-in plot will be placed

* To make the function easy to use, the positions as the input variables are based on the axes in the original plot.

Download the zoomPlot.m file from here here and run the following code to see the function in action.


figure; 
t = 0:0.001:0.1;
plot(t, cos(2*pi*50*t)); 
ah = gca; 
% location of the plot to be zoomed in. 
s_pos =[0.05 0 0.06 0.1];
% location of the zoom-in plot 
t_pos = [0.035 0.4 0.065 0.7];    

% generate a zoom-in plot. 
zoomPlot(ah, s_pos, t_pos);     


The resulting plot will look like



So what has been cover in this post is the basic idea of how to get a zoom-in plot to be on top of the original plot. There are many ways to make this basic function more robust and useful.
  • to allow user specify the positions interactively using mouse 
  • to allow user more flexibility in controlling the appearance of the zoom-in axes. 
  • etc. 




Friday, February 24, 2012

MATLAB: How to plot a circle

Ever wonder why there is no circle function when there is a rectangle function?

If you can draw a circle using rectangle function, why would you need a circle function?

The code below plots a circle centered at (x,y), with a radius r, using the rectangle function,

x = 0.5; y = 0.5; r = 0.5;
rectangle('position', [x-r,y-r, 2*r, 2*r], 'Curvature', [1 1]);

The neat thing about using the rectangle function is that you don't need to decide on a 'sampling rate'. The sampling rate is needed if you actually write a function that computes the samples on the circle and plot them as line objects. 



Tuesday, February 21, 2012

MATLAB GUI Inserting background image

Update [2014-04-13]: a function was created for inserting background image to MATLAB gui (or any figure for that matter). It can be found on github.

In many cases, you may wish to insert a background image to the existing GUI. Some of the uicontrols, such as pushbutton and toggle button, have the CData property, which you can set as background using data read from an image (see here). The gui itself, however, does not have such property, and a walk around is needed.

This tutorial shows how you can create an axes in the opening function of the gui and have the background image shown on the axes. To proceed, simply open up an existing gui (or create a new one), and insert the following code in the opening function of the gui.

% create an axes that spans the whole gui
ah = axes('unit', 'normalized', 'position', [0 0 1 1]); 
% import the background image and show it on the axes
bg = imread('example.jpg'); imagesc(bg);
% prevent plotting over the background and turn the axis off
set(ah,'handlevisibility','off','visible','off')
% making sure the background is behind all the other uicontrols
uistack(ah, 'bottom');

Note that the aspect ratio of the gui and the background image should match so that the appearance of the background image is optimized. 

Video walk-through 

MATLAB GUI: implementing drag-and-drop

In the last tutorial, we went over the design of the drag-and-drop functionality of a uicontrol. In this tutorial, we will be implementing the functionality using the code from earlier tutorial. For your convenience, the drag_and_drop function is included in the zip file.

Procedures: 

1. Download mouse5.zip and ready the files in a working matlab directory.
2. Using GUIDE, set WindowButtonMotionFcn, WindowButtonDownFcn, and WindowButtonUpFcn property of the gui to null. You may remove their corresponding functions in the gui_mouse.m file.
3. Attach/register drag_and_drop function to WindowButtonMotionFcn property of the gui in the opening function of the gui.

set(hObject, 'WindowButtonMotionFcn', @drag_and_drop);

4. Save all files and execute the gui.

Video walk-through


Files
Download source code


MATLAB GUI: Designing drag-and-drop interaction

The goal of the series is to design and implement a gui that allows the user to drag and drop an uicontrol in the gui. After all the basic forms of interactions covered in the previous tutorials, we are now ready to design the mouse interactions to meet our specific need.

The mouse has two states, up and down, and has three actions, pressed, moved, and released. However, since you can not press the button when the it is already down, or release the button when it is already up, we are left with only four scenarios to account for (as shown in Table 1 below).

Table 1. Mouse states and actions
State \ Action Pressed Moved Released
Up Pick up the uicontrol Update the pointer N/A
Down N/A Move the uicontrol Drop the uicontrol

Note: when the mouse is moved over a uicontrol, the ButtonDownFcn property of the uicontrol takes over the WindowButtonDownFcn property of the gui, thus, when the mouse is pressed within the uicontrol, ButtonDownFcn is called instead of WindowButtonDownFcn. Moreover, there is a specific set of rules for activating the ButtonDownFcn. It is not as simple as pressing the mouse button within the uicontrol (though, in theory, it should be that simple). For detail of the rules, check here

While it may still look complicated, the design of the drag-and-drop functionality is rather easy, mainly because of the fact that the different functions can be attached to the ButtonDownFcn property of the uicontrol, WindowButtonUpFcn and WindowButtonMotionFcn properties of the gui. Since the the same action can have two different outcomes depending on the state the mouse is in, one has to attach different functions to the properties accordingly. Below, we will go through the design using pseudo code. Note that some of the functions are coded as sub/nested function because they will never be called in any other places.


function drag_and_drop [attached to WindowButtonMotionFcn]
updates the pointer and ButtonDownFcn property as mouse moves in and out of the uicontrol hotspot  

   function grab [attached to ButtonDownFcn]
   updates the pointer to closed-hand shape, attach function drag to WindowButtonMotionFcn and attach function drop to WindowButtonUpFcn as mouse button is pressed 

      function drag [attached to WindowButtonMotionFcn ]  
      updates the object locations 
      end of function drag

      function drop [attached to WindowButtonUpFcn] 
      updates the pointer to hand-shape, re-attach function drag_and_drop to WindowButtonMotionFcn, remove handle to WindowButtonUpFcn. 
      end of function drop 

end of function grab

end of function drag_and_drop

Sunday, February 19, 2012

MATLAB GUI: changing figure pointer

In the fourth part of the series, we look at how to change the figure pointer to a hand shape to indicate the static text can be picked up. Since the hand shape figure pointer is not available in MATLAB, we will be using the setfigptr.m from File Exchange, which has both the hand shape and the closed-hand shape (and many other useful shapes).

Implementation wise, only two lines of code needed to added to the WindowButtonMotionFcn, one line will change the figure pointer to the hand shape when the mouse moves into in the hotspot, while the other line will change the figure pointer to the default pointer when the mouse moves out of the hotspot.

1. Download mouse3.zip and ready the files in a working folder in MATLAB.
2. The setfigptr.m file is included in the zip file. Note that as I am writing this post, the one listed on File Exchange has issues.
3. Open up gui_mouse.m file, go to WindowButtonMotionFcn and copy and paste the following line of code to the corresponding conditional block,

    % change to hand shape pointer     
    setfigptr('hand', handles.fig_mouse);


    % change back to normal arrow pointer 
    setfigptr('arrow', handles.fig_mouse);


4. Save files and run the gui. 


Video walk-through 




Source Files
Download the files here.

Thursday, January 26, 2012

Signals Virtual Lab (SVL) Suite demo (advanced)

A demo video is shown below to put the potentials of the SVL suite into prospective. The video shows the SVL suite being used in examining the signals sampling and reconstruction. One major improvement when compare to the SVL suite's first release is the fact that users can draw connections between devices as if they were using Simulink, however, the customized characteristics of the individual GUI remains. In other words, the SVL suite utilizes the connection functionality of Simulink, but retains the same GUI for each device.

In the video, the function generator output is fed to a signal splitter, with one splitter output going to the oscilloscope, and the other going into the sampling box. One by one, the sampling waveform and the sampled signal are examined using the oscilloscope. To reconstruct the original signal, the sampled signal then goes through a low-pass filter. Spectrum of the various signals are observed.

Note that as long as the function generator and oscilloscope are used, any Signals lab can be transformed into the virtual lab if the corresponding signal processing components are created.




Wednesday, January 25, 2012

MATLAB GUI: Finding the 'hotspot' (the missing mouseover function)

In the third tutorial of the series, we will look at a walk-around for the missing mouse over function for the uicontrols. We will be coding in the WindowButtonMotionFcn callback. Whenever the mouse moves in the figure, we will capture the mouse location, capture the boundaries of the uicontrol, and finally, determine whether the mouse is over the uicontrol or not. 


Step-by-step guide
1. Download mouse2.zip and ready the files in a working folder in MATLAB. 
2. Add a static text to the GUI, rename its tag to lbl_target and set its unit to pixel 
3. Copy and paste the following code to the end of the WindowButtonMotionFcn 

% get position information of the uicontrol 
bounds = get(handles.lbl_target,'position');
lx = bounds(1); ly = bounds(2);
lw = bounds(3); lh = bounds(4);
% test to see if mouse is within the uicontrol. 
if x >= lx && x <= (lx + lw) && y >= ly && y <= (ly + lh)
    set(handles.lbl_target, 'string', 'IN');
    set(handles.lbl_target, 'backgroundcolor', 'red');
else
    set(handles.lbl_target,'string', 'OUT');
    set(handles.lbl_target, 'backgroundcolor', 'green');    
end


4. Save all files and run the GUI. 


Video 




Files
Files can be downloaded here

Tuesday, January 24, 2012

MATLAB GUI: Tracking mouse actions

In the second tutorial of the series, we will look at how we can track the mouse actions such as pressing and releasing. We will start with the GUI we created in the first tutorial, and we will add a text field that details the last mouse action and its location.

Step-by-step guide

1. Download mouse1.zip, and ready the files in a working folder of MATLAB.
2. Open gui_mouse.fig using GUIDE.
3. Add a static text to the GUI and rename its tag to lbl_last_action
4. Open the property window of the figure (double click on the figure), click on WindowButtonDownFcn to populate the callback and paste the following code:
    pos = get(hObject, 'currentpoint'); % get mouse location on figure
    x = pos(1); y = pos(2); % assign locations to x and y
    set(handles.lbl_last_action, 'string', ['Mouse pressed @ X: ', num2str(x), ', Y: ', num2str(y)]);
5. Click on WindowButtonUpFcn to populate the callback and paste the following code: 
    pos = get(hObject, 'currentpoint'); % get mouse location on figure
    x = pos(1); y = pos(2); % assign locations to x and y
    set(handles.lbl_last_action, 'string', ['Mouse released @ X: ', num2str(x), ', Y: ', num2str(y)]);
6. Press F5 (or click on run button) to start up the GUI.

Anytime when the mouse is pressed (down) or released (up), the text field is updated with the action and the location of the action. Note that if the button is released quickly after pressed, the text field may be updated too quickly as if there is no press action.

Video 


Files: 

Files can be downloaded here. Note some changes have been made to the GUI to make it more presentable.

MATLAB GUI: inserting background image to push/toggle button

This is a brief tutorial on how to insert background image to push button and toggle button in MATLAB GUI. The idea is simple, read in the background image as a matrix, and then assign the matrix to CData property to the buttons.

1. Create a new GUI in MATLAB using GUIDE, and save the file.
2. Add uicontrols push button (or toggle button), rename their tag as pb (or tb).
3. In the OpeningFcn in the corresponding m-file, copy and paste the following code:

bg_image = imread('button.jpg');
set(handles.pb_with_bg, 'CData', bg_image);

Note, you may want to set pixel as the figure's unit so that you can easily figure out the size of the button that will show you the entire background image. In other words, the size of the button and the size of the background image should match.  


Video


Files
Source files can be download here.

Saturday, January 21, 2012

MATLAB GUI: Tracking mouse locations


In the first part of the series, we will create a simple GUI that will show the current position of the mouse on the GUI.

Step-by-step guide:
  1. Create new GUI using MATLAB GUIDE, save file as gui_mouse
  2. Create two static text fields and configure them as fellow
    1. rename tags to lbl_x and lbl_y
    2. set strings to 'x loc:' and 'y loc:'
    3. set FontSize to 18, and resize components 
  3. Configure figure property (double-click on figure)
    1. rename tag to fig_mouse
    2. change unit to pixels
    3. click on callback for WindowButtonMotionFcn and insert the following code
    pos = get(hObject, 'currentpoint'); % get mouse location on figure
    x = pos(1); y = pos(2); % assign locations to x and y
    set(handles.lbl_x, 'string', ['x loc:' num2str(x)]); % update text for x loc
    set(handles.lbl_y, 'string', ['y loc:' num2str(y)]); % update text for y loc 


  4. Save all files and run the GUI or M-file.
Note: anytime the mouse is moved over the GUI, it will trigger the WindowButtonMotionFcn on the figure, which will then update the labels with the current location of the mouse pointer on the figure. 

Video:




Files:

Download source files here

Friday, January 20, 2012

MATLAB GUI tutorial planned


I am planning to create a tutorial series on how to incorporate mouse movements and actions into MATLAB GUI. Traditionally, the GUIs are designed to accept user input via a variety of uicontrols (or components). While having the components are sufficient in most applications, there are still cases where the user experience may be drastically enhanced by utilizing mouse movements and actions.

The end-product of the series is a GUI that allows user dragging and dropping an object in the GUI. The series will contain a number of tutorials, each dealing with a specific subject (or sub-problem). Each tutorial will be built on the earlier tutorials, either provides a different functionality or a more advance usage of the same functionality (or both).

The series of tutorials may cover the following topics (subject to change)

Thursday, January 19, 2012

SVL Suite V1.0.0 available for download

My submission of SVL Suite to MATLAB Central File Exchange was rejected. The reason for the rejection is because my submission contains p-files. Having the p-files, which hides the source code, kind of defeats the purpose of File Exchange, which is to allow users to learn from each other by looking at the source code. It is fair, but I wish they had spelled it out in their Guidelines for New Submissions.

In any case, I believe SVL suite is a very useful application/learning tool and I am determined to at least try to give it a bit of exposure on the Internet. And therefore, I updated the project website I created for the SVL Suite (in 2010), and package up the first release for download. I also submitted the link of the project website to the Link Exchange on MATLAB Central. Let's see where it takes us.