Занимаемся днем недели
Нам не хочется трудиться, размещая в рамке метку, а потом еще и настраивая ее для дня недели. Замечаем, что она такая же, как для даты. Скопируем ее, как это принято в Windows, щелкнув по ней правой клавишей мыши и выбрав в контекстном меню Copy, а затем щелкнув правой клавишей мыши в рамке Часы и выбрав в контекстном меню Paste.
Даем метке имя Циферблат_дня_недели. Чтобы там появился правильный день недели, нужно выполнить оператор
Циферблат_дня_недели.Text = Format(Now, "dddd")
Раз мы организовали процедуру Смена_даты_и_дня_недели, то нам нет нужды вспоминать, в каких местах проекта оператор должен менять день недели. Вставляем его в эту процедуру:
Sub Смена_даты_и_дня_недели()
Циферблат_даты.Text = Format(Now, "Long Date")
Циферблат_дня_недели.Text = Format(Now, "dddd")
End Sub
Итак, часы с датой и днем недели готовы.
Делаем будильник
Поместим в рамку с именем Будильник все объекты, необходимые для будильника:
· Метку с именем Метка_будильника
· Текстовое поле с именем Циферблат_будильника
· Кнопку с именем Кнопка_включения_выключения_будильника
· Кнопку с именем Кнопка_выключения_звонка
В циферблат занесем в режиме проектирования текст 12:00:00. Просто для того, «чтобы было». В качестве циферблата мы выбрали текстовое поле, а не метку. А это для того, чтобы иметь возможность в режиме работы вручную устанавливать время будильника.
Заставляем будильник звучать. Давайте пока забудем обо всех этих кнопках, метках и прочих подробностях, а возьмем быка за рога и заставим будильник при совпадении времени на часах и на будильнике исполнять какую-нибудь мелодию.
Вспомните, как мы занимались музыкой в «Плеере». Поместите на форму Windows Media Player и дайте ему имя Звонок. Сделайте его невидимым. Для настройки звонка достаточно трех операторов в процедуре Form1_Load:
Звонок.AutoStart = False 'Чтобы не запускался слишком рано
Звонок.PlayCount = 0 'Чтобы закончив играть, начинал снова
Звонок.FileName = "Mozart's Symphony No. 40.RMI"
Эта троица, несмотря на то, что встретится в программе скорее всего только один раз, в глазах программиста просится стать процедурой. Здесь принцип такой:
Если группа операторов (или даже один сложный оператор) представляет собой некое единое целое в том смысле, что решает какую-то свою отдельную задачу, то оформляйте ее процедурой.
Это улучшит понятность и читабельность программы – один из важнейших факторов ее качества.
Запустит воспроизведение мелодии оператор Звонок.Play(), но он должен выполниться только тогда, когда время на циферблате часов совпадет со временем на циферблате будильника.
Вот как дополнится теперь наш проект (я показываю только те процедуры, которых коснулись дополнения):
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Настройка_звонка()
Смена_даты_и_дня_недели()
End Sub
Private Sub Таймер_часов_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Таймер_часов.Tick
Время_на_часах = Format(Now, "HH:mm:ss")
Циферблат_часов.Text = Время_на_часах
If Время_на_часах = "00:00:00" Then Смена_даты_и_дня_недели()
If Время_на_часах = Циферблат_будильника.Text Then Звонок.Play()
End Sub
Sub Настройка_звонка()
Звонок.AutoStart = False 'Чтобы не запускался слишком рано
Звонок.PlayCount = 0 'Чтобы закончив играть, начинал снова
Звонок.FileName = "Mozart's Symphony No. 40.RMI"
End Sub
Запустите проект и установите время на циферблате будильника. Дождитесь, когда будильник зазвонит. Завершите работу проекта, не дожидаясь конца мелодии.
Два состояния будильника. Пора заняться кнопками и меткой. Подумаем вот о чем. Во время работы будильник в каждый момент времени может находиться в одном из двух состояний: «Установлен» или «Не установлен». Состояние «Установлен» означает, что вы хотите, чтобы будильник, когда подойдет его срок, звонил. Состояние «Не установлен» означает, что вы не хотите, чтобы будильник звонил, даже когда подойдет его срок. Компьютер должен в любой момент времени знать, в каком именно состоянии находится будильник, чтобы принять решение – звонить или не звонить. В таких случаях, когда компьютер должен в любой момент что-то знать или помнить, программист всегда придумывает переменную величину, дает ей подходящее имя и тип и организует хранение в этой переменной той самой информации, которую нужно помнить.
Придумаем переменную и дадим ей имя Будильник_установлен. Каким типом ее объявить? Для этого надо понять, а какие значения будет принимать эта переменная? По смыслу задачи значений два – «да» и «нет». Можно придать ей тип String, но хорошая практика программирования диктует тип Boolean. Вы скоро привыкнете к нему и поймете, что он удобнее.
Кнопки и метка будильника. Теперь попробуем вообразить, что должно происходить при включении будильника:
· Нужно сообщить компьютеру, что будильник установлен
· Нужно, чтобы текст над циферблатом был такой: «Будильник установлен на»
· Нужно, чтобы текст на кнопке был такой: «Выключить будильник»
В соответствии с этим пишем процедуру:
Sub Включить_будильник()
Будильник_установлен = True
Метка_будильника.Text = "Будильник установлен на"
Кнопка_включения_выключения_будильника.Text = "Выключить будильник"
End Sub
Теперь попробуем сообразить, что должно происходить при выключении будильника:
· Нужно сообщить компьютеру, что будильник не установлен
· Нужно, чтобы текст над циферблатом был такой: «Будильник отключен»
· Нужно, чтобы текст на кнопке был такой: «Включить будильник»
В соответствии с этим пишем процедуру:
Sub Выключить_будильник()
Будильник_установлен = False
Метка_будильника.Text = "Будильник отключен"
Кнопка_включения_выключения_будильника.Text = "Включить будильник"
End Sub
Теперь нам удивительно легко написать процедуру щелчка по кнопке включения-выключения будильника:
Private Sub Кнопка_включения_выключения_будильника_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Кнопка_включения_выключения_будильника.Click
If Будильник_установлен Then Выключить_будильник() Else Включить_будильник()
End Sub
Обратите внимание, что благодаря удачному выбору имен и структуры программы язык программы по своей понятности приближается к естественному, человеческому.
Напомню, что поскольку переменная Будильник_установлен имеет логическое значение True или False, ее можно в одиночку использовать в качестве условия оператора If. Поэтому совсем не обязательно было писать
If Будильник_установлен = True Then ….
Процедуру щелчка по кнопке выключения звонка приведу без пояснений:
Private Sub Кнопка_выключения_звонка_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Кнопка_выключения_звонка.Click
Звонок.Stop()
End Sub
В последних четырех процедурах заключена вся механика работы кнопок и метки. Теперь надо позаботиться о том, чтобы будильник звенел только тогда, когда включен. Для этого вместо оператора
If Время_на_часах = Циферблат_будильника.Text Then Звонок.Play()
мы пишем оператор
If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then Звонок.Play()
Теперь нужно решить, должен ли будильник после запуска проекта быть включен или выключен. Можно решить и так и эдак, на работоспособность проекта это никак не повлияет. Я предпочитаю, чтобы он был выключен, поэтому пишу в процедуру Form1_Load оператор Выключить_будильник().
Программа часов с будильником целиком. Часы с будильником готовы. Приведу полностью их программу:
Public Class Form1
Inherits System.Windows.Forms.Form
Windows Form Designer generated code
Dim Время_на_часах As String
Dim Будильник_установлен As Boolean
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs)Handles MyBase.Load
Настройка_звонка()
Выключить_будильник()
Смена_даты_и_дня_недели()
End Sub
Private Sub Таймер_часов_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Таймер_часов.Tick
Время_на_часах = Format(Now, "HH:mm:ss")
Циферблат_часов.Text = Время_на_часах
If Время_на_часах = "00:00:00" Then Смена_даты_и_дня_недели()
If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then Звонок.Play()
End Sub
Sub Настройка_звонка()
Звонок.AutoStart = False
Звонок.PlayCount = 0
Звонок.FileName = "Mozart's Symphony No. 40.RMI"
End Sub
Sub Смена_даты_и_дня_недели()
Циферблат_даты.Text = Format(Now, "Long Date")
Циферблат_дня_недели.Text = Format(Now, "dddd")
End Sub
Sub Включить_будильник()
Будильник_установлен = True
Метка_будильника.Text = "Будильник установлен на:"
Кнопка_включения_выключения_будильника.Text = "Выключить будильник"
End Sub
Sub Выключить_будильник()
Будильник_установлен = False
Метка_будильника.Text = "Будильник отключен"
Кнопка_включения_выключения_будильника.Text = "Включить будильник"
End Sub
Private Sub Кнопка_включения_выключения_будильника_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Кнопка_включения_выключения_будильника.Click
If Будильник_установлен Then Выключить_будильник() Else Включить_будильник()
End Sub
Private Sub Кнопка_выключения_звонка_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Кнопка_выключения_звонка.Click
Звонок.Stop()
End Sub
End Class
Еще раз о стиле. Вы, наверное, заметили, что если после запуска проекта вы заставляли будильник звенеть несколько раз, то каждый раз он начинал мелодию не с начала, а с того места, где вы звонок выключили. Мне это все равно, но если вам это не нравится, то вы должны включать звонок не одним оператором
Звонок.Play()
а парой операторов:
Звонок. CurrentPosition = 0
Звонок.Play()
Но в этом случае вместо
If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then Звонок.Play()
вам придется писать
If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then
Звонок.CurrentPosition = 0
Звонок.Play()
End If
Вы видите, что оператор включения звонка теряет прозрачность и краткость. Выход один: оформлять эту пару операторов, как процедуру Включить_звонок. Тогда можно будет написать
If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then Включить_звонок()
Вот теперь оператор включения звонка приобрел элегантность и максимальную понятность. Теперь никто не обвинит нас в корявости стиля. К тому же, если нам в будущем захочется как-то улучшить включение звонка, то все изменения мы сможем делать внутри процедуры Включить_звонок, не трогая сам оператор.
Делаем секундомер
Размести в проекте второй таймер, дав ему имя Таймер_секундомера. Поместим в рамку с именем Секундомер элементы управления для секундомера и дадим им имена:
· Циферблат_секундомера (это метка)
· Кнопка_пуска_паузы_секундомера
· Кнопка_обнуления_секундомера
а также не забудем метку с текстом «Секундомер».
Таймер нужен секундомеру для того же, для чего и часам, а именно – чтобы вовремя менялись цифры на циферблате, а собственный таймер предпочтительно (но в нашем случае не обязательно) иметь для того, чтобы не зависеть от часов. Поскольку нам нужно отслеживать сотые доли секунды, установим ему интервал меньше 10. Когда секундомер считает, таймер секундомера должен работать:
Таймер_секундомера.Enabled = True
а когда он в паузе или в нуле, таймер может и отдохнуть:
Таймер_секундомера.Enabled = False
Режимы работы секундомера. Если будильник в каждый момент времени может находиться в одном из двух состояний (режимов) – установлен или не установлен, то секундомер – в трех: считает, стоит в паузе, стоит в нуле. Придумаем переменную и дадим ей имя Режим _секундомера. Объявить ее типом Boolean явно недостаточно, ведь в типе Boolean всего два возможных значения, а нам нужно три. Здесь напрашивается перечисление:
Enum Режим
считает
пауза
в_нуле
End Enum
Dim Режим_секундомера As Режим
Для каждого режима нам придется организовать свою переменную для отсчета секунд на секундомере:
Dim Секунды_на_секундомере As Double
Dim Секунды_при_запуске_секундомера As Double
Dim Секунды_на_паузе_секундомера As Double
Обратите внимание, что эти переменные, хоть и имеют смысл времени, объявлены, как дробные числовые, а не как Date. Почему так получилось и почему переменных три, а не одна, сейчас попытаюсь пояснить.
Для отсчета времени на секундомере мы будем использовать известную вам функциюTimer класса DateAndTime (не путайте с элементом управления Timer), потому что она выдает секунды с дробной частью (нас устроят сотые доли секунды – какой же секундомер без сотых долей). Поскольку она выдает число секунд, прошедших с полуночи, а нам нужно число секунд, прошедших с момента запуска секундомера, да еще с учетом того, что во время паузы на секундомере уже стояли какие-то показания, нам придется поразмыслить, как это сделать.
Засечем в момент пуска секундомера значение функции Timer оператором
Секунды_при_запуске_секундомера = DateAndTime.Timer
Тогда в каждый момент времени после запуска секундомера выражение
DateAndTime.Timer - Секунды_при_запуске_секундомера
как раз и будет равняться числу секунд, прошедших с момента запуска секундомера. А если нам удастся правильно засечь Секунды_на_паузе_секундомера (имеющие смысл секунд, прошедших с момента пуска до паузы), то дело решит оператор
Секунды_на_секундомере = DateAndTime.Timer - Секунды_при_запуске_секундомера _
+ Секунды_на_паузе_секундомера
Если поместить его в процедуру таймера, то он будет оперативно выдавать нужные для циферблата секунды.
Внешний вид показаний секундомера. Переменная Секунды_на_секундомере – это дробное число типа Double. Например, такое – 65.28194. Но на секундомере эти самые цифры, сами понимаете, должны выглядеть по-другому – 00:01:05.28. Для этого нам нужно как-то преобразовать число секунд в стандартный формат времени. Организуем переменную и константу:
Dim Время_на_секундомере As Date
Const Полночь As Date = #12:00:00 AM#
Здесь время #12:00:00 AM# обозначает, как ни странно, полночь по-американски.
Дело решает пара операторов:
Время_на_секундомере = Полночь.AddSeconds(Секунды_на_секундомере)
Циферблат_секундомера.Text = Format(Время_на_секундомере, "mm:ss.ff")
Кнопки секундомера. Теперь нам нужно подумать о том, что должно происходить во время нажатия на кнопки секундомера. Наши размышления должны течь примерно в том же русле, что и размышления о кнопках и метке будильника. Никакой новой идеологии по сравнению с будильником вы не обнаружите. По аналогии с будильником вы, наверное, должны придти к выводу о необходимости создания трех процедур:
Sub Секундомер_обнулить()
Sub Секундомер_остановить()
Sub Секундомер_запустить()
Я думаю, эти размышления вам полезно будет довести до конца самостоятельно. А проверить себя вы сможете, заглянув в окончательный текст программы ниже. Все процедуры, касающиеся будильника собраны вместе и вы их легко найдете.
Еще о режиме. Самые дотошные из вас, разобравшись в программе, скажут мне, что нечего было огород городить – создавать перечисление, когда можно было обойтись логической переменной Секундомер_считает. Верно. Но неправильно. Потому что нужно оставлять простор для дальнейшего развития проекта. Например, вы можете в будущем захотеть, чтобы во время паузы цифры на секундомере мигали, а в нуле – нет.