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 tools (App Designer since 2016 and Guide for older Matlab versions) 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 31 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 when version R2014b was released, Matlab finally allowed you to set the grid line color independently (a long requested feature) 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 a significant fraction 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 R2019a 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).

Matlab's newer GUI tool, App Designer, doesn't split the program definition into two files like Guide does but it doesn't solve the version compatibility issues since it saves the program in a new file type that can't be used in versions of Matlab older than 2016. Also applications developed with App Designer often can't be edited using versions of Matlab older than the version used to create the application. 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 as automated as Guide or App Designer, 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 (gui1.m)

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()
  figure('name','gui1','menu','none','pos',[150 60 430 350],'color',[0 .1 .2]);
The next line defines the choices for the popup menu. Then we create an array which contains the positions for the three pseudo sliders and the uitable followed by the positions of the seven uicontrols. Note that the positions of the three pseudo sliders are all the same which means (initially) they will all be on top of each other. Also note that the uitable and all seven uicontrols all (initially) have the same position, which means they too will be on top of each other. This is done for simplicity and as you will soon see will not be a problem because it will be easy to use the mouse to move and resize the sliders and other controls to an appropriate position.
  cho = {'choice A' 'choice B' 'choice C'}; % choices for popup control
  p = {[.5 .5      ];  % PseudoSlider 1
       [.5 .5      ];  % PseudoSlider 2
       [.5 .5      ];  % PseudoSlider 3
       [.1 .1 .1 .1];  % uitable
       [.1 .1 .1 .1];  % frame
       [.1 .1 .1 .1];  % popup
       [.1 .1 .1 .1];  % slider
       [.1 .1 .1 .1];  % button
       [.1 .1 .1 .1];  % checkbox
       [.1 .1 .1 .1];  % listbox (80 lines)
       [.1 .1 .1 .1]}; % text (10 lines)
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(5:11));
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 13 lines of code! (not counting the initial position array) 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)                     % The slider callback -------------------
  h = get(gcf,'user');                  % Get the handle list
  t = 1e20.^(rand(3,80))/1e6;           % Random numbers (with wide dynamic range)
  set(h(10:11),'fontname','courier',... % Use the same random table of numbers
    'string',prin('3{%6V   }~, ',t));   %  for both the listbox and the textbox
  set(h(4),'data',100*rand(3,2));       % More random numbers 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. When in repositioning mode, plt saves the handle of the last object that you clicked on in a variable named hhh in the base workspace. So now you 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.

Another way of 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. (This 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.

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:
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
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;  % 2.0e10   1.3e12   3.5e-4  
uic: 201  .540 .050 .440 .400;  % 2.0e10   1.3e12   3.5e-4  
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 work we did to reposition the controls permanent by changing the position array (p) from the initial guess to the values that appear in the command window (blue text above). First cut and paste those 11 lines of coordinates from the command window directly into the gui1.m source code as shown here.

Then we use the column select mode of our editor (as pretty much every programmer's editor does) to remove the initial coordinate estimates, and then highlight the new positions, and finally pasting the highlighted (yellow) text into the p array. Once that is done, delete the 11 lines that we had pasted in from the command window and now our file should like the same as the gui1.m file that is included in the demo folder.

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 (afiltS.m)

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 box for visual appeal and to allow those four parameters to be moved around as a group when repositioning. 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.

By the way, I've called this program afiltS.m (supplied in the demo folder) where the "S" stands for "Simplified" because it is a simplified version of afilt.m (also in the demo folder) which has several more advanced features. But of course here we want to start simple.

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 types and number of points to display:
function afilt()
  p = {[.5 .4 .2 .2];  % plot    position
       [.4 .2 .1 .1];  % axis    position: Parameter frame
       [.2 .2 .1 .1];  % edit    position: filter order
       [.2 .3 .1 .2];  % popup   position: filter type
       [.2 .4 .1 .2];  % popup   position: # of decades
       [.2 .5 .1 .2];  % popup   position: # of points
       [.2 .8      ];  % slider  position: Passband ripple
       [.4 .8      ];  % slider  position: Stopband ripple
       [.6 .8      ];  % slider  position: Cutoff frequency
       [.8 .8      ]}; % slider  position: frequency 2
  typ = {'low pass' 'high pass' 'band pass' 'stop band'};  pts = 100*[1 2 4 8 16];
  ftypes = {'Butter' 'Bessel', 'Cheby1'  'Cheby2' 'Elliptic'};

So how good are my guesses? To find out, let's look at this first screen shot (even though I haven't yet shown you all the code used to create it). Clearly the guesses are not accurate. The plot is way too small compared to the sketch and the pseudo popups and edit objects are scattered around and not inside the grey frame meant to contain them. But as you will see, this will not be a problem. In fact, it would be a waste of time to spend more than a few minutes coming up with the initial guess. (Even if all the objects were on top of each other, it would not be difficult to drag them to the desired locations.)

Ok, now let's continue with the coding by creating the plotting pseudo object (figure, axis, cursor, grid, traces, etc) = plt(0,zeros(1,5),'TraceID',ftypes,'Options','logX','xy',p{1},...
             'Ylim',[-80 5],'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 (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 a frame (actually an axis object) and four pseudo objects that will go inside the frame:
       'linewidth',3,'xcolor',[1 1 1]/2,'ycolor',[1 1 1]/2,'tag','frame');
  axes(get(,'parent'));   % reset the main plot axis as the active one
  S.n   = plt('edit',  p{3} ,[6 1 25],@clb,'label','Or~der:');
  S.typ = plt('pop',   p{4} ,typ,@clb,'index',3,'swap');
  S.dec = plt('pop',   p{5} ,1:5,@clb,'index',3,'label','Decades:');
  S.pts = plt('pop',   p{6} ,pts,@clb,'index',2,'label','Points:');
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, but notice that there is a tilde character (~) inserted into that string. This is to change the amount of space (width) allocated to the label. You might want to experiment by moving and/or removing the tilde character to see how that effects the label width. 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} ,[.004 .001  1],'Cutoff frequency',@clb,5,'%4.3f 6 2');
  S.Wm  = plt('slider',p{10},[.03  .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 one more line left to complete the afiltS function:
  set(gcf,'user',S);  clb; % save parameters for clb, and initialize the plot
% end function afiltS
The set command saves the handle structure in the figure user data where the callback function can easily retrieve it. The clb command (the last command of the afiltS function) executes the callback function to initialize the display to agree with the initial values of the controls. After just 16 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 clb() that makes the GUI come alive and this will require another 16 lines of code:
function clb() % callback function for all objects
  S = get(gcf,'user');                           % get handle structure
  ty = plt('pop',S.typ);                         % get filter type index
  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);                    % X-axis data (radians/sec)
First we pick up the filter parameters that are inside the gray box (filter type, filter order, number of decades, and number of points). Finally the logspace command generates the requested number of points logarithmically spaced between .001 and 1 (for the 3 decades example).
  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
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.
for k=1:5 set(,'x',X,'y',af(k,ty,N,Wn,Rp,Rs,X)); end; % set trace data
  plt('cursor',-1,'xlim',X([1 end]));                        % set Xaxis limits
% end function clb
The next line of the callback function calls the af() function (which computes the magnitude frequency response) and copies that result to the trace data. Then the final line uses the plt cursor function to sets the x-axis limits of the display in case the decades parameter had been modified. The af function is not shown here because it is only computational and doesn't deal with any of the GUI design issues. Refer to that function inside the afiltS.m file if you are interested in the computational details.

We are done with the initial coding and we can try it out. Typing afiltS to start the program brings up the figure shown above. 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. Soon we will have all the objects placed in about the right locations based on our original sketch and the figure will look something like this one. Note that once objects are placed inside the gray frame (axis) moving the frame will also move all the objects inside it.

Before closing this figure, 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  .125 .105 .800 .760;  % axes
axi:  52  .100 .882 .235 .100;  % axes
edi: 211  .165 .935 .040 .030;  %   6  
pop: 102  .110 .710 .100 .200;  % band pass
pop: 103  .310 .750 .024 .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. (If you forget to do this left-clicking sequence on all the objects, you won't be able to copy and paste all the positions at once as described here. You could still copy and paste each position individually by finding them in the Matlab command window output stream, but this would be more time consuming.)

As we did in the previous example we use the editor's column select mode to do a block move (as shown by the red arrow) of 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 the same figure we had before.

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 application with so few lines of code. 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 after testing afiltS I had ideas for the six enhancements described below. Because of the power of the plt pseudo objects, it didn't take much code to implement these enhancements. When reviewing the enhancements listed below, you can look at the code in afilt.m (also in the demo folder) to see how each of these new features was implemented.

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

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?

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 additional line to the position array at the beginning of the program, to define the location for the new text object. As with the other object positions, initially it was just a wild guess which was refined using the plt repositioning mode.

Once the transition ratio is calculated (saved in the variable h) it only takes this one line to convert it into a multi-line string:
set(S.etr,'string',prin('Elliptic ~, transition ~, ratio: ~, %5v',h));
Because the sprintf function does not handle cell arrays, this operation normally would require several awkward lines of code, but this is simplified by using the prin.m supplied in the plt folder. Learn more about this useful function here: Auxiliary functions.

2.) Selecting colors

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.

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 five screen elements. The first four are the pseudo sliders at the top edge of the figure that control the pass and stop band ripple and the cutoff frequencies. The fifth element to gain this color adjustment feature is the multi-line text string (elliptic transition ratio) mentioned above. (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 afilt's pseudo sliders or the multi-line text string 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. (Read about the details of the ColorPick object here: Pseudo objects)

The first line (below) 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 right click action) to the 4 pseudo sliders as well as to the elliptic ratio text object. If we stopped there (i.e. omitted the third line) then we would be able to pick 16 different colors for the sliders (i.e 4 components for each of the 4 pseudo sliders). However that's more flexibility than we probably would want and actually might be a nuisance. It's probably better to tie all 16 of those objects together so that when you change any one of them, the other 15 automatically change to agree with the new color. The third line accomplishes that by setting an application data variable as explained in the ColorPick Pseudo object documentation.
  h = getappdata(gcf,'sli');  h(5:5:end) = []; 
  set([h S.etr],'buttondown','plt ColorPick;'); 
  for k = 1:length(h) setappdata(h(k),'m',{'backgr' h}); end; 

3.) Adding the phase display

Since the phase of the frequency response is an important characteristic in some applications, I thought it would be useful to have the plot show phase as well as amplitude. So far we have only used the left hand axis, so the easiest way to show the phase information is to plot it on the right hand axis. Usually the right hand axis is used to superimpose traces on the left hand axis, although in this instance I decided it would be clearer to plot the phase above the magnitude so the traces would not overlap. That choice allows us to display both magnitude and phase for all five filter types at once without the display becoming hopelessly cluttered and confusing. This is accomplished by adjusting the left and right Yaxis limits so that the magnitude and phase traces don't overlap. The tick mark labels are set explicitly so that they only appear in the valid range of the display. Here is an example of what the display looks like when a single filter type (Cheby1) is enabled.

It would have been natural to expand the TraceID box to include 10 elements instead of 5 however that has the following drawback. To enable or disable the traces associated with a particular filter type (Cheyb1, for example) one would have to click on two traceID tags, one for the magnitude and one for the phase. That is somewhat unnatural and it would be less cumbersome to do this with just a single click. The trick to make this happen is to link traces 1 and 5 (Butterworth mag/phase) and to link traces 2 and 6 (Bessel mag/phase), etc. so that 5 is enabled only when trace 1 is enabled, etc. This linking can be done in a single line inside the call back function, and I will show that line in section 4 below since that enhancement also involves linking traces.

4.) Adding the pole/zero plot

The analog filters are designed by first designing a prototype filter (always low pass) and than transforming it as selected (high pass, stop band, pass band) with the requested filter cutoff frequencies. Displaying the roots of the prototype filter in the s-plane can help our understanding of the design and use of the five filter types displayed by afilt. I could have used plt's subplot parameter to create both plots (Mag/Phase plot on the left and Pole/Zero plot on the right all with a single call to plt. However because the characteristics of these two plots are so dramatically different, this would have been difficult and I decided to use a separate call to plt for each of the two plots. This necessitated using the Fig parameter on the second call so that both plots are created in the same figure window. Like the Mag/Phase plot on the left, the Pole/Zero plot uses 10 traces (the first 5 to display the zeros for the 5 filter types, and the next 5 to display the poles. Actually an 11th trace was added to show a unit circle on the plot. A unit circle doesn't have the significance it does on z-plane plots, but it still provides a useful size reference, and some of the filter types have roots placed along or near the unit circle. Usually the x/y cursor readout appears below the plot, but for this plot they were moved to the top because that's where there was the most room (allowing for a bigger plot in a given figure size). If you enable only the Bessel filter traces, the figure will look like the one shown here. Note that the Bessel filter has only poles (X's on the plot) where as some of the filter types will have both poles and zeros (X's and O's).

This single line was added to the callback function so that if you enabled or disabled the magnitude display of a certain filter type (Butterworth for example) then the associated phase trace, zeros trace and poles trace would also be appropriately enabled or disabled:
t = plt('show');  t = t(find(t<6));  plt('show',[t t+5 t+10 t+15 21]); % for traceID callback
t (using the find subscript) will contain a list of magnitude traces that are enabled, while t+5 will be a list of the associated phase traces, t+10 will be a list of the associated zeros traces, and t+15 will be a list of the associated poles traces. Trace 21 (showing the unit circle) is always enabled.

Often when the TraceID callback is a simple one or two line function, it is more convenient to include the entire TraceID callback in the plt parameter list. Look at the function afiltALT.m (an alternate version of afilt also supplied in the demo folder) for an example of how this is done.

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

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.

To implement this, 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 this file as a 11 element cell array:
function cfg() % write configuration file
  S = get(gcf,'user');       sli = findobj(gcf,'style','slider');       sli = sli(1);
  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,'backgr');   get(S.etr,'color');  get(gcf,'position')};
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); % load configuration file if it exists -------------------
                  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(S.etr,'color',      cf{10});
                  set(gcf,'position',     cf{11});
And finally we add this parameter to the plt call:


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

6.) Adding temporary user help message

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. 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 shown here, these four lines were added to the beginning of the afilt routine:

htxt = {'Select the filter order & type' 'in the parameter box above.' '' ...
        'Vary the ripple & frequency' ' parameters using the sliders.' .6+.62i 2i ...
        'pole/zero plot' 1.19+1.06i 'color' 'yellow' 2i ...
        'Prototype filters' '(low pass with' '1 rad/sec cutoff)' 1.16+.98i};
Note that we have defined three help strings within the htxt cell array, each delimited by 2i (which actually may be any complex number).
  1. The first of these strings consists of the 5 lines that appear inside the left hand plot (the 3rd line is a blank line). The .6+.62i after those five lines indicates that the upper left corner of the 5 line help text should be positioned at x = .6 and y = .62 (normalized coordinates in the magnitude/phase plot axis frame). Since no color was specified, the default helptext color was used (orange).
  2. The second help string consists of just the one line "pole/zero plot" and is positioned at x = 1.19 and y = 1.06 (again in normalized coordinates in the magnitude/phase plot axis frame). A color (yellow) was specified for this string. (Note that instead of the string 'yellow' we could have used the traditional Matlab color triple vector ([1 1 0]) or the alternate vector form allowed in plt calls ([100 100 0]) or by the single number 010100 (the most compact way of specifying colors in plt parameters). These alternate ways of specifying colors are explained more completely at the top of the Colors help section.
  3. Finally the last help string consists of three lines that appear inside the pole zero plot (again using the same reference frame for the xy position as the previous two help strings and again using the default orange help string color).
Then this line was added at the end of the afilt routine to make the help text visible on the screen:

plt('HelpText','on',htxt);  % show help text

And lastly, this line was added to the end of the callback function (clb):


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

This concludes our discussion of the afilt example. Although it might seem like coding this example was a lot of work, GUI programming is notorious for its complexity, and I believe that if you tried to implement 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.

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, julia.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. However sometimes you may want to move objects around with finer precision. The SnapTo figure allows you to do that.

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.