GUI building with plt


Over the years I have created dozens of GUIs using Matlab, nearly all of which involved collecting and/or viewing data and interacting with the data or data collection process in some way, and I suspect the same is true for the GUIs that you need to create. The first GUIs I created were quite difficult, but as I built up my bag of tools each new program became easier and quicker to write. The key I found was to avoid re-inventing the wheel each time and the best way to do that was to create a series of "pseudo objects". A pseudo object is a collection of Matlab graphics objects embedded with features commonly needed in Matlab GUI applications. (I chose to call them pseudo objects to distinguish them from the graphics objects supplied in the standard Matlab environment.) These pseudo objects are combined into one file exchange submission called (for historical reasons) plt. My primary goal for plt is to make building GUI applications in Matlab easier, faster, and more fun while enabling you to create clearer, more concise code that is compatible across all Matlab platforms and versions.

The two main tasks in creating a GUI application are:
  1. Choosing the graphical elements and configuring the sizes and positions of these elements.
  2. Writing the code that enables these graphical elements to serve their intended purpose.
Matlab's GUI building tool (called Guide) helps a lot with the first task but contributes little to the second. However I've found that for all but the most trivial applications, the second task accounts for most of the frustrations and time spent. My strategy is to aid the second task by providing a rich set of the pseudo objects mentioned above. It may seem like this is a tall order for these new objects, but I hope the examples that follow will convince you that they can impressively reduce the amount of code you need to write. The current set of pseudo objects is merely a start. I plan to continue implementing new pseudo objects ... hopefully many of them conceived by Matlab users such as yourself.

A parallel goal is to make it easier to learn Matlab GUI programming by providing many well commented examples that demonstrate as many of the pseudo object features as possible. It's easier to begin a new GUI application by starting with an example that has at least some of the graphical features that you need. To this end, plt includes 26 example programs covering a wide variety of GUI features and programming techniques. New example programs have been steadily added to the list over the years since the first version of plt, often initiated by questions and requests sent to me by plt users. Although the standard Matlab plotting and graphical elements are thoroughly documented, a common complaint is that this information is spread out over thousands of pages of Matlab documentation making it difficult to find what you are looking for. Also it is difficult to find examples for most features. This inspired me to design the plt help file to avoid these pitfalls by organizing plt's many features into one coherent help file including many examples. Every question from a plt user leads me to reexamine the documentation to see if I have described each feature and example as completely and clearly as possible.

Although the first task mentioned above (configuring sizes and positions of graphical elements) is not where most of the time is spent, without an appropriate tool this could be a painstaking task. Matlab's Guide tool does provide a reasonable solution to this problem however I found several annoyances with this tool:
  1. Guide forces me to adopt a particular programming methodology and style. Although plt offers unique pseudo objects you are free to use all or none of them to suit your purpose and no demands are made on your programming style. My preferences lean towards conciseness and clarity, as you can see from my programming examples.
  2. Guide has evolved over the years and works somewhat differently in different Matlab versions which can cause compatibility issues. I often support my Matlab applications for use with older Matlab versions, some of which were released even before Guide was invented.
  3. I sometimes find it inconvenient that the program definition is split between the .m and .fig files. The .fig file format can also lead to Matlab version dependencies. (GUIs designed with plt don't depend on .fig files.)
  4. And most importantly Guide was not compatible with the powerful pseudo objects that I had created.
In addition, to the Guide compatibility issues mentioned in point 2 above, the graphics objects themselves have changed between versions. For example the latest Matlab version (R2014b) finally allows you to set the grid line color independently allowing you to create far more pleasing plots. However this won't help when you try to run your program on previous Matlab versions (which in fact represent the majority of the user base). On the other hand, plt is designed to work the same across all Matlab versions, so you can share your code with colleagues running older Matlab versions. I have tested plt on most Matlab releases dating as far back as June 2001 (Matlab 6.1) and of course it works with Matlab's most recent R2014b version as well. You might think that supporting older versions would limit plt's flexibility, however as you will see plt generally meets or exceeds the flexibility available with even the latest native Matlab plot commands (including grid lines of course).

After abandoning Guide for the above reasons, at first I simply entered position coordinates by hand, often iterating many times to adjust controls to achieve the desired look. But for complicated GUIs with many objects, this is too time consuming. This led me to develop a more automated method of positioning pseudo objects as well as native Matlab objects without using Guide. This method is not quite as automated as Guide, but I hope you will see from the following examples that it strikes a good balance between automation, power, and flexibility. The basic idea was to give plt the the ability to reposition and resize the graphical objects and to display the results in a way that allows you to copy the positions of all your objects into your program with a single cut and paste.

Since the pseudo objects are the key innovation that simplifies Matlab GUI programming, let's briefly summarize the nine pseudo objects that have been implemented so far: A more complete description of these pseudo objects from a programming perspective can be found here: Pseudo objects.

Now you are prepared to dive into the examples that follow:

A first example

Our first GUI example doesn't do any plotting or anything else useful for that matter, so it may not present a compelling case for the GUI tools provided by plt. However since it consists of just a few dozen lines of code it is simple enough that you can quickly see how to use plt to arrange graphic elements inside a figure window.

I find that it is best to start working on a new GUI with pencil and paper. Imagine the control types and arrangement for the application and then sketch a mock up such as this. Your finished application rarely will look much like your first sketch, but with the rapid prototyping possible with Matlab and plt you can quickly iterate improvements in form, concept and implementation.

Here I have decided on 3 pseudo sliders across the top followed below by a uitable on the left and a frame on the right containing 4 uicontrols (a popup, slider, button, and checkbox).

The bottom part of the GUI consists of two large objects for displaying lists of numbers. The right most one is a simple text box with room to show about 10 lines of text. The left most one is a listbox which by virtue of its scroll bar can display a far larger data set (80 lines of text in this example).

First we create the figure window. I usually start by typing "figure" in the command window and adjust the figure size to get a first guess. In this example I decide on 430 by 350 pixels. The menu bar is not needed for such a simple GUI so the menu property is set to 'none'. I chose a dark blue-green for the figure background:

function gui1(in1)  
  figure('name','gui1','menu','none','pos',[60 60 430 350],'color',[0 .1 .2]);

The next line defines the choices for the popup menu. Then we create the array which contains the positions for the three pseudo sliders followed by a single position which is (initially) used for all the uicontrols as well as the uitable. Note that all three slider positions are exactly the same (in the middle of the figure). This is easy and convenient at the moment, but means that all three sliders will appear on top of each other. Not a problem however, as it will be easy to use the mouse to move and resize the sliders to an appropriate position. We will use the fourth (and last) position in this array for all the uicontrols. So of course all these controls will also appear on top of each other, but again we will use the mouse to move them to the desired locations.

  cho = {'choice A' 'choice B' 'choice C'};         % choices for popup control   p = {[.5 .5]; [.5 .5]; [.5 .5]; [.1 .1 .1 .1]};   % initial positions: Slider1; Slider2; Slider3; All uicontrols

Next, the three pseudo sliders and the uitable are created. @CBsli, the last parameter of the slider call, specifies the callback - a function that will be called when the slider value is changed. Note that we save the handles of these four objects even though we don't really use them. (In a real GUI we would almost always need them.)

  h1 = plt('slider',p{1}, 10,'PseudoSlider 1',@CBsli);  % create the pseudo sliders
  h2 = plt('slider',p{2}, 60,'PseudoSlider 2',@CBsli); 
  h3 = plt('slider',p{3},800,'PseudoSlider 3',@CBsli); 
  h4 = uitable('units','norm','pos',p{4});              % create the uitable

Since the uitable hadn't been invented yet for Matlab 6, there is an alternate version of gui1.m in the demo folder called gui1v6.m where this uitable is replaced by a radio button. Of course these two objects don't serve the same function, but since we aren't worried yet about functionality with this example, this is not a problem.

Next, let's create all seven uicontrols in a single line while collecting the handles in a variable named "h". (A long variable name I know ...) The spaces after the "h = " are there so that the property values (style, string, and callback) in the following three lines line up under the respective uicontrol command. This makes it easier to follow what is going on.

  h =            [uicontrol uicontrol uicontrol uicontrol uicontrol uicontrol uicontrol]; 
  set(h,{'style'},{'frame' ;'popup';  'slider';'pushb'  ;'checkbox';'listbox';'text';},...; 
       {'string'},{'frame1'; cho   ;  'slider';'button1';'check001';''       ;''     },...; 
       { 'callb'},{''      ; @CBpop;  @CBsli  ;@CBpush  ; @CBcheck ;''       ;''     },...; 
       'backgr',[.5 1 1],'units','norm','pos',p{4});

Finally we save all 11 handles in the figure user data so the callbacks can easily find them (h1,h2,h3,h4 followed by the seven uicontrols we just created). This method works fine for such a simple program, but in the next example we will see the advantages of using a structure for this purpose instead of an array. We also execute the callback function to initialize the random data tables:

  set(gcf,'user',[h1 h2 h3 h4 h]); CBsli; % save the handles and execute the slider callback

That's it for the main function, just 14 lines of code! (Although we will add a few more lines later.) We finish up by writing the control callbacks. The first three are just stubs to remind us to eventually put some useful action there. The last one (the slider callback) is the only one that does anything, which is to update the data tables with new random data. The reason the random numbers are in the exponent is to create numbers with a widely varying magnitude so that the table looks more interesting. (The same random numbers get put into both the textbox and the listbox). Note that the random numbers are converted to strings using "prin" a substitute for "sprintf" that includes features commonly needed in GUI programming. (prin.m and its documentation prin.pdf are included with plt.)

function CBpop(a,b)   % popup callback -------------
  disp('popup callback'); 
function CBcheck(a,b) % checkbox callback --------- 
  disp('checkbox callback'); 
function CBpush(a,b)  % button callback ------------- 
  disp('pushbutton callback'); 
function CBsli(a,b)                     % slider callback ----------------------------- 
  h = get(gcf,'user');                  % get the object handles
  t = 1e20.^(rand(3,80))/1e6;           % generate the random data
  set(h(10:11),'fontname','courier',... % convert random data to a cell array
    'string',prin('3{%6V }~, ',t));     % of strings for the listbox and the textbox
  set(h(4),'data',100*rand(3,2));       % more random data for the uitable
 


Now that we are done with the coding, start the GUI by typing gui1 in the command window. The figure window on the left will appear. As we mentioned above, the uitable and all seven uicontrols are on top of each other near the lower left corner, so we only see the last one. Likewise all three pseudo sliders are also on top of each other in the middle of the figure. Now its time to fix this problem. Type plt move in the command window to enable the mouse driven repositioning mode.

The graphical objects inside the current figure are grayed out to indicate that the repositioning mode is active. Now we can:

        left-click, hold, and drag to move an object.
        right-click, hold and drag to resizes an object.
        Double click to open an object's property inspector window.

At the stage shown here we at least have moved and resized all the objects so we can see all the individual items, none of them overlap, and they are at least close to the positions we outlined in our sketch.

I notice that the frame is too bright since I intended it to be a subtle grouping. One way to adjust this is by typing commands into the command window. After clicking on an object (in repositioning mode) a variable called "hhh" is added to the base workspace containing the object's handle. So now I can experiment with colors or other properties by typing commands such as:

set(hhh,'backgr',[1 1 2]/6,'foregr',[1 1 1]/2);.

Once I get the look I want I can copy and paste the command from the command window into the program (right before the line that saves the handles to the figure userdata):

set(h(1),'backgr',[1 1 2]/6,'foregr',[1 1 1]/2);.

Note that I changed the "hhh" to h(1) before inserting the line into the program. A second method for doing this (instead of typing the commands in the command window) is to double click on the object which will bring up the property inspector for that object. Then as you change the property values in the inspector you will immediately see the effect on the GUI. (The second method is certainly easier if you don't know the exact property names for the objects you are working with.)

With a little more rearranging we finally get the look we are aiming for as shown here. Note that the controls are still grayed out since the repositioning mode is still active. One thing that you will notice while repositioning objects is that moving the frame also moves all the objects inside the frame. Unless you have a very old version of Matlab you may also use a uitable to allow this grouping effect. The main advantage of the uitable is that you can optionally specify a label for the grouping that will appear along the top edge. (The second example demonstrates this.)

Before exiting the program or canceling the repositioning mode click on each of the graphical objects once in the order that you created them in the program (left to right and top to bottom in the GUI). As you are clicking each item, plt will be displaying the positions of the objects in the command window. When you are done the command window will contain something similar to this:

uic: 207  .540 .500 .440 .280;  % frame1 
uic: 206  .680 .710 .170 .050;  % choice A 
uic: 205  .570 .610 .380 .060;  % slider 
uic: 204  .570 .520 .170 .060;  % button1 
uic: 203  .780 .520 .170 .060;  % check001 
uic: 202  .020 .050 .480 .400;  % 1.79e9   8.4e-6   2.5e12 
uic: 201  .540 .050 .440 .400;  % 1.79e9   8.4e-6   2.5e12 
sli: 401  .020 .920 .300;       % PseudoSlider 1 
sli: 406  .350 .920 .300;       % PseudoSlider 2 
sli: 411  .680 .920 .300;       % PseudoSlider 3 
uit: 208  .020 .500 .480 .280;  % uitable 

The first column is a three letter identifier for the object type. The native Matlab types - uicontrol, uitable, uipanel, axis, and text are identified as uic, uit, uip, axi, txt respectively and plt's pseudo objects are identified as sli, edi, pop, and xy. (xy refers to elements created by the cursor and plot pseudo objects.) The next column is a unique integer associated with the object. We won't need that now, but example 2 will show how that is used. The next four columns (or three columns for the pseudo sliders) specify the size and position of each object in normalized units. At the end of the line, the object type or string property of the object is included as a comment to make it clear which object the line refers to.

Now we are going to make the hard work we did to reposition the controls permanent. First cut and paste the 11 lines of coordinates from the command window (shown above in blue) directly into the gui1.m source code.


Then we fix up the 11 imported lines by deleting everything except the coordinates and comments, and adding brackets as appropriate. I've also made slight changes to the comments for clarity. I find it handy to turn on my editor's keystroke recording while fixing up the first line. Then I can replay this record to fix up the remaining ten lines by hitting the "play macro" button ten times. Most editors have this feature ... but even if you do each line manually, it's not a big deal.


Finally, in this line where we are setting the position of all seven uicontrols to the same value ... we have to change that (as shown here) so that the last seven entries of the position array are used in sequence.


Hit save on your editor, close the gui1 figure window, type "gui1" in the command window to restart it ... and as we hoped the GUI appears just as we had organized it. Try expanding the size of the figure window and note how all the objects grow in proportion to the figure size. This is because we used normalized coordinates throughout. If you want to convert this GUI to pixel coordinates, enter repositioning mode, type set(findobj(gcf),'units','pix'), and then click on every graphics object, again in the same order as they are defined. Again, cut and paste the coordinates (which now are integer pixels) into the program as we did before. Also remove the two instances of 'units','norm' (since pixels is the default when the units aren't specified). When you run it, the GUI will at first look the same, but when you stretch the figure size, all the objects will stay exactly the same size in the same position, thus creating empty space inside the figure. Generally you will stick with either pixels or normalized coordinates, although you can mix them if it suits your purposes.

This concludes our example, although if this was a real GUI you would not likely be satisfied yet. But using the methods we just demonstrated you will be able to iterate until you are satisfied with the control types and positions.

A second example

Now that we have covered most of the basic concepts and techniques, its time to explore the true power of plt by reviewing the design of a real GUI in one of the application areas that Matlab was designed for. The application I have chosen is the display and analysis of the classical analog filters. Granted this is not a particularly novel idea as it probably has been done before in Matlab and other languages, but nonetheless it serves various educational and practical needs and there is always room to apply our own slant to the project. I'll start out with a relatively modest set of goals:
As is my habit, I start with a sketch to clarify my thoughts. I decide to use an array of four pseudo sliders along the top to control the continuously adjustable parameters (edge frequencies & ripple) The four remaining filter and display parameters are grouped to the left of the sliders inside a uipanel unimaginatively labeled "Parameters". The Trace IDs to the left of that will be named after the classical filter types and be used to select which filters to display. The plot and the cursor controls and readouts along the bottom edge are the standard ones created by the plt pseudo object.

Ok ... it's time to start writing code. First I have to come up with some first guess for the object positions, and then I define the choices for filter type and number of points to display:

function gui2()
  p = {[.4 .3 .5 .5];  % plot position
       [.2 .5 .1 .2];  % uipanel position: Parameters
       [.2 .5 .1 .2];  % edit position: filter order
       [.2 .5 .1 .2];  % popup position: filter type
       [.2 .5 .1 .2];  % popup position: # of decades
       [.2 .5 .1 .2];  % popup; position: # of points
       [.2 .2      ];  % slider position: Passband ripple
       [.2 .2      ];  % slider position: Stopband ripple
       [.2 .2      ];  % slider position: Cutoff frequency
       [.2 .2      ]}; % slider  position: frequency 2
  typ = {'low pass' 'high pass' 'band pass' 'stop band'};  pts = 100*[1 2 4 8 16]; 

So how good are my guesses? Look at the first screen shot below to find out. Clearly not so good. The plot is way too small compared to my sketch. All the pseudo sliders are on top of each other ... and they are near the bottom instead of near the top as shown in the sketch. Plus all the pseudo popups and edit objects are also on top of each other. But as you will see, this will not be a problem at all. In fact, it would be a waste of time to spend more than a minute coming up with the initial guess. Ok, now it is time to create the plotting pseudo object (figure, axis, cursor, grid, traces, etc):

  S.tr = plt(0,zeros(1,5),'Options','LogX','Ylim',[-80 10],...  
            'TraceID',{'butter' 'bessel' 'cheby1' 'cheby2' 'elliptic'},...  
            'xy',p{1},'LabelX','radians/sec','LabelY','dB');  

The 'xy' parameter is used to position the plot within the figure window (although you will soon learn that this parameter can do far more than that). The data to be plotted for all 5 traces is defined in the plt call (as it must), but notice that each trace just contains the single point (0,0). When calling plt from the command line, you almost always include the actual plot data in the argument list, however in a GUI more often than not the data supplied is just a place holder. The real data is loaded later (in the callback in this example) by using the trace handles returned by plt. Note that we save these handles in S.tr (a 1x5 array). S is the structure where we will store the handles of all the objects we define in the GUI. The remaining plt parameters should be reasonably self explanatory. Next we create the uipanel and the four pseudo objects that we will put inside it:

  uipanel('units','norm','title','Parameters','backgr',get(gcf,'color'),... 
                 'pos',p{2} ,'high',[.4 .4 .4],'foregr',[.4 .4 .4]); 
  S.n   = plt('edit',  p{3} ,[6 1 25],'callbk',@clb,'label',{'Order:' .05}); 
  S.typ = plt('pop',   p{4} ,typ,'callbk',@clb,'swap'); 
  S.dec = plt('pop',   p{5} ,1:5,'callbk',@clb,'index',3,'label','Decades:'); 
  S.pts = plt('pop',   p{6} ,pts,'callbk',@clb,'index',2,'label','Points:'); 

For the uipanel, I set the background color to be the same as the figure color to give it a transparent look. For both the border outline and the text label of the uipanel I used light grey (rgb = .4 .4 .4). The uipanel wasn't invented yet for Matlab 6, so there is an alternate version of gui2.m called gui2v6.m in the demo folder where this uipanel was replaced by an axis. Note that the tag property of the axis was set to 'frame'. This is to tell plt that moving the axis in repositioning mode should also move all the objects inside it (even objects not children of the axis).

The [6 1 25] parameter of the pseudo edit object means that its initial value will be six with min/max limits of 1 and 25. The string 'Order' is used as a label for the edit object, and the ".05" tells it how much space to allocate for the label (in normalized coordinates). The parameters for the three pseudo popup objects are probably more obvious, but if not, consult the Pseudo objects page. Next we create the pseudo sliders:

  S.Rp  = plt('slider',p{7} ,[ 2  .01   9],'Passband ripple', @clb); 
  S.Rs  = plt('slider',p{8} ,[ 40  10 120],'Stopband ripple', @clb); 
  S.Wn  = plt('slider',p{9} ,[.02 .001  1],'Cutoff frequency',@clb,5,'%4.3f 6 2'); 
  S.Wm  = plt('slider',p{10},[.2  .001  1],'frequency 2',     @clb,5,'%4.3f 6 2'); 

The [2 .01 9] on the first slider has the same meaning as the similar pseudo edit parameter mentioned above - i.e. 2 is the initial value with min/max limits of .01 and 9. The @clb specifies the callback function. (Note that same callback function is used for all the controls.) The "5" after the callback function indicates that the slider will move logarithmically (so for example the slider will move the same number of pixels going from .01 to .1 as it does when changing from .1 to 1. The final parameter '%4.3f 6 2' is shorthand for '%4.3f %6v %2v' and specifies the display format for the min value, current value, and max value respectively. Now we have just a few more lines left to complete the gui2.m function:

  set(gcf,'user',S); 
  clb; 
% end function gui2 

The first line saves the handle structure in the figure user data where the callback function can easily retrieve it. The next line (the last of the gui2 function) executes the callback function to initialize the display to agree with the initial values of the controls. After just 17 lines of code, we're finished writing the main line function, plus 10 more lines for our initial guess for the control positions. But now the real work begins - the callback function that makes the GUI come alive:

function clb() % callback function for all objects; 
  S = get(gcf,'user');                                  % get handle structure
  ty = plt('pop',S.typ);                                % get filter type index
  t = {'low' 'high' 'bandpass' 'stop'};  t = t{ty};     % get filter type name
  N  = plt('edit',S.n);                                 % get filter order
  dec = plt('pop',S.dec);                               % get number of decades to plot
  pts = str2num(get(S.pts,'string'));                   % get # of points to plot
  X  = logspace(-dec,0,pts);  W = X*1i;                 % X-axis data (radians/sec)

First we pick up the filter parameters that are inside the uipanel (filter type, order, number of points, number of decades). You might wonder why I seem to repeat myself by defining the filter types again since it would seem more logical to simply get the filter type with get(S.typ,'string'). That command would retrieve one of the following strings: {'low pass' 'high pass' 'band pass' 'stop band'}, but the strings accepted by the Matlab filter functions are slightly different: {'low' 'high' 'bandpass' 'stop'}. It would have been much easier just to use the strings that Matlab requires for the popup control, but I was too picky about the look of the popup control to use those somewhat inconsistent strings. Finally the logspace command generates the requested number of points logarithmically spaced between .001 and 1 (for the 3 decades example). W is this same vector on the imaginary axis, which is used with polyval to compute the frequency response function.

  Wn = plt('slider',S.Wn);                        % get filter freq
  Rp = plt('slider',S.Rp);                        % get passband ripple
  Rs = plt('slider',S.Rs);  Rs2 = max(Rp+.1,Rs);  % get stopband ripple (must be > passband)
  if ty>2 Wn = [Wn plt('slider',S.Wm)];           % get frequency 2
          plt('slider',S.Wm,'visON');             % make frequency 2 slider visible
  else    plt('slider',S.Wm,'visOFF');            % make frequency 2 slider invisible
  end; 

Next we pick up the filter parameters from the four pseudo sliders. Note that for the last two filter types (bandpass and stopband) we need the second frequency slider ("frequency 2") and so this slider is only visible when one of those filter types is selected.

  [B,A] = butter(N,Wn,t,'s');        H{1} = polyval(B,W)./polyval(A,W); 
  [B,A] = besself(N,Wn(1));          H{2} = polyval(B,W)./polyval(A,W); 
  [B,A] = cheby1(N,Rp,Wn,t,'s');     H{3} = polyval(B,W)./polyval(A,W); 
  [B,A] = cheby2(N,Rs,Wn,t,'s');     H{4} = polyval(B,W)./polyval(A,W); 
  [B,A] = ellip(N,Rp,Rs2,Wn,t,'s');  H{5} = polyval(B,W)./polyval(A,W); 
  if ty~=1 H{2}=H{2}+NaN; end;         % bessel filter only applicable for low pass

Then we use the Matlab classical filter functions to compute the numerator and denominator s-plane polynomials (B,A) and compute the frequency response using polyval. Although it would have been slightly shorter to use freqs() instead of polyval(), I didn't do that since freqs is part of a toolbox that some users will not have. If a filter type other than low pass is selected, the last line changes the Bessel transfer function to "NaN" so that the trace will not appear on the plot. (The Bessel filter is only defined for low pass.)

  for k=1:5 set(S.tr(k),'x',X,'y',20*log10(abs(H{k}))); end; % set trace data
  plt('cursor',-1,'xlim',X([1 end]));                         % set Xaxis limits
% end function clb 

Then we use the absolute value function to compute the magnitude of the frequency response, and convert to dB (20*log10) before placing the result in the y-axis property of the 5 traces. Finally to set the x-axis limits in case they have changed (which happens when the callback is in response to the "number of decades" control).

Finally we are done with the initial coding and we can try it out. Typing "gui2" to start the program brings up this figure. Although all the controls are there as promised, they are not anywhere close to being in the right place, but it will take only a few minutes to fix this. Begin by entering "repositioning mode" by right-clicking on the delta button (or if you prefer, by typing plt move.) Then as I described in the previous example, use a left click and drag to move the objects around and a right click and drag to resize them.


I still don't have the final positioning, but it's close. The uipanel contains the controls it should and the other objects are also at least in the vicinity of where they should be. Note that once objects are placed inside the uipanel, moving the uipanel will also move all the objects inside it. After a few more tweaks, we will have at least our first cut positioning. Before closing this figure it is important to remember to left-click once on every object, in the order that they were created in our program. As we are doing this, the text below will appear in the command window:

 xy:   1  .130 .105 .840 .760;  % axes 
uip: 213  .100 .885 .240 .110;  % uipanel 
edi: 211  .165 .935 .040 .030;  %   6 
pop: 102  .110 .710 .100 .200;  % band pass 
pop: 103  .310 .750 .020 .200;  % 3 
pop: 104  .287 .710 .054 .200;  % 200 
sli: 401  .350 .946 .150     ;  % Passband ripple 
sli: 406  .510 .946 .150     ;  % Stopband ripple 
sli: 411  .670 .946 .150     ;  % Cutoff frequency 
sli: 416  .830 .946 .150     ;  % frequency 2 

Then as before, we copy and paste those lines into the source code as shown here.


We could fix up the brackets and comments line by line as we did in the previous example, however since my editor has a column select mode (as pretty much every programmers editor does) I find it easier to block delete the old coordinates (our rough first guess) and then do a block move (as shown by the red arrow) the new coordinates into the blank array.

We're done with the repositioning step, so we hit save in our editor, restart the application and we should see a figure similar to the one below.


Now we can play with all the controls and make sure everything is behaving as we imagined. Not bad considering we've written a non-trivial GUI applications involving non-trivial filter computations by writing only 51 lines of code. (Fewer if you don't count the automatically generated table of numbers that specify the object positions.) It's the power of the pseudo objects that allows the program to be written so quickly and concisely.

Of course what nearly always happens the first time you get to experiment with your GUI is that you will have some new ideas:
Indeed when I got to this stage of testing the application I did have a few enhancement ideas:
  1. I was curious (mostly for the elliptic filter) how the width of the transition band (the space between the passband and the stopband) varied as the filter order changed as well as the four slider parameters. Could I define such a measure, figure out how to compute it and find a place on the GUI to display it?
  2. My second idea was to allow the user to control at least some aspect of the color choices used in the application. Actually I don't think such a simple application like this really needs this flexibility, but my ulterior motive was to showcase the ColorPick pseudo object and how easy it is to add to your GUI and how easy it is to select the color you find most pleasing for any display element.
  3. What does the phase response of these filters look like? Could I add a display of the phase response without cluttering up the plot or obscuring the magnitude response (which is still the primary interest).
  4. It would be nice if whenever we made a change to the figure size/position or the color selection, that these changes would be recorded so that the application looks the same the next time it is restarted. While we are at it, we might as well remember the state of the eight filter parameters (shown above the plot) so that on start up, the figure looks identical to the way it was when it was shut down.
  5. Finally lets add a very brief set of help messages to allow a new user of the program to get started without having to consult any help files or manuals. The most important consideration should be that the help messages are not distracting in any way to the user who is already familiar with the help information presented.
These enhancements turned out to be fairly easy to implement. You can look at the final code which includes these enhancements (gui2.m in the plt\demo folder) or read on to find out more about the process.

1.) Adding a multi-line text string (elliptic transition ratio)


For a low pass filter, I characterized the transition width in terms of the ratio of these two frequencies:
        The frequency where the stop band spec is first achieved
        divided by the last frequency where the passband spec is still achieved
For a high pass filter, the ratio is:
        The frequency where the pass band spec is first achieved
        divided by the last frequency where the stopband spec is still achieved

I added an 11th line to the position array at the beginning of the program, to define the location for the new text object. Initially it was just a wild guess as usual which was refined using the repositioning mode:

  [-.09 .650          ]; % text position: elliptic transition ratio

The text object was created with this line (added after the slider definitions):

  S.etr = text(0,0,'','pos',p{11},'units','norm','horiz','center','color',[.2 .6 1]); 

Note that the string to display was set to null, because the actual string to display will be set in the callback function as follows:

  h = find(get(S.tr(5),'y') < -Rs2);                      % find indices where the stopband spec is satisfied
  if     isempty(h)                    h = 0;             % stopband specification not achieved
  elseif (ty-2)*(ty-3)                 h = X(h(1))/Wn(1); % computation for lowpass & stopband filters
  else    h = find(diff([h inf])>1);   h = Wn(1)/X(h(1)); % computation for highpass & passband filters
  end; 
  set(S.etr,'string',prin('Elliptic ~, transition ~, ratio: ~, %5v',h)); 

This last line takes advantage of prin's cell array delimiter feature to create the multi-line string used to display the elliptic transition ratio in the small space available on the left side of the plot. To learn more about prin and the %v format used here, check out the Auxiliary functions.

2.) Selecting colors


This was one of the simplest of the five enhancements requiring just the three extra lines shown below. I decided to enable color adjustment of just the pseudo sliders (the most prominent controls), although it would be easy to extend this to other graphic elements. In this figure I have changed the background color of the sliders from its default gray to orange. I encourage you to play around with this ColorPick figure. (Just right-click on any of gui2's pseudo sliders to bring up ColorPick.) If you have ever dealt with the frustrations of assigning screen colors, I think you will be pleasantly surprised about how easy it can be. Also you can read about the ColorPick details near the bottom of this page: Pseudo objects

The first line (below) gets puts the handles of all the objects associated with pseudo sliders into "h". Then the 5th element of each slider is removed, since that is the edit box portion which generally is set to a contrasting color. The second line assigns the ColorPick object as the buttondown function (the action associated with right clicking on the pseudo slider). The third line is necessary to tell ColorPick which property of these objects should be adjusted when a new color is chosen (the background color in this example).

  h = getappdata(gcf,'sli');  h(5:5:end) = []; 
  set(h,'buttondown','plt ColorPick;'); 
  for k = 1:length(h) setappdata(h(k),'m',{'backgr' h}); end; 

3.) Linking traces (adding the phase display)

To add the phase display, the most important change is in the callback function. Before we set trace data for 5 traces, but now we must set trace data for 10 traces (the first five for magnitude and the last five for phase):

  for k=1:5   % set trace data
    set(S.tr([k k+5]),'x',X,{'y'},{20*log10(abs(H{k})); angle(H{k})*180/pi}); 
  end; 

Then we just need to increase the data array defining the traces in the plt call from 5 to 10, specify that the last 5 traces should be on the right hand axis ... and we would be done. However then we would need 10 TraceIDs up there as well. I didn't want that because then to enable or disable the trace for the cheby1 filter (for example), I would have to click on two TraceID tags. Not so convenient. Also by default, ten different colors would be chosen for the ten traces. This would make it more difficult to tell which phase trace was associated with which magnitude trace. Both these problems are fixable of course:

  c = [0 1 0; 1 0 1; 0 1 1; 1 0 0; .2 .6 1]; % trace colors
  lbl = {'dB' [blanks(70) 'Phase \circ']};   % y-axis labels: {left, right}

The first line above defines the trace colors that also happen to be the default colors normally used for the first five traces. Only we are going to use them below for both the first five traces as well as for the last five traces. The next line defines the y-axis labels for both the left axis (magnitude response in dB) and for the right axis (phase response in degrees). Note the Tex command "\circ" in the right axis which inserts a small circle (the degree symbol) into the label. The 70 blanks that are inserted in front of the right hand label is used to push the label up towards the top of the display where the phase information will be plotted. And finally we have to fix up the main plt call:

  S.tr = plt(0,zeros(1,10),'Right',6:10,'Options','LogX',... 
          'DualCur',-5,'TraceID',{'butter' 'bessel', 'cheby1'  'cheby2' 'elliptic'},... 
          'Ylim',{[-90 60] [-1000 200]},'LabelX','radians/sec','LabelY',lbl,... 
          'TIDcback','t=plt("show"); t=t(find(t<6)); plt("show",[t t+5]);',... 
          'xy',p{1},'TraceC',[c;c],'+Ytick',-140:20:0,'-Ytick',[-180 0 180]); 

Some of the parameter changes in the plt call were already mentioned, but some others merit mention:

4.) Saving/restoring the GUI state using a configuration file

Before the call to plt, let's choose a file name and path for saving the configuration data:   S.cfg = [which(mfilename) 'at']; 

Next let's add a new function, called cfg which saves the current configuration to the file:

  function cfg() 
    S = get(gcf,'user'); sli = findobj(gcf,'style','slider'); 
    cf = { plt('edit',S.n);      plt('pop',S.typ); 
           plt('pop',S.dec);     plt('pop',S.pts); 
           plt('slider',S.Rp);   plt('slider',S.Rs); 
           plt('slider',S.Wn);   plt('slider',S.Wm); 
           get(sli(1),'backgr'); get(gcf,'pos')           }; 
    save(S.cfg,'cf'); 

Then right before we initialize the plot, we load the configuration file if it exists and set the GUI elements to agree with the data in the file:

    if exist(S.cfg) load(S.cfg); 
                    plt('edit',S.n,'value', cf{1}); plt('pop',S.typ,'index',cf{2}); 
                    plt('pop',S.pts,'index',cf{3}); plt('pop',S.dec,'index',cf{4}); 
                    plt('slider',S.Rp,'set',cf{5}); plt('slider',S.Rs,'set',cf{6}); 
                    plt('slider',S.Wn,'set',cf{7}); plt('slider',S.Wm,'set',cf{8}); 
                    set(h,'background', cf{9}); set(gcf,'position', cf{10}); 
    end; 

And finally we add this parameter to the plt call:

    'closeReq',@cfg 

This instructs plt to call the function that saves the configuration data when the user closes the figure window to exit the application.

5.) Adding temporary user help message


The HelpText pseudo object is ideal for this task since it provides a mechanism for removing the messages once you start using the program. To define the help text and make it visible on the screen, these three lines were added to the end of the main gui2 routine:

htxt = {'Select the filter order & type' ...
        'in the parameter box above.' '' ... 
        'Vary the ripple & frequency' ...
        'parameters using the sliders.' .6+.62i}; 
plt('HelpText','on',htxt);  % show help text

Note that we have defined a help message consisting of five lines of text (with the middle line is blank). The complex number at the end specifies the position relative to the main axis where we want the help text to appear. The real part specifies the horizontal position and the imaginary part specifies the vertical position (in normalized units). And lastly, this line was added to the and of the callback function (clb):

plt('HelpText','off');

That line insures that as soon as the user starts doing anything with the program, the HelpText will disappear insuring that it does not become a distraction.

This concludes our discussion of the gui2 example. Although it might seem like coding this example was a lot of work, only about 85 lines of code were needed to implement a fairly complex set of display and computational requirements. GUI programming is notorious for its complexity, and I believe that if you tried to implement a this application in other programming languages you would be looking at a far larger effort with source code running into the many hundreds of lines. I would like to be able to report how long the program would be in Matlab using GUIDE (without using plt), so I would be thrilled if one of the guide experts out there would take up this challenge by implementing the original five goals of gui2 as well as the five enhancements. If you manage to do this, I would gladly include your GUI (with credit of course) to contrast the Guide programming style with the one I present here.

To further your education of GUI programming with plt, I especially recommend reviewing the pltsq.m application if you are interested in moving plots (i.e. real-time updating). Also the curves.m, editz.m, pltmap.m, and winplt.m applications are worth reviewing since they each have a fairly rich GUI design with lots of opportunities for using various plt features in interesting ways.

SnapTo resolution

You may have noticed that in repositioning mode, the objects when dragged don't move or resize smoothly, but rather move in steps of a fixed size. This makes it easier to align related objects and generally gives a more pleasing result. The default grid size is 100 by 100 which means that there are 100 useable positions inside the figure in both the x and y directions. This also means that if you are using normalized coordinates the third decimal place for all position vector elements will be zero.

There are three ways to bring up the SnapTo figure shown below:
  1. Type plt move res in the command window. (This is the only method if you are not using a plot pseudo object).
  2. First left-click on the delta button, followed by a right click on the same button.
  3. It's easy to forget the sequence for method two, so you can also go to the plt menu in the menu bar. There you will see the option "Reposition grid size" which will bring up the SnapTo figure.

The default resolution is usually enough, but if you want finer control, move one or both of these sliders to the right edge (i.e. 200). This is nearly always enough, although if you like you can type in a number bigger than 200 into either edit box. Or you can move the slider all the way to the left (i.e. zero) which disables the snap-to feature altogether.