Saturday, February 1, 2014

Multi-'screen' MATLAB GUI

Once again, I had little time cleaning up the post so read at your own risk. :-) 

Why bother?


First and foremost, the multi-screen GUI I am talking about is much like a Single Page Application (SPA) in the context of web application. Everything you see in the GUI can change entirely as you 'navigate' from "screen" to "screen". An example would be an application that has various stages to it, as the user goes from stage to stage, only the relevant information/controls will show on the GUI. Of course, you can always just slap everything on the GUI without considering splitting them into "screens", but you only have so much space on the GUI and every bit of information that shouldn't be there is wasting the UI space.

<begin digression>
Also, one can also implement the application using multiple GUIs instead of multiple screens. Such approach is more appropriate in the case where the different GUIs have to be shown at the same time, but there are distinct features about each GUI so it doesn’t really make sense to put them all in the same GUI, also, reusability is another issue. But that’s a different topic altogether.
<end digression>

Enough background. Below is how.

First of all, there is no such feature (multiple-screen GUI) in Matlab, but there is a little trick we can use to make it appear so, and that is with the use of 'uipanel'.

The concept is fairly simple, assuming you have already got a design in mind, i.e., you are clear about what uicontrols go into to what uipanel, all you need to do is have them populated in the right panels, subsequently, we show and hide the panels as we see fit. Note that the panels should have been resized at this point so they overlap completely, and at any given time, only one panel should be shown (Visible = 'on'), while others are hidden (isVisible = 'off'). (I don't understand why 'on'/'off' are used instead of true/false or 1/0.)

Now, I would strongly recommended creating everything programmatically instead of relying on Matlab’s GUIDE, as the panel will need to be resized, moved around, which is impossible to do in GUIDE. You also need to pay special attention to the unit setting for all the uicontrols when you are doing this, if there is any mis-matches or invalid values, then the uicontrols may not show up, or when you resize the GUI, the uicontrols in a panel may all go crazy.
Below is the link to a fairly simple demo that shows the multi-screen in action of the application. Three buttons to show one of the three panels (much like the tab effect if you just position them in a better way).
https://github.com/weishang/matlab-multiScreen

What else?


The trick described in this post is using overlapping panels as screens and show/hide the panels as you desired. Here’s what else you can do with this trick.
  • A demo application which goes from one screen to the next, much like a slide show. With the help of the timer object (may find this post useful). 
  • A GUI with the ‘tab’ effect 
  • A fully blown single page application 
  • ...

Saturday, August 31, 2013

MATLAB GUI and Timer Object

I shouldn't really started this post at all since I have little time to 'beautify' the post, but what's described in this post can be very useful for someone that needs to have the GUI update itself at a fixed time interval, say, plotting the latest forecast data. Note that it doesn't have to be a fixed timer interval, in fact if you put enough thought into this, the schedule for the updates can be very flexible (by using database, etc).

Be warned though, the quality of the post is lacking, but I will make sure the sample application is running as expected so all you need to do is to expand on it and plug in all the pieces you need/have.

If you are not familiar with MATLAB GUI, you should read up on it (and unfortunately this post is probably not for you). If you are fairly familiar with MATLAB GUI, but haven't used  the timer object much. Here is the documentation, it is best if you try out a few examples that uses timer object without the GUI, just to get a feel for it.

Now, without further ado, below is a walk through of creating a MATLAB GUI-based digital clock (from scratch).

1. Type GUIDE in matlab, and choose to create a blank gui.

2. Create a static text on the GUI, make it and its fontsize big, and tag it as lbl_timeNow

3. Save the GUI (with the name, say, myClock), and open up its m-file.

4. In the OpeningFcn (prefixed with whatever name), insert the following code

timer_obj = timer(...
    'StartFcn',         @user_timer_start, ...              % start function
    'TimerFcn',         {@user_timer_update, hObject}, ...  % timer function, has to specific the handle to the GUI,
    'StopFcn',          @user_timer_stop, ...               % stop function
    'ErrorFcn',         @user_timer_err, ...                % error function
    'ExecutionMode',    'fixedRate', ...                    %
    'Period',           0.1, ...                           % updates every xx seconds
    'TasksToExecute',   inf, ...
    'BusyMode',         'drop');


start(timer_obj);        % start the timer object

setappdata(hObject, 'timer_obj', timer_obj');  % save the timer object as app data

Effectively, we just created and started a timer whose TimerFcn gets executed every 0.1 second. And the timer is saved as the app data so when we try to close the GUI, we can easily get our hands on the timer and get rid of it at the same time. 

5. Implement the user_timer_update function (which is set as the TimerFcn of the timer). Insert the following function to the m file. Notice how it has three input parameters, the last of which was past in in step 5 with '{@user_timer_update, hObject},'.

function user_timer_update(src,evt, fig_handle)

handles = guihandles(
fig_handle);

set(handles.lbl_timeNow, 'string', datestr(now, 'yyyy-mm-dd HH:MM:SS'));


6. Put in the other timer functions, StartFcn, StopFcn, ErrorFcn into the m file. They are just dump text printer for now. 

function user_timer_start(src, evt)
disp('Timer started!');

function user_timer_stop(src, evt)
disp('Timer stop');

function user_timer_err(src, evt)
disp('Timer error');


7.  We are almost done, just need to make sure the timer is removed when we close the GUI. So go to the GUIDE editor, and select 'CloseRequestFcn' property of the figure, it should populate the function in the m file for you. Now, before delete(hObject), put in

stop(getappdata(hObject, 'timer_obj')); % stops the timer 
delete(getappdata(hObject, 'timer_obj'));  % delete the timer object 

8. Now, here is a list of things that you may want to do: 

  • make the gui look pretty 
  • properly implement the other timer functions, StartFcn, StopFcn, etc. 


Here is what it looks like and here are .fig and .m files. 



Well. That's all for now. Good luck. 

Saturday, July 27, 2013

Connecting to (local) MySQL database in MATLAB

If you try to connect to a local MySQL database based on the Matlab documentation on the database function (see connection example here), you are likely to get the 'Unable to find JDBC driver' error. 

To fix the error, first download the JDBC driver from MySQL website. 


Once download, unzip and find the mysql-connector-java-5.0.8-bin.jar (or more current version) file and move it to Matlab's java path ($matlabroot\java\jar\). 

Next, type prefdir in MATLAB and find your preferences folder, once in that folder, create a text file called javaclasspath.txt, and place the $matlabroot\java\jar\name_of_the_jar_file in and save the file, restart Matlab.

Now if you type javalclasspath, you should see the jar file appear at the bottom of the static path.

Finally, to test out if everything works as expected, run 

conn = database('mysql', 'root', 'DB_password', 'com.mysql.jdbc.Driver',  'jdbc:mysql://localhost/');

conn

tables(conn)


close(conn);

* May need to adjust database name, user account, password, driver name, and url of the database. 


Saturday, June 22, 2013

MATLAB Database Toolbox + MS Access + Date Comparison Problem

I was trying to get a date comparison into a simple select statement.

query = 'select * from telemetry where measured_at < `06/06/2012 11:25:00 AM`;';

I kept getting this "super" informative error message.

Error using database/fetch (line 37)
[Microsoft][ODBC Microsoft Access Driver] Too few parameters. Expected 1.

Error in get_DB_conn (line 17)
x = fetch(conn, query);

"Too few parameters". Hmm, I thought I called the function the wrong way. Then again, where statement with other fields worked just fine. Took me sometime to figure out this is a formatting issue. Apparently, I need to place 'special markers' around the date so that it knows it is a date. doh!

Changed to

query = 'select * from telemetry where measured_at < #06/06/2012 11:25:00 AM#;';

and it works fine now.

Maybe it is nice to have this piece of information emphasized somewhere in the documentation?

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.