MFC用高精度计时器实现五段时序控制的改进

在前斯博客MFC用高精度计时器实现五段时序控制功能基础上,每个时间段增加一个条件。五个独立条件按钮:每个按钮单独处理,点击切换"启用/禁用"状态。

顺序执行:启动后从第一个启用的时间段开始,程序按照顺序检查时间段条件,只有当前条件为true才执行该时间段,完成后继续检查下一个时间段,一旦遇到禁用的时间段,立即停止整个流程(不再执行后面的时间段)。实时显示:当前时间段编号和当前时间段内的实时值(精度0.01秒)。停止功能:点击停止按钮立即停止计时,并显示当前时间段的实时值。启动控制:运行期间启动按钮和所有条件按钮被禁用,停止后恢复。

MultiTimerDlg.h

cpp 复制代码
// MultiTimerDlg.h : header file
#if !defined(AFX_MULTITIMERDLG_H__INCLUDED_)
#define AFX_MULTITIMERDLG_H__INCLUDED_

#include "CElapsed.h"

#if _MSC_VER > 1000
#pragma once
#endif

class CMultiTimerDlg : public CDialog
{
public:
    CMultiTimerDlg(CWnd* pParent = NULL);
	 virtual ~CMultiTimerDlg();   // 改为仅声明
    enum { IDD = IDD_MULTITIMER_DIALOG };

protected:
    HICON m_hIcon;

    // 数据
    double m_dTimeStages[5];      // 五个时间段时长(秒)
    bool   m_bCondition[5];       // 五个条件标志(true=执行)
    int    m_nCurrentStage;       // 当前执行的时间段索引(0~4)
    bool   m_bRunning;            // 是否正在运行
    bool   m_bThreadRunning;      // 线程运行标志
    CElapsed m_elapsedTimer;      // 高精度计时器
    CWinThread* m_pTimerThread;   // 计时线程指针

    CRITICAL_SECTION m_csSync;    // 临界区

    static UINT TimerThreadProc(LPVOID pParam);
    void UpdateDisplay(int nStage, double dCurrentTime);
    void StopTimer();

    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    afx_msg void OnButtonStart();
    afx_msg void OnButtonStop();
    // 五个独立条件按钮处理函数
    afx_msg void OnCond1();
    afx_msg void OnCond2();
    afx_msg void OnCond3();
    afx_msg void OnCond4();
    afx_msg void OnCond5();
    afx_msg LRESULT OnUpdateDisplay(WPARAM wParam, LPARAM lParam);
    afx_msg LRESULT OnUpdateTimeOnly(WPARAM wParam, LPARAM lParam);

	afx_msg void OnCancel();
    afx_msg void OnDestroy();

    DECLARE_MESSAGE_MAP()

private:
    enum {
        WM_UPDATE_DISPLAY = WM_USER + 100,
        WM_UPDATE_TIME_ONLY = WM_USER + 101
    };
};

#endif
复制代码
MultiTimerDlg.cpp
cpp 复制代码
// MultiTimerDlg.cpp : implementation file
#include "stdafx.h"
#include "MultiTimer.h"
#include "MultiTimerDlg.h"
#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// CAboutDlg 对话框(标准"关于"对话框)
class CAboutDlg : public CDialog
{
public:
    CAboutDlg();
    enum { IDD = IDD_ABOUTBOX };
protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { }
void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); }
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()

// CMultiTimerDlg 消息映射
BEGIN_MESSAGE_MAP(CMultiTimerDlg, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON_START, OnButtonStart)
    ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
    ON_MESSAGE(WM_UPDATE_DISPLAY, OnUpdateDisplay)
    ON_MESSAGE(WM_UPDATE_TIME_ONLY, OnUpdateTimeOnly)
    ON_BN_CLICKED(IDC_BUTTON_COND1, OnCond1)
    ON_BN_CLICKED(IDC_BUTTON_COND2, OnCond2)
    ON_BN_CLICKED(IDC_BUTTON_COND3, OnCond3)
    ON_BN_CLICKED(IDC_BUTTON_COND4, OnCond4)
    ON_BN_CLICKED(IDC_BUTTON_COND5, OnCond5)
END_MESSAGE_MAP()

// 构造函数
CMultiTimerDlg::CMultiTimerDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CMultiTimerDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_nCurrentStage = 0;
    m_bRunning = false;
    m_bThreadRunning = false;
    m_pTimerThread = NULL;

    for (int i = 0; i < 5; i++)
    {
        m_dTimeStages[i] = 0.0;
        m_bCondition[i] = false;   // 初始全部禁用
    }
    InitializeCriticalSection(&m_csSync);
}



// 初始化对话框
BOOL CMultiTimerDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // 添加"关于..."菜单项
    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        CString strAboutMenu;
        strAboutMenu.LoadString(IDS_ABOUTBOX);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    SetIcon(m_hIcon, TRUE);
    SetIcon(m_hIcon, FALSE);

    // 设置编辑框默认值
    SetDlgItemText(IDC_EDIT_TIME1, _T("5.0"));
    SetDlgItemText(IDC_EDIT_TIME2, _T("10.0"));
    SetDlgItemText(IDC_EDIT_TIME3, _T("8.0"));
    SetDlgItemText(IDC_EDIT_TIME4, _T("12.0"));
    SetDlgItemText(IDC_EDIT_TIME5, _T("6.0"));

    // 初始显示
    SetDlgItemText(IDC_STATIC_STAGE, _T("未启动"));
    SetDlgItemText(IDC_STATIC_CURRENT_TIME, _T("0.00"));

    // 设置条件按钮初始文本
    SetDlgItemText(IDC_BUTTON_COND1, _T("禁用"));
    SetDlgItemText(IDC_BUTTON_COND2, _T("禁用"));
    SetDlgItemText(IDC_BUTTON_COND3, _T("禁用"));
    SetDlgItemText(IDC_BUTTON_COND4, _T("禁用"));
    SetDlgItemText(IDC_BUTTON_COND5, _T("禁用"));

    return TRUE;
}

// 系统命令处理
void CMultiTimerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

// 绘制图标(当对话框最小化时)
void CMultiTimerDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this);
        SendMessage(WM_ICONERASEBKGND, (WPARAM)dc.GetSafeHdc(), 0);
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

HCURSOR CMultiTimerDlg::OnQueryDragIcon()
{
    return (HCURSOR)m_hIcon;
}

// 条件按钮1独立处理
void CMultiTimerDlg::OnCond1()
{
    m_bCondition[0] = !m_bCondition[0];
    SetDlgItemText(IDC_BUTTON_COND1, m_bCondition[0] ? _T("启用") : _T("禁用"));
}

// 条件按钮2独立处理
void CMultiTimerDlg::OnCond2()
{
    m_bCondition[1] = !m_bCondition[1];
    SetDlgItemText(IDC_BUTTON_COND2, m_bCondition[1] ? _T("启用") : _T("禁用"));
}

// 条件按钮3独立处理
void CMultiTimerDlg::OnCond3()
{
    m_bCondition[2] = !m_bCondition[2];
    SetDlgItemText(IDC_BUTTON_COND3, m_bCondition[2] ? _T("启用") : _T("禁用"));
}

// 条件按钮4独立处理
void CMultiTimerDlg::OnCond4()
{
    m_bCondition[3] = !m_bCondition[3];
    SetDlgItemText(IDC_BUTTON_COND4, m_bCondition[3] ? _T("启用") : _T("禁用"));
}

// 条件按钮5独立处理
void CMultiTimerDlg::OnCond5()
{
    m_bCondition[4] = !m_bCondition[4];
    SetDlgItemText(IDC_BUTTON_COND5, m_bCondition[4] ? _T("启用") : _T("禁用"));
}

// 启动按钮
void CMultiTimerDlg::OnButtonStart()
{
    if (m_bRunning)
    {
        MessageBox(_T("程序正在运行,请等待完成或点击停止"), _T("提示"), MB_ICONINFORMATION);
        return;
    }

    // 读取五个时间值
    CString strTime;
    for (int i = 0; i < 5; i++)
    {
        UINT nEditID = IDC_EDIT_TIME1 + i;
        GetDlgItemText(nEditID, strTime);
        m_dTimeStages[i] = atof(strTime);
        if (m_dTimeStages[i] <= 0.0)
        {
            CString strMsg;
            strMsg.Format(_T("请输入有效的时间%d(大于0)"), i + 1);
            MessageBox(strMsg, _T("错误"), MB_ICONERROR);
            return;
        }
    }

    // 严格顺序检查:从时间段1开始,如果条件1为 false 则无法启动
    if (!m_bCondition[0])
    {
        MessageBox(_T("条件1未启用,无法启动!请先启用条件1。"), _T("提示"), MB_ICONINFORMATION);
        return;
    }

    // 重置状态
    m_nCurrentStage = 0;
    m_bRunning = true;
    m_bThreadRunning = true;

    m_elapsedTimer.Start();
    UpdateDisplay(m_nCurrentStage, 0.0);

    // 禁用按钮
    GetDlgItem(IDC_BUTTON_START)->EnableWindow(FALSE);
    GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(TRUE);
    // 使用不同的循环变量名,避免重定义
    for (int j = 0; j < 5; j++)
        GetDlgItem(IDC_BUTTON_COND1 + j)->EnableWindow(FALSE);

    // 启动线程
    m_pTimerThread = AfxBeginThread(TimerThreadProc, this, THREAD_PRIORITY_NORMAL);
}

// 停止按钮
void CMultiTimerDlg::OnButtonStop()
{
    if (!m_bRunning) return;
    StopTimer();

    // 计算当前时间段内经过的时间(用于显示)
    double dTotalElapsed = m_elapsedTimer.NowSec();
    double dPrevTotal = 0.0;
    for (int i = 0; i < m_nCurrentStage; i++)
        dPrevTotal += m_dTimeStages[i];
    double dCurrentTime = dTotalElapsed - dPrevTotal;
    if (dCurrentTime < 0) dCurrentTime = 0;
    if (dCurrentTime > m_dTimeStages[m_nCurrentStage]) dCurrentTime = m_dTimeStages[m_nCurrentStage];

    UpdateDisplay(m_nCurrentStage, dCurrentTime);

    // 恢复按钮状态
    GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);
    GetDlgItem(IDC_BUTTON_COND1)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON_COND2)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON_COND3)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON_COND4)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON_COND5)->EnableWindow(TRUE);
}

// 停止计时器内部函数
void CMultiTimerDlg::StopTimer()
{
    if (!m_bRunning) return;
    m_bThreadRunning = false;
    m_bRunning = false;
    if (m_pTimerThread)
    {
        WaitForSingleObject(m_pTimerThread->m_hThread, 1000);
        m_pTimerThread = NULL;
    }
}

// 只更新时间值(不更新阶段)
LRESULT CMultiTimerDlg::OnUpdateTimeOnly(WPARAM, LPARAM lParam)
{
    double dCurrentTime = *(double*)lParam;
    CString strTime;
    strTime.Format(_T("%.2f"), dCurrentTime);
    SetDlgItemText(IDC_STATIC_CURRENT_TIME, strTime);
    delete (double*)lParam;
    return 0;
}

// 完整更新(阶段+时间)
LRESULT CMultiTimerDlg::OnUpdateDisplay(WPARAM wParam, LPARAM lParam)
{
    if (!IsWindow(m_hWnd))
    {
        if (lParam) delete (double*)lParam;
        return 0;
    }
    if (wParam == -1)  // 恢复按钮的专用消息
    {
        GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);
        GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);
        for (int i = 0; i < 5; i++)
            GetDlgItem(IDC_BUTTON_COND1 + i)->EnableWindow(TRUE);
        return 0;
    }
    int nStage = (int)wParam;
    double dCurrentTime = *(double*)lParam;
    UpdateDisplay(nStage, dCurrentTime);
    delete (double*)lParam;
    return 0;
}


void CMultiTimerDlg::UpdateDisplay(int nStage, double dCurrentTime)
{
    CString strStage;
    strStage.Format(_T("时间%d"), nStage + 1);
    SetDlgItemText(IDC_STATIC_STAGE, strStage);

    CString strTime;
    strTime.Format(_T("%.2f"), dCurrentTime);
    SetDlgItemText(IDC_STATIC_CURRENT_TIME, strTime);
}

// 计时器线程函数
UINT CMultiTimerDlg::TimerThreadProc(LPVOID pParam)
{
    CMultiTimerDlg* pDlg = (CMultiTimerDlg*)pParam;
    const double UPDATE_INTERVAL = 0.01;

    CElapsed updateTimer;
    updateTimer.Start();

    int nCurStage = 0;  // 始终从0开始
    double dStageOffset = 0.0;  // 前面阶段总时长

    while (pDlg->m_bThreadRunning && nCurStage < 5)
    {
        // 检查当前阶段的条件
        if (!pDlg->m_bCondition[nCurStage])
            break;

        double dTotalElapsed = pDlg->m_elapsedTimer.NowSec();
        double dElapsedInStage = dTotalElapsed - dStageOffset;

        if (dElapsedInStage >= pDlg->m_dTimeStages[nCurStage])
        {
            // 当前阶段完成,检查下一个阶段是否存在且条件为 true
            int next = nCurStage + 1;
            if (next >= 5)
                break;  // 所有阶段完成
            if (!pDlg->m_bCondition[next])
                break;  // 下一个阶段条件为 false,停止整个流程

            // 条件满足,切换到下一阶段
            nCurStage = next;
            dStageOffset += pDlg->m_dTimeStages[nCurStage - 1];

            EnterCriticalSection(&pDlg->m_csSync);
            pDlg->m_nCurrentStage = nCurStage;
            LeaveCriticalSection(&pDlg->m_csSync);

            double* pZero = new double(0.0);
            pDlg->PostMessage(WM_UPDATE_DISPLAY, (WPARAM)nCurStage, (LPARAM)pZero);
            continue;
        }

        // 发送时间更新
        double* pTime = new double(dElapsedInStage);
        pDlg->PostMessage(WM_UPDATE_TIME_ONLY, 0, (LPARAM)pTime);

        // 精确等待
        double dWaitTime = UPDATE_INTERVAL - fmod(updateTimer.NowSec(), UPDATE_INTERVAL);
        if (dWaitTime > 0)
            Sleep(static_cast<DWORD>(dWaitTime * 1000));
    }

    // 线程结束处理
    EnterCriticalSection(&pDlg->m_csSync);
    pDlg->m_bRunning = false;
    pDlg->m_bThreadRunning = false;
    LeaveCriticalSection(&pDlg->m_csSync);

    // 显示最终值(发送消息)
    if (nCurStage < 5 && !pDlg->m_bCondition[nCurStage])
    {
        double dTotalElapsed = pDlg->m_elapsedTimer.NowSec();
        double dElapsedInStage = dTotalElapsed - dStageOffset;
        if (dElapsedInStage > pDlg->m_dTimeStages[nCurStage])
            dElapsedInStage = pDlg->m_dTimeStages[nCurStage];
        double* pFinal = new double(dElapsedInStage);
        pDlg->PostMessage(WM_UPDATE_DISPLAY, (WPARAM)nCurStage, (LPARAM)pFinal);
    }
    else if (nCurStage >= 5)
    {
        double* pFinal = new double(pDlg->m_dTimeStages[4]);
        pDlg->PostMessage(WM_UPDATE_DISPLAY, (WPARAM)4, (LPARAM)pFinal);
    }

    // 恢复按钮状态(发送消息到主线程,不要直接操作 UI)
    pDlg->PostMessage(WM_UPDATE_DISPLAY, -1, 0);

    return 0;
}

CMultiTimerDlg::~CMultiTimerDlg()
{
    DeleteCriticalSection(&m_csSync);
}

void CMultiTimerDlg::OnCancel()
{
    StopTimer();
    CDialog::OnCancel();
}

void CMultiTimerDlg::OnDestroy()
{
    StopTimer();
    CDialog::OnDestroy();
}
相关推荐
会编程的土豆4 小时前
日常做题 vlog
数据结构·c++·算法
22信通小白4 小时前
USRP初学者使用手册(基础配置及bug记录)——Linux+Clion(单台X310收发)
linux·运维·c++·5g·bug·信息与通信
郭涤生4 小时前
C++模板元编程理论基础简介
c++
CheerWWW5 小时前
C++学习笔记——栈内存与堆内存、宏、auto、std::array
c++·笔记·学习
WBluuue5 小时前
数据结构与算法:二项式定理和二项式反演
c++·算法
yashuk5 小时前
C语言 vs. C++ ,哪个更适合初学者?
c语言·c++·面向对象编程·初学者·学习路径
-许平安-5 小时前
MCP项目笔记十(客户端 MCPClient)
c++·笔记·ai·raii·mcp·pluginapi·plugin system
一只旭宝5 小时前
【C++ 入门精讲2】函数重载、默认参数、函数指针、volatile | 手写笔记(附完整代码)
c++·笔记
旖-旎5 小时前
哈希表(存在重复元素||)(4)
数据结构·c++·算法·leetcode·哈希算法·散列表