首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 實作電子七段顯示器仿真控制項:CNumPane 封裝與座標計算

     編輯:VC 教學

前言

七段顯示器(Seven-segment Display)廣泛用於儀表、時鐘、計數器。本文實作 `CNumPane` MFC 控制項,繼承 `CStatic`,以向量繪圖模擬七段數碼管,支援多位數顯示、前景/背景色、邊框色、小數點、負號、背景虛位顯示。核心在於 `MakeCoordinate()` 根據控制項矩形與位數動態計算每段線段端點座標,構建 `Pane8` 結構陣列供 `OnPaint` 高效繪製。

一、類別介面設計


class CNumPane : public CStatic
{
    DECLARE_DYNAMIC(CNumPane)
public:
    CNumPane(BOOL bShowBackground = FALSE);
    virtual ~CNumPane();

    // 屬性設定
    void SetShowNumber(const CString& str); // 字串:支援 "123.45", "-99"
    void SetShowNumber(float f);            // 浮點數自動格式化
    void SetNumColor(COLORREF clr);         // 前景亮段顏色
    void SetBackNumColor(COLORREF clr);     // 背景暗段顏色
    void SetBorderColor(COLORREF clr);      // 控制項邊框顏色
    void SetBackGroundColor(COLORREF clr);  // 控制項背景顏色
    void SetShowBackground(BOOL bShow);     // 是否顯示背景虛位(暗段)

    void Restore(); // 重置所有屬性為預設

protected:
    struct Pane8
    {
        CPoint pts[6]; // 七段端點:A(0,1) B(1,2) C(2,3) D(3,4) E(4,5) F(5,0) G(0,3) 中心橫段
        CDC* pDC;
        void DrawSegment(int seg); // 0=A,1=B,2=C,3=D,4=E,5=F,6=G
        void DrawDigit(int digit); // 0-9, 10='-', 11='.'
    };

    CArray m_segments; // 每個數碼位一個 Pane8
    CString m_strValue;
    COLORREF m_clrFore, m_clrBack, m_clrBorder, m_clrBkgnd;
    BOOL m_bShowBack;

    // 核心:根據控制項大小與位數計算所有段座標
    void MakeCoordinate();

    afx_msg void OnPaint();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    DECLARE_MESSAGE_MAP()
};

二、座標計算核心:MakeCoordinate()


void CNumPane::MakeCoordinate()
{
    m_segments.RemoveAll();
    CRect rc; GetClientRect(&rc);

    int nDigits = m_strValue.GetLength();
    if (nDigits == 0) return;

    // 佈局參數
    int nMargin = 4;
    int nGap = 2; // 位間距
    int nSegW = (rc.Width() - 2*nMargin - (nDigits-1)*nGap) / nDigits;
    int nSegH = rc.Height() - 2*nMargin;
    if (nSegW <= 0 || nSegH <= 0) return;

    float fThick = max(2.0f, min(nSegW, nSegH) * 0.12f); // 段寬度比例
    float fGap = fThick * 0.15f; // 段間微縫

    for (int i = 0; i < nDigits; ++i)
    {
        Pane8 pane;
        pane.pDC = NULL; // 繪製時賦值

        float x0 = rc.left + nMargin + i * (nSegW + nGap);
        float y0 = rc.top + nMargin;
        float x1 = x0 + nSegW;
        float y1 = y0 + nSegH;
        float xm = x0 + nSegW / 2.0f;
        float ym = y0 + nSegH / 2.0f;

        // 七段端點定義(順時針:A上橫、B右上豎、C右下豎、D下橫、E左下豎、F左上豎、G中橫)
        // 使用 6 個關鍵點構建所有段
        pane.pts[0] = CPoint((int)(x0 + fGap), (int)(y0 + fGap));           // 左上
        pane.pts[1] = CPoint((int)(x1 - fGap), (int)(y0 + fGap));           // 右上
        pane.pts[2] = CPoint((int)(x1 - fGap), (int)(ym));                  // 右中
        pane.pts[3] = CPoint((int)(x1 - fGap), (int)(y1 - fGap));           // 右下
        pane.pts[4] = CPoint((int)(x0 + fGap), (int)(y1 - fGap));           // 左下
        pane.pts[5] = CPoint((int)(x0 + fGap), (int)(ym));                  // 左中

        m_segments.Add(pane);
    }
}

三、七段繪製邏輯


void Pane8::DrawSegment(int seg)
{
    // seg: 0=A(0-1), 1=B(1-2), 2=C(2-3), 3=D(3-4), 4=E(4-5), 5=F(5-0), 6=G(0-3 中點連線)
    static const int idx[7][2] = {
        {0,1}, {1,2}, {2,3}, {3,4}, {4,5}, {5,0}, {0,3} // G 段特殊:左上到右下中點
    };
    if (!pDC) return;
    CPoint p1 = pts[idx[seg][0]];
    CPoint p2 = pts[idx[seg][1]];
    if (seg == 6) // G 段:水平中線
    {
        p1.y = p2.y = (pts[0].y + pts[3].y) / 2;
        p1.x = pts[0].x; p2.x = pts[3].x;
    }
    pDC->MoveTo(p1); pDC->LineTo(p2);
}

void Pane8::DrawDigit(int digit)
{
    // 七段編碼:A B C D E F G
    static const BOOL code[12][7] = {
        {1,1,1,1,1,1,0}, // 0
        {0,1,1,0,0,0,0}, // 1
        {1,1,0,1,1,0,1}, // 2
        {1,1,1,1,0,0,1}, // 3
        {0,1,1,0,0,1,1}, // 4
        {1,0,1,1,0,1,1}, // 5
        {1,0,1,1,1,1,1}, // 6
        {1,1,1,0,0,0,0}, // 7
        {1,1,1,1,1,1,1}, // 8
        {1,1,1,1,0,1,1}, // 9
        {0,0,0,0,0,0,1}, // 10: '-'
        {0,0,0,1,0,0,0}  // 11: '.' (只亮 D 段下端點附近,這裡簡化亮 D)
    };
    if (digit < 0 || digit > 11) return;
    for (int s = 0; s < 7; ++s)
        if (code[digit][s]) DrawSegment(s);
}

四、OnPaint 繪製流程


void CNumPane::OnPaint()
{
    CPaintDC dc(this);
    CRect rc; GetClientRect(&rc);

    // 背景
    dc.FillSolidRect(&rc, m_clrBkgnd);
    // 邊框
    CPen penBorder(PS_SOLID, 1, m_clrBorder);
    CPen* pOldPen = dc.SelectObject(&penBorder);
    CBrush* pOldBrush = (CBrush*)dc.SelectStockObject(NULL_BRUSH);
    dc.Rectangle(&rc);
    dc.SelectObject(pOldPen); dc.SelectObject(pOldBrush);

    // 建立畫筆
    CPen penFore(PS_SOLID, max(1, (int)(rc.Height()*0.12)), m_clrFore);
    CPen penBack(PS_SOLID, max(1, (int)(rc.Height()*0.12)), m_clrBack);

    // 確保座標更新
    MakeCoordinate();

    for (int i = 0; i < m_segments.GetSize(); ++i)
    {
        Pane8& pane = m_segments[i];
        pane.pDC = &dc;

        int digit = 0;
        TCHAR ch = m_strValue[i];
        if (ch >= _T('0') && ch <= _T('9')) digit = ch - _T('0');
        else if (ch == _T('-')) digit = 10;
        else if (ch == _T('.')) digit = 11;
        else continue;

        // 1. 畫背景虛位(暗段)
        if (m_bShowBack)
        {
            dc.SelectObject(&penBack);
            for (int s = 0; s < 7; ++s) pane.DrawSegment(s);
        }
        // 2. 畫前景亮段
        dc.SelectObject(&penFore);
        pane.DrawDigit(digit);
    }
}

五、使用範例


void CMyDialog::OnInitDialog()
{
    CDialog::OnInitDialog();
    m_numPane.SubclassDlgItem(IDC_STATIC_NUMPANE, this);
    m_numPane.SetNumColor(RGB(255, 0, 0));       // 紅色亮段
    m_numPane.SetBackNumColor(RGB(80, 0, 0));    // 暗紅背景段
    m_numPane.SetBorderColor(RGB(128,128,128));
    m_numPane.SetBackGroundColor(RGB(0,0,0));
    m_numPane.SetShowBackground(TRUE);
    m_numPane.SetShowNumber(_T("-123.45"));
}

六、執行效果

七段顯示器示範

支援多位數、負號、小數點、背景虛位顯示,顏色可自訂,`OnSize` 觸發重新計算座標,適合即時數據顯示(電壓、頻率、計數器)。

七、已知不足與改進

  • 立體效果:目前純 `MoveTo/LineTo` 線條,可改用 `Polygon` 填充梯形段體 + 漸層筆刷模擬 LED 發光感。
  • 斜體/斜段:真實七段顯示器段體常有傾斜角度,座標計算可引入傾斜參數。
  • GDI+ 抗鋸齒:遷移至 `Graphics` 繪製可獲得平滑邊緣。
  • 動畫轉換:數值變更時逐段淡入/淡出動畫。

八、結語

`CNumPane` 以約 200 行核心代碼實現功能完整的七段顯示器控制項,展示了 MFC 控制項開發的標準流程:繼承 CStatic → 定義資料結構 → 動態座標計算 → OnPaint 分層繪製 → 屬性介面封裝。此架構可輕鬆擴充至十四段、十六段、點陣顯示器等更複雜的數字顯示裝置仿真。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved