在前斯博客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();
}
