Global TMW:
Login  |  Register          Free Newsletter Subscription
Subscribe
Email
Print
Reprint
Learn RSS

Foundation Classes Simplify C/C++ Programming

Programming a GUI in C or C++ under Windows is complicated, but not impossible. By using the tools available and understanding the programming system, you can improve your programming productivity.

Darrin Rothe, DerTech, Mesa, AZ -- Test & Measurement World, 2/1/2000

Editor’s note: This article assumes a basic knowledge of C and C++ programming.

Ten years ago, programming a user interface for test applications in C or C++ was easy. You were probably programming on a DOS-based PC and your user interfaces consisted of a prompt and a few lines of test results. A fancy user interface consisted of some text windows and menus. The systems were relatively easy to set up, maintain, and clone.

Today, programming proves more complicated. To keep test systems easy to use and to integrate them with enterprise computer systems, you should program in Windows and build a clean graphical user interface (GUI). But that isn’t easy. Under Windows, a program that displays “Hello World” must be several lines long—quite different from DOS, which could handle the job in a single line of code.

Rapid Application Development (RAD) packages such as Visual Basic let you easily build a GUI. LabView and HP VEE—two other packages geared toward test and measurement applications—also have objects that let you design a custom GUI. You may be tempted to build the user interface in a RAD package, then code the difficult functions in a C library and call the library functions from the RAD package. Yet this method requires you to be familiar with two development systems and two languages, and it makes troubleshooting and debugging more difficult. I prefer to program entirely in C or C++ because of the flexibility the languages provide.

If you want to program in C or C++ and you want to develop a nice user interface, you must make some choices. You could write code that uses the low-level Windows application programming interface (API), but that’s too complicated. Fortunately, Microsoft Foundation Classes (MFC) and other tools let you build GUIs while working in the same development program that you use to write the rest of your test code. MFC comes bundled with Microsoft Visual C++ and other development systems such as Watcom C/C++ and Borland Inprise C++ Builder. In the examples that follow, I used Microsoft Visual C++.

What Is MFC?
MFC is a collection of C++ classes that encapsulate much of the Windows API functionality into prewritten, reusable code. In addition to the classes, Microsoft Visual C++ comes with tools and wizards to help you manage your program development.

While having intimate knowledge of C++ helps, you don’t need it to use MFC. You can still write the bulk of your application in C and compile the MFC code with your C code. Depending on how you implement your program, you may be able to use your legacy C code with minor changes.

The Document-View Concept
Many familiar Windows programs operate on a concept called document view, a predefined way of organizing and viewing data. For example, a spreadsheet holds numerical values in a tabular view. A plot of the data offers an alternative view, although it’s the same data. If you change a value in the table, you expect the plot to change, too. Likewise, if you modify the plot, the values in the table should change.

MFC handles documents with a class called CDocument and handles views with a class called CView. The data the program holds will be part of your class derived from the MFC class CDocument. CDocument provides mechanisms for saving and loading the data, including standard file-save and file-open dialog boxes. All you have to do is fill in some blanks to view them. To view the data, you must derive a class from MFC’s CView, or one of its variations, such as CFormView. With a few customizations, this class will interact with your CDocument derived class to show the data on the screen, interact with the user, and provide printing with print-preview capability.

Set Up the Application
To start a new MFC application, use the Microsoft C++ AppWizard. Select either SDI (single document interface) or MDI (multiple document interface). An MDI application is ideal for a system that tests multiple units at the same time. If your application doesn’t do this, select SDI. Name your application “TestApp.” On the final step of the AppWizard, be sure to select CFormView as the view’s base class. The class CFormView is derived from the CView class and it will make the program’s view resemble a dialog box.

Upon completion, the AppWizard will generate commented source code files for you to customize. In this example, some of the files AppWizard will create include TestAppDoc.h and TestAppDoc.cpp (the header file and source code for your C++ CDocument derived class) and TestAppView.h and TestAppView.cpp (the header and source file for your C++ CView derived class).

As a quick test, build and run the application. The main view of the program will display a message saying “TODO: Place form controls in this dialog.” You’ll also see a default menu that’s mostly nonfunctional right now.

Set up the Document
Before you add the controls to customize a view, you should add code to the document class to handle data. Open the header file (TestAppDoc.h) for the document class. Under the public section, add any variables you will need to store the application data.

TMW00_02F4fig1.gif (21171 bytes)
Figure 1. Microsoft Developer Studio uses a Windows-Explorer-like format to provide a map of your application’s files.

Figure 1 shows the document class’s header file and the separate areas for function and variable declarations. These variables should include anything you need to set up the system to start a test run, record events during the run, and handle any other information.

In a real application, you shouldn’t make these variables public. Instead, make them private and add routines to regulate access to these variables. To provide a simple working example, I’ve omitted these access-regulating routines. Add the code in Listing 1 to your TestAppDoc header file within the CTest-AppDoc declaration.

The variables in Listing 1 represent the minimum information you’ll likely need. The serial number lets you record which unit was tested. You can use the model number to set up test parameters. The results array can record a transcript of the test as it runs. You can then display the contents of the array in your view to indicate the test’s progress.

The variable types CString and CStringArray are MFC utility classes that simplify string handling. A real implementation might have many more variables, such as the time and date, the operator’s name, the test station, as well as other test parameters.

Next, open the TestAppDoc.cpp file and add the code from Listing 2 to initialize your variables in CTestAppDoc::OnNewDocument(). This routine should already exist in TestAppDoc.cpp. You merely need to add the variable initialization. Add one simple routine (Listing 3) to add a string to the array and increment the index. Be sure to put the function declaration in the header file under the public section.

TMW00_02F4fig2.gif (23215 bytes)
Figure 2. Build your user interface by inserting icons from a tool bar into a form.

Set up the View
Next, you need to add controls to the view form. Add these controls with the DevStudio editor, which is part of the Microsoft Visual Studio development system. Add edit controls that provide for entry and display of the serial number and model number. Add static labels for the edit controls and a single button labeled “Start Test.” Figure 2 shows the completed form with the two static labels, two edit controls, one push button, and one list box. For this example, I named the list box IDC_LIST. Assign the variable m_List to IDC_LIST using Visual Studio’s ClassWizard. You’ll need it for the next step.

You now have data that sits in the document class. You also have a place to view the data. Next, you need to link the data to the class. Start by adding the view class-member function OnUpdate with the ClassWizard. Add the code in Listing 4 to the OnUpdate function. Incidentally, the function GetDocument() used in OnUpdate automatically gets a pointer to the current document and gives you access to the data members.

Run a Test
You have a framework in place, but it still doesn’t do anything. For a real application, you would now write routines that control instruments and process data.

To initiate a test routine by pressing the Start Test button on the view, you must add a button handler to the button object using the ClassWizard. Doing so will add a handler to the view class’s source in TestAppView.cpp. In this example, I call the handler OnStart. Add the code in Listing 5 to your OnStart routine. Every time you click the “Start Test” button, the code will run.

At last, the example is complete. Build and run the program. When you press Start Test, the informational messages coded into the OnStart routine will appear in the list box, simulating a sequence of test steps.

Because you’ve written the application with MFC and used the document-view concept, you can easily add the capability to save and load the results to disk with three or four lines of additional code. Printing and print-preview will take a little more time—about 25 lines of code. A sample listing accompanies the online version of this article on the Test & Measurement World Web site. T&MW

FOR FURTHER READING

Brain, Marshall, and Lance Lovette, Developing Professional Applications for Windows 98 and NT Using MFC, Prentice Hall Computer Books, Upper Saddle River, NJ. ISBN: 0-13085-121-3. 1999.

Kruglinski, David J., Scot Wingo, and George Shepherd, Programming Visual C++, Microsoft Press, Redmond, WA. ISBN: 1-57231-857-0. 1998.

Darrin Rothe is a consulting engineer with DerTech LLC, Mesa, AZ. Previously, he worked at Rockwell Automation and Marquette Medical Systems as a test engineer. He holds a B.S.E.E. from Milwaukee School of Engineering and an M.S.E.E. from Marquette University. E-mail: rothe.darrin@dertech.com.

Listing 1.
public:
  CString m_SerialNumber;
  CString m_ModelNumber;
  CStringArray m_Results;

Listing 2.
BOOL CTestAppDoc::OnNewDocument()
{
  if (!CDocument::OnNewDocument())
  return FALSE;

  m_SerialNumber = "";
  m_ModelNumber = "";
  m_Results.SetSize(0,100);

  return TRUE;
}

Listing 3.
void CTestAppDoc::AddString (CString string)
{
  m_Results.Add(string);
  UpdateAllViews(NULL);
  return;
}

Listing 4
void CTestAppView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
  static int last_update;
  int n;
  int current_update = GetDocument()->m_Results.GetSize();

  if (last_update > current_update) // must have reset document
  {
  m_List.ResetContent();
  last_update=0;
  }

  for (n=last_update;n<CURRENT_UPDATE;N++) do we need to
  {
 m_List.AddString(GetDocument()->m_Results.GetAt(n));
  m_List.SetCurSel(n); // keep selection bar on new string
  }

  // update edit controls
 SetDlgItemText(IDC_SERIAL_NUMBER,GetDocument()->m_SerialNumber);
 SetDlgItemText(IDC_MODEL_NUMBER,GetDocument()->m_ModelNumber);
 
  last_update=current_update;

m_List.SendMessage (WM_PAINT); // force listbox to update immediately
}
Listing 5
void CTestAppView::OnStart()
{
  // update serial number and model number in document
 GetDlgItemText(IDC_SERIAL_NUMBER,GetDocument()->m_SerialNumber);
 GetDlgItemText(IDC_MODEL_NUMBER,GetDocument()->m_ModelNumber);

  // disable the start button while test is running, and display a wait cursor
  CWaitCursor cwait;
  // call test routine here like the following line. You need to pass a
  // pointer to the document to be able to add information to the results
  // array, or to access the serial and model number information
  // RunTestOne(GetDocument());

  // for this example, we'll run the test here
  {
  CString temp;
  CTestAppDoc *pDoc = GetDocument(); // get a pointer to the document

  // you can access the serial and model number if you would like
  temp.Format ("Serial Number: %s",pDoc->m_SerialNumber);
 pDoc->AddString(temp);

  temp.Format ("Model Number: %s",pDoc->m_ModelNumber);
 pDoc->AddString(temp);

  // do anything you like
 pDoc->AddString("Starting Test Number One");
  Sleep(1000);

 pDoc->AddString("Running Test Number One");
  Sleep(1000);

 pDoc->AddString("Finishing Test Number One");
  }
 
  //remove any unwanted mouse clicks that occur during the test
  MSG msg;
  while (PeekMessage(&msg,this->m_hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
{
}
}

Email
Print
Reprint
Learn RSS

Talkback

We would love your feedback!

Post a comment

» VIEW ALL TALKBACK THREADS

Related Content

Related Content

 

By This Author

There are no other articles written by this author.

Sponsored Links



 
Advertisement
SPONSORED LINKS

More Content

  • Blogs
  • Podcasts

Blogs

  • Martin Rowe
    Rowe's and Columns

    November 5, 2008
    Technical articles retain value
    I'm always amazed, and pleased, when I hear from readers who still find value in old T&MW articl...
    More
  • Martin Rowe
    Rowe's and Columns

    October 31, 2008
    Measurement proverbs
    The other day, I received some measurement proverbs that I'd like to share. The proverbs come from K...
    More
  • » VIEW ALL BLOGS RSS

Podcasts

Advertisements





NEWSLETTERS
Click on a title below to learn more.

Test Industry News (3 Times Per Month)
Machine-Vision & Inspection (Monthly)
Communications Test (Monthly)
Design, Test & Yield (Monthly)
Automotive, Aerospace & Defense (Monthly)
Instrumentation (Monthly)
Resource Center E-Alert (Monthly)
©2008 Reed Business Information, a division of Reed Elsevier Inc. All rights reserved.
Use of this Web site is subject to its Terms of Use | Privacy Policy
Please visit these other Reed Business sites