Назад | Начало урока | Вперед
Содержание

День 5 (продолжение 1)

  • Добавление второго таймера к приложению
  • Добавление к приложению новых переменных
  • Запуск и остановка счетчика-таймера
    Добавление второго таймера к приложению

    Как вы убедились выше, добавление к приложению одного таймера представляет довольно простую задачу. Все что необходимо сделать - вызвать функцию SetTimer() внутри инициализирующей функции OnInitDialog, а затем в функции OnTimer() добавить программный код таймера, который обрабатывает сообщение таймера WM_TIMER. Но иногда встречаются ситуации, когда надо использовать в приложении несколько таймеров. Это несколько более сложная задача.

    Вверх

    Добавление к приложению новых переменных
    Для таймера часов необходима лишь одна переменная, которая используется для обновления текущего времени, отображаемого в окне приложения. Чтобы использовать второй таймер, необходимо с соответствующими элементами управления связать дополнительные переменные. Эти дополнительные переменные перечислены в таблице 5.4

    Таблица 5.4 Переменные соответствующие элементам управления

    Элемент управления: Имя переменной: Категория: Тип:
    IDC_STATICCOUNT m_sCount value CString
    IDC_STARTTIME m_сStartTime Control CButton
    IDC_BSTOPTIMER m_cStopTime Control CButton
    IDC_EINTERVAL m_iInterval value int

      Добавим переменные. Воспользуемся для этого Мастером добавления переменных (Add Member Variable Wizard) к управляющим элементам формы. Выполните следующие пошаговые инструкции :

    1. Введите имя переменной m_sCount, тип -CString,доступ -public, категория value. Это переменная для элемента IDC_STATICCOUNT

    2. Введите переменную для управляющего элемента IDC_STARTTIME. Введите имя переменной m_сStartTime, тип - CButton, доступ -public, категория control.

    3. Введите переменную для управляющего элемента IDC_BSTOPTIMER. Введите имя переменной m_cStopTime, тип - CButton, доступ -public, категория control.

    4. Добавим переменную связанную с элементом управления IDC_EINTERVAL

      Введите имя переменной int m_iInterval, тип - int, доступ -public, категория value. В поле Min Value введите 1 в качестве минимального значения, а в поле Max Value -100 000 в качестве максимального значения.

      Таким образом вы определили диапазон изменения интервальной переменной таймера. Теперь если в соответствующем поле пользователь введет значение, выходящее за пределы допустимого диапазона, Visual C++ автоматически выведет подсказку с интервалом значений данной переменной. Вывод подсказки обеспечивает функция UpdataData(), которая вызывается функцией OnEnChangeEinterval.

    5. Перейдите на вкладку Class View и пользуясь вашими знаниями добавьте переменную-член класса CTimerDlg. int - в качестве типа, m_iCount в качестве имени. Способ доступа - private

      Последняя из добавленных с помощью рабочей области переменных i_Count фактически используется как счетчик, значение в котором возрастает на единицу с каждым событием таймера.

      АВ:обратите внимание что есть теперь две переменных: m_iCount типа CString и m_sCount типа int. Эти переменные для управляющего элемента IDC_STATICCOUNT Первую переменную будем использовать как счетчик, вторую переменную будем использовать для вывода значения счетчика на экран через элемент IDC_STATICCOUNT.

      Все объявленные нами переменные будет автоматически добавлены мастером как в объявление класса, так и в конструктор класса CTimersDlg. Разумеется в конструктор класса не добавлены переменные m_сStartTime и m_сStopTime, так как эти переменные ассоциированы с кнопками и их не надо инициализировать в конструкторе.

      CTimersDlg::CTimersDlg(CWnd* pParent /*=NULL*/)
      : CDialog(CTimersDlg::IDD, pParent)
      , m_sTime(_T(""))
      , m_iInterval(0)
      , m_sCount(_T(""))
      , m_iCount(0)
      {

      m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
      }

    6. Добавьте функцию для обработки сообщения о событии EN_CHANGE для элемента управления с идентификатором IDC_EINTERVAL - этим элементом является окно редактирования. Отредактируйте функцию согласно листинга 5.3

      Листинг 5.3


      void CTimersDlg::OnEnChangeEinterval()
      {
      //если это элемент управления RICHEDIT,то элемент управления не будет
      //посылать это уведомление, если вы не переопределите функцию
      //CDialog::OnInitDialog()
      //и не вызовете CRichEditCtrl(). SetEventMask()
      // с флагом ENM_CHANGE, объединенным с помощью операции OR(или) с маской

      //здесь вставьте ваш код обрабочика событий от элемента управления
      UpdateData(TRUE);

      }

    АВ: Повторяю, что вывод подсказки о некорректном вводе интервала обеспечивает функция UpdataData(), которая вызывается вышеуказанной функцией OnEnChangeEinterval. Эта же функция обеспечивает инициализацию переменной m_iInterval в случае правильного ввода значения пользователем.

    Функция void CTimersDlg::OnEnChangeEinterval() вызывается периодически постоянно согласно карты сообщений. Вот карта сообщений:

    BEGIN_MESSAGE_MAP(CTimersDlg, CDialog)

    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_BN_CLICKED(IDC_EXIT, OnBnClickedExit)
    ON_WM_TIMER()
    ON_EN_CHANGE(IDC_EINTERVAL, OnEnChangeEinterval)
    ON_BN_CLICKED(IDC_BSTARTTIME, OnBnClickedBstarttime)
    ON_BN_CLICKED(IDC_BSTOPTIMER, OnBnClickedBstoptimer)
    END_MESSAGE_MAP()

    Вверх

    Запуск и остановка счетчика-таймера

    Чтобы привести второй таймер в рабочее состояние,необходимо выполнить следующее:

    Для этого выполните следующие пошаговые инструкции:

    1. Отредактируйте функцию OnInitDialog как указано ниже :

      Листинг 5.4


      BOOL CTimerыDlg::OnInitDialog
      {
      CDialog::OnInitDialog();

      . . . //часть кода пропущена

      //инициализировать интервал отсчета
      m_iInterval = 100;

      //обновить диалоговое окно
      UpdateData(FALSE);

      //запустить таймер часов
      SetTimer(ID_CLOCK_TIMER,1000,NULL);

      return TRUE;

      }

    2. Добавьте функцию для обработки сообщения BN_CLICED от кнопки IDC_BSTARTTIME Отредактируйте функцию OnBnClicedBstarttime в соответствии с листингом 5.5

      Листинг 5.5


      void CTimersDlg::OnBnClickedBstarttime()
      {
      //обновить переменные
      UpdateData(TRUE);

      //инициализировать счетчик m_iCount
      m_iCount = 0;

      //Форматировать (Format) счетчик для отображения
      m_sCount.Format("%d",m_iCount);

      //обновить диалоговое окно
      UpdateData(FALSE);

      //Запустить таймер-счетчик
      SetTimer(ID_COUNT_TIMER,m_iInterval,NULL);

      }

      АВ: Как видим код запускающий таймер-часы :

      //запустить таймер часов
      SetTimer(ID_CLOCK_TIMER,1000,NULL);

      помещен в функцию инициализации BOOL CTimerDlg::OnInitDialog

      а код,запускающий таймер-счетчик :

      //Запустить таймер-счетчик
      SetTimer(ID_COUNT_TIMER,m_iInterval,NULL);

      помещен в функцию нажатия кнопки Start Timer:
      void CTimersDlg::OnBnClickedBstarttime()

      Причем в первом случае интервал срабатывания таймера указан константой (1000), а во втором случае интервал срабатывания второго таймера указан при помощи переменной (m_iInterval).

      Эта переменная в первый раз инициализируется программно в функции
      BOOL CTimerыDlg::OnInitDialog

      //инициализировать интервал отсчета
      m_iInterval = 100;
      UpdateData(FALSE);//вывести значение на экран

      В дальнейшей работе программы пользователь будет вводить значение этой переменной интерактивно через управляющий элемент Edit Box

      Вы помните для этой переменной было предусмотрено ограничение на ввод (защита от некорректного ввода) Чтобы интервал вводился в разумных пределах.Минимум -1. максимум 1000.


    3. Добавьте функцию,обрабатывающую сообщение BN_CLICED от кнопки IDC_BSTOPTIMER
      Отредактируте функцию OnBnClicedBstoptimer следующим образом.

      OnBnClicedBstoptimer
      {

      //остановить таймер
      KillTimer(ID_COUNT_TIMER);
      }
    4. Отредактируйте функцию OnTimer,так как указано ниже

      Листинг 5.6

      //Обновленная функция OnTimer


      void CTimersDlg::OnTimer(UINT nIDEvent)
      {

      //получить текущее время
      CTime curTime = CTime::GetCurrentTime();

      switch(nIDEvent)
      {

      case ID_CLOCK_TIMER:
      m_sTime = curTime.Format("%H:%M:%S");
      break;

      case ID_COUNT_TIMER:

      m_iCount++;
      m_sCount.Format("%d",m_iCount);
      break;

      }

      UpdateData(FALSE);

      CDialog::OnTimer(nIDEvent);

      )


    АВ:в аргумент функции OnTimer передается идентификатор таймера, чтобы программа могла определить значения какого таймера выводить на экран. Напомню функция OnTimer обрабатывает сообщения WM_TIMER. Вероятнее всего эти сообщения поступают от обоих таймеров?

    Анализ :

    1. В теле функции OnInintDialog инициализируется переменная m_Interval. В качестве начального значения ей присваивается 100. Чтобы в диалоговом окне отобразить начальное значение переменной, вызывается функция UpdateData().

      //инициализировать интервал отсчета

      m_iInterval = 100;

      UpdateData(FALSE);

    2. Сначала функция OnBnClicedBstarttime синхронизирует значение переменных значениями, хранящимися в элементах управления, позволяя вам определить текущее значение переменной m_Interval.

      UpdateData(TRUE);

      Далее инициализируется переменная m_iCount, которой присваивается 0 в качестве начального значения и форматируется значение, держащееся в переменной m_sCount типа CString,которое затем используется для обновления диалогового окна.

      //инициализировать счетчик m_iCount
      m_iCount = 0;

      //Форматировать (Format) счетчик для отображения
      m_sCount.Format("%d",m_iCount);

      //обновить диалоговое окно
      UpdateData(FALSE);

      Наконец функция запускает таймер ID_COUNT_TIMER используя значение интервала таймера, записанное в переменной m_Interval.

      //Запустить таймер
      SetTimer(ID_CLOCK_TIMER,m_iInterval,NULL);

    3. Функция OnBnClicedBstoptimer выполняет единственное действие -остановку таймера. Для этого вызывается функция KillTimer(), которой в качестве единственного аргумента передается идентификатор таймера.

      //остановить таймер
      KillTimer(ID_COUNT_TIMER);

    4. Интересно обстоят дела с функцией OnTimer() :
      она все еще содержит код для обработки события от таймера часов:

      CTime curTime = CTime::GetCurrentTime();
      m_sTime = curTime.Format("%H:%M:%S");

      Чтобы функция могла так же обрабатывать события от таймера-счетчика, она должна уметь определять вызвавший ее таймер. Единственным аргументом функции OnTimer() является идентификатор таймера. Чтобы определить таймер,вызвавший данную функцию, и выбрать тот код, который нужно выполнить, можно воспользоваться оператором выбора switch.


      void CTimersDlg::OnTimer(UINT nIDEvent)
      {
      //получить текущее время
      CTime curTime = CTime::GetCurrentTime();

      switch(nIDEvent)
      {

      case ID_CLOCK_TIMER:
      m_sTime = curTime.Format("%H:%M:%S");
      break;

      case ID_COUNT_TIMER:

      m_iCount++;
      m_sCount.Format("%d",m_iCount);
      break;

      }

      UpdateData(FALSE);

      CDialog::OnTimer(nIDEvent);

      )


      При этом программный код таймера-часов остается неизменным.(Листинг 5.2):

      CTime curTime = CTime::GetCurrentTime();
      m_sTime = curTime.Format("%H:%M:%S");

      Код счетчика-таймера размещается в соответствующей ветви оператора выбора switch. Этот код увеличивает значение счетчика и обновляет значение переменной m_sCount.

      m_iCount++;
      m_sCount.Format("%d",m_iCount);

      Теперь можно скомпилировать и запустить приложение. Задайте значение интервала таймера и запустите таймер.

      Приложение работает хорошо. Но в данном приложении имеется "ошибка" (Смотри далее текст).


      АВ: Вопрос такой: когда и кто вызывает эту функцию CDialog::OnTimer(nIDEvent); и кто передает ей нужный аргумент? Поскольку значения переменных в этой функции все время изменяются, это мы видим на экране во время исполнения программы, то вероятно эта функция вызывается циклически самой программой Windows согласно карты сообщений.

      Назад | Вверх | Вперед
      Содержание

      Hosted by uCoz