分类: C/C++
2008-09-14 20:12:41
Recently, I've been involved in a instrumentation project wherein one single display (a CView) is used to present measurement results as well as the status of some transducers. The desire was to have everything in one "window" that could be maximized and all of the relevant information could be seen without having to deal with overlapping windows or dialog boxes.
As a means of graphically displaying the transducer status, an "analog meter" class was developed which could be used in any square rectangle within any display context. Thus, a sqaure area (CRect) could be determined within an application's CView derived class and the meter could be displayed and updated as needed within this CRect.
The drawing a meter is somewhat trivial, however the smooth (and fast) animation of such a meter is a different matter. To this end I referred back to the "triple buffering" method I used in my . In this approach, I create three bitmaps: one for the underlying "grid" (the pie shaped wedge, the title and numerical limit values), one for the "needle" and one which is used to temporarily store the "result" of the combination of the needle and grid prior to BitBlt'ing to the destination display context.
To improve the speed of animation, these bitmaps are only updated on an "as needed" basis. For example, the grid bitmap is only redrawn when the meter's rectangle has changed and the needle bitmap is only redrawn when the needle's position has changed. Furthermore, the combination of bitmaps is performed in a memory based result prior to display in order to provide faster performance.
The Analog Meter can be used based on the following:
These steps are outlined in detail as folllows:
It may also be useful to establish CRect member variables to keep track of the location of your meters.
class CAnalogMeterTestView : public CView { public: CRect m_rectLeftMeter ; CRect m_rectRightMeter ; CAnalogMeter m_meterLeft ; CAnalogMeter m_meterRight ; } // end CAnalogMeterTestView
This is best accomodated in meter-owner's constructor.
CAnalogMeterTestView::CAnalogMeterTestView() { // setup the Left meter properties m_meterLeft.SetRange (-5.0, 5.0) ; m_meterLeft.SetRangeDecimals(1) ; m_meterLeft.SetValueDecimals(3) ; m_meterLeft.SetTitle("Channel A") ; // setup the Right meter properties m_meterRight.SetRange (-10.0, 10.0) ; m_meterRight.SetRangeDecimals(1) ; m_meterRight.SetValueDecimals(3) ; m_meterRight.SetTitle("Channel B") ; } // end CAnalogMeterTestView constructor
This is best accomodated in meter-owner's OnDraw function.
void CAnalogMeterTestView::OnDraw(CDC* pDC) { // determine the meter rectangle(s) - make sure they are square // fill in the member variable CRect's accordingly // show the meters in their respective rectangles m_meterLeft.ShowMeter (pDC, m_rectLeftMeter) ; m_meterRight.ShowMeter (pDC, m_rectRightMeter) ; } // end OnDraw
The approach I use is to periodically update the needle position based on a timer.
void CAnalogMeterTestView::OnTimer(UINT nIDEvent) { double dLeftValue, // the new meter values to be shown dRightValue ; CClientDC dcClient(this) ; // get a client dc for the updated meter // determine the new values // update the meters m_meterLeft.UpdateNeedle (&dcClient, dLeftValue) ; m_meterRight.UpdateNeedle (&dcClient, dRightValue) ; // call the base class, as the Class Wizard says I should CView::OnTimer(nIDEvent); } // end OnTimer
Customization
The meter range, the title and the number of decimal places on the numerical values can be easily modified as shown above in Step 2. However, it should be noted that you must call ShowMeter after making these changes in order to "see" the effects. These attributes can be changed (via "Set" functions) or retrieved (via "Get" functions). The public attribute modification/access functions are:
"Inside the Meter" Customizations
The CAnalogMeter constructor contains some areas which may provide some interesting customizations, particularly in terms of the colors. Try modifying the member variables: m_colorGrid, m_colorNeedle and m_colorValue.
Also, the width of the meter's "pie slice" is based on an angle specified CAnalogMeter::DrawGrid() function. This angle is set through the variable dLimitAngleDeg.
READ THIS BEFORE YOU SEND ME NASTY EMAIL... (You don't have to read this before sending nice email)
This meter was intended for use in a CView derived class and has been tested with my printer and clipboard functions. I have not experimented with using it in other areas (such as a control in a dialog). I'm sure these capabilities could be added or perhaps this approach could be applied in these other areas, but I don't have the time to do it at this point.