Интернет-журнал "Домашняя лаборатория", 2008 №7 - Журнал «Домашняя лаборатория»
В Visual Basic нет типа данных, в котором переменная имела бы ровно три значения, зато Visual Basic, как и многие языки, позволяет программисту создавать собственные типы. Наша задача — создать тип, в котором переменная принимает ровно три значения: считает, пауза, в нуле — а затем объявить этим типом переменную Режим_работы_секундомера.
В нашем случае для создания такого типа достаточно записать выше процедур конструкцию:
Private Enum типРежим_работы_секундомера
считает
пауза
в_нуле
End Enum
Это не процедура, хоть и похожа. Слово Enum означает, что тип — перечислимый. Это означает, что мы обязаны были придумать и в этой конструкции перечислить имена всех возможных значений переменной этого типа, что мы и сделали. Поскольку каждый тип должен иметь свое имя (Integer, String…), нам тоже пришлось придумать имя новому типу (типРежим_работы_секундомера) и указать его в заголовке.
Теперь, когда новый тип определен, можно любую переменную объявить этим типом, что мы и делаем:
Dim Режим_работы_секундомера As типРежим_работы_секундомера
Замечание для новичков: Новички с трудом воспринимают смысл строк со стоящими рядом похожими именами, таких, например, как только что написанная. Новичку кажется, что эти имена означают одно и то же. Должен сказать, что профессиональные программисты специально обозначают близкие вещи похожими именами, им так больше нравится (можете себе вообразить?!). Например, у них может встретиться кнопка с именем сmdПодать_сюда_Ляпкина_Тяпкина и в этом же проекте переменная с именем strПодать_сюда_Ляпкина_Тяпкина. Для того, чтобы отличать одно от другого, они начинают каждое имя с префикса, который говорит программисту (не компьютеру!) о том, кому принадлежит имя (кнопке (префикс cmd), строковой переменной (префикс str) или кому-нибудь другому). Со временем вы поймете неизбежность такого подхода, а пока вам придется быть внимательным и не путать близкие имена.
Делаем секундомер
Поместим на форму элементы управления и дадим им имена:
Таймер_секундомера Циферблат_секундомера Кнопка_пуска_паузы_секундомера Кнопка_обнуления_секундомера а также не забудем метку и рамочку.
Будем использовать следующие переменные:
Dim Режим_работы_секундомера As типРежим_работы_секундомера
Dim Время_на_секундомере As Single
Dim Время_запуска_секундомера As Single
Dim Время_на_паузе_секундомера As Single
Dim Цифра_десятых As Long
Обратите внимание, что все переменные времени объявлены, как дробные числовые, а не как Date. Сейчас вам станет ясно, почему.
Давайте по порядку. Таймер нужен секундомеру для того же, для чего и часам, а именно — чтобы вовремя менялись цифры на циферблате, а собственный таймер нужен для того, чтобы не зависеть от часов. Поскольку нам нужно отслеживать десятые доли секунды, установим ему интервал поменьше, например 10 или 5 — не играет роли. Когда секундомер считает, таймер секундомера должен работать: Таймер_секундомера. Enabled = True
а когда он в паузе или в нуле, таймер должен стоять Таймер_секундомера. Enabled = False
Для отсчета времени на секундомере мы будем использовать известную вам функцию Timer (не путайте с элементом управления Timer), потому что она выдает с приемлемой для нас точностью десятые доли секунды (какой же секундомер без десятых). Поскольку она выдает число секунд, прошедших с полуночи, а нам нужно число секунд, прошедших с момента запуска секундомера, да еще с учетом того, что во время паузы на секундомере уже стояли какие-то показания, нам придется поразмыслить, как это сделать.
Засечем в момент пуска секундомера значение функции Timer оператором Время_запуска_секундомера = Timer
Тогда в каждый момент времени после запуска секундомера выражение (Timer — Время_запуска_секундомера) как раз и будет равняться числу секунд, прошедших с момента запуска секундомера. А если нам удастся правильно засечь Время_на_паузе_секундомера (выраженное в секундах, прошедших с момента пуска), то дело решит оператор
Время_на_секундомере = (Timer — Время_запуска_секундомера) + Время_на_паузе_секундомера
Если поместить его в процедуру таймера, то он будет оперативно выдавать нужное Время_на_секундомере. Задача решена.
Теперь займемся внешним видом показаний секундомера. Время_на_секундомере — это дробное число — количество секунд. Например, такое — 67,2. А нам хотелось бы получить его в таком виде — 00:01:07.2. Для этого нам нужно как-то преобразовать число секунд в стандартный формат времени. Дело решает оператор:
Циферблат_секундомера.Text = DateAdd ("s", Время_на_секундомере, #12:00:00 AM#)
Здесь время #12:00:00 AM# обозначает, как ни странно, полночь по-американски. Задача решена.
Но функция DateAdd оставляет за бортом десятые доли секунды. Попробуем выделить их из числа Время_на_секундомере. Для этого мысленно проведем такую цепочку операций с использованием функции Int:
Целая часть = Int (Время_на_секундомере)
Дробная часть = Время_на_секундомере — Целая часть Цифра_десятых = Int (10 * Дробная часть)
Сведем эти три оператора в один:
Цифра_десятых = Int(10 * (Время_на_секундомере
Int(Время_на_секундомере)))
и дополним оператор вывода времени на циферблат секундомера: Циферблат_секундомера.Text = DateAdd ("s", Время_на_секундомере, #12:00:00 AM#) & & Цифра_десятых
Здесь знак & является удобным заменителем знака + для сборки данных в одну строку. Я рекомендую пользоваться именно им, так как компьютер его уж никак не спутает со сложением чисел. Между целой и дробной частью секунд я решил поставить точку. Обратите внимание, что знак & без проблем соединяет данные трех разных типов: функцию DateAdd, строку и число Цифра_десятых.
Ну вот, пожалуй, и все объяснения. В остальном вы сможете разобраться сами, прочитав текст программы, так как ничего нового по сравнению с будильником не обнаружите.
После этого самые дотошные скажут мне, что нечего было огород городить — создавать новый тип данных, когда можно было обойтись логической переменной Секундомер_считает. Верно, но неправильно. Потому что нужно оставлять простор для дальнейшего развития проекта. Например, вы можете захотеть, чтобы во время паузы цифры на секундомере мигали, а в нуле — нет. Отличить одно состояние от другого вам и поможет переменная Режим_работы_секундомера.
Недостатки проекта
Мой проект работает на первый взгляд нормально, я проверял и часы, и будильник, и секундомер. Но делал я это не так тщательно, как положено при тестировании, поэтому в нем вполне возможны ошибки. Вот те, что я сумел заметить, но не стал исправлять, предоставив это вам:
В будильнике, если сигнал завершился сам, без нажатия кнопки "Выключить сигнал", то