Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

End Sub




Таймер

Создайте новый проект. Поместите на форму таймер. Установите его свойство Interval равным 10000. Сделайте двойной щелчок по таймеру. В появившуюся заготовку процедуры впишите одну строчку:

Private Sub Timer1_Timer()

Debug.Print "Процедура сработала"

End Sub

Запустите проект. Подождите немного. Через 10 секунд в окне Immediate появится строчка "Процедура сработала". Еще через 10 секунд появится такая же строчка, через 10 секунд еще одна, и так далее.

Если бы мы установили Interval равным 5000, то строчки появлялись бы каждые 5 с, а если равным 500, то - каждые 0.5 с.

 

Вывод: Таймер Timer1 - объект, вся работа которого заключается в том, чтобы через каждые Interval секунд создавать событие (импульс), которое запускает процедуру Timer1_Timer.

Таймеров в проекте может быть несколько. Все они работают независимо друг от друга и каждый имеет свою собственную процедуру, которая реагирует только на его импульсы.

Цикл без цикла

В процедуре Timer1_Timer мы можем написать все, что угодно. Например:

Dim i As Long

Private Sub Timer1_Timer()

Debug.Print "Процедура сработала", i

i = i + 1

End Sub

Установите Interval в 1000. Запустите проект. Вы увидите через каждую секунду возникающие строчки:

Процедура сработала 0

Процедура сработала 1

Процедура сработала 2

Процедура сработала 3

……………………..

Пояснение: Поскольку мы нигде не придали переменной i никакого значения, Visual Basic при первой встрече с ней в операторе Debug.Print посчитал ее равной 0. После первого выполнения процедуры Timer1_Timer переменная i стала равной 1, после второго - 2. И так далее.

Мы видим, что у нас заработал цикл, несмотря на то, что операторов цикла мы не писали! Чуть позже вы догадаетесь, как задавать начальные значения переменным цикла и вовремя делать выход из цикла.

 

Установим свойство таймера Enabled (по русски - "в рабочем состоянии") в False. Запустим проект. Никаких строк не появляется. Значит таймер выдает импульсы только тогда, когда свойство Enabled установлено в True.

Добавим в проект две кнопки. Допишем две процедуры:

Private Sub Command1_Click()

Timer1.Enabled = True

End Sub

 

Private Sub Command2_Click()

Timer1.Enabled = False

End Sub

Запустите проект. Вы увидите, что новые строчки начинают появляться только после щелчка по первой кнопке, а после щелчка по второй прекращают.

 

Что будет, если процедура Timer1_Timer еще не успела закончить работу, а от таймера уже пришел новый импульс? Проверим. Установим Interval равным 100, то есть импульсы от таймера должны посылаться примерно 10 раз в секунду. Напишем простую процедуру Timer1_Timer, которая очень долго работает:

Private Sub Timer1_Timer()

Debug.Print "Процедура таймера начала работу"

For i = 1 To 20000000: Next

Debug.Print "Процедура таймера закончила работу"

End Sub

Здесь долго работает пустой цикл For i = 1 To 20000000: Next. Несмотря на то, что делать ему нечего (тело у цикла отсутствует), у него много времени уходит на то, чтобы просто досчитать до 20 миллионов. На моем компьютере это занимает секунды две, на вашем, наверно, по-другому. Это неважно, а важно то, что за эти две секунды в процедуру врежется порядка 20 импульсов от таймера. И все как об стену горох - процедура на них не реагирует. Пока не завершит свою работу, как положено, оператором End Sub. После этого первый же следующий импульс, которому повезло, снова ее запускает. Вы увидите, что на экране не спеша, где-то раз в две секунды появляется очередная пара строчек.

 

Точность таймера невелика, а на интервалах меньше 1 секунды вообще никуда не годится. Не ждите, что если вы установили интервал равный 10, то в секунду будет послано 100 импульсов. Причина в том, что таймер вообще не способен выдавать больше 18 импульсов в секунду.

Задание 99: Запрограммируйте с помощью таймера печать чисел от 100 до 110 через 1 секунду.

 

11.3. Проект "Будильник-секундомер"

Вы узнали о Visual Basic вполне достаточно, чтобы рваться в бой. Наверняка у вас в голове зреет идея создать некий элегантный и одновременно вполне "убойный" проект строчек на 200, способный поразить в самое сердце ваших друзей и, что не менее важно, подруг. Однако, спешу вам сказать, что вы спешите. Ибо! Друзья и подруги может быть и понесут вас на руках до ближайшего кафе-мороженого, но человек, мало-мальски разбирающийся в программировании, бросив скучающий взгляд на ваше жалкое детище, спросит: "Юноша, в каком году вы кончали церковно-приходскую школу?" И не потому, что проект работает плохо, он вполне может проделывать на экране что-нибудь любопытное. Но программа! Программа! В ней невозможно разобраться! 200 строк вы осилили, а сколько вы возились? Неделю? А веди могли все сделать за пару дней! Сколько у вас процедур? 8? А нужно было 40! И так далее. Когда придет пора делать проект из 1000 строк, вы не сможете его одолеть никогда!

Что же делать? Все очень просто. Вы ни в чем не виноваты. Ведь я пока только объявил вам, что надо писать много маленьких процедур. Но ведь не научил еще, как это делать правильно. А учиться надо на примерах.

Пришла пора создать проект, "правильно" составленный из процедур. Проект "Калькулятор", написанный нами ранее, не подходит в качестве учебного пособия, потому что логика его работы слишком проста: нажал кнопку - выполнилась соответствующая процедура и все. Нужна более сложная задача. Для ее решения мы создадим совсем небольшую программу из 80 строк, включающую 15 процедур. Поскольку создаваемая программа представляет для многих из вас неведомую страну, я буду применять метод изложения "за ручку", который я уже применял в 1.3. Повторяйте за мной все, что я буду делать.

Начнем с постановки задачи.

Постановка задачи

Создать часы-будильник следующего вида:

 

 

В постановке задачи нужно с максимальной подробностью описать, что должен делать ваш проект с точки зрения пользователя (а не программиста!). Ввиду очевидности того, что изображено на картинке, я поясню только то, чего не видно или может быть неправильно понято:

· На верхнем циферблате - текущее время суток (системное время Windows) в часах, минутах и секундах

· Под ним - дата и день недели (02.08.00 означает 2 августа 2000 года)

· Пользователь имеет возможность редактировать содержимое циферблата установки времени будильника

· При нажатии на кнопку "Выключить будильник" надпись на кнопке меняется на "Включить будильник", а надпись над циферблатом меняется на "Будильник отключен"

· При срабатывании будильника раздается какая-нибудь продолжительная мелодия, которая замолкает при нажатии на кнопку "Выключить сигнал"

· Секундомер измеряет время с точностью до 0.1 сек. На картинке вы видите секундомер в момент паузы. Если нажать на ПУСК, то отсчет времени продолжится с 1 минуты 15.4 сек, которые вы видите на картинке, а надпись на кнопке сменится на ПАУЗА. Если снова нажать на кнопку, цифры на секундомере снова замрут.

· При нажатии на кнопку "Обнулить" секундомер сбрасывается в ноль и останавливается. На циферблате - 0:00:00.0.

Делим проект на части

Нельзя делать сразу весь проект одновременно. То есть было бы фатальной ошибкой сразу все нарисовать и пытаться все сразу программировать. Не пытайтесь ломать весь веник сразу, ломайте по прутикам. Когда Наполеон видел, что его армия слишком мала, чтобы сразу одолеть противника, он весь удар сосредотачивал на одной какой-то части вражеской армии и, разбив ее, нападал на следующую.

Надо разделить проект на части. Это не всегда легко. Некоторые проекты, особенно игры, представляют на первый взгляд единое неразрывное целое, так что нужен некоторый опыт, чтобы увидеть, "из каких кубиков построен домик". В нашем случае все более-менее просто. Три части просматриваются сразу, это

· Часы (с датой и днем недели)

· Будильник

· Секундомер

Отлично! За какую из этих частей браться вначале? Для этого нужно сначала решить, зависят ли друг от друга отдельные части. Здесь очень пригодился бы некоторый опыт программирования. А если его нет, подойдет житейский опыт. Действительно, если бы вы мастерили будильник-секундомер из шестеренок, что бы в нем от чего зависело? Ну, ясно, что будильник не сработал бы без часов, ведь он должен чувствовать, когда время на циферблатах часов и будильника совпадает. А вот часы ходят и без будильника, они от него не зависят. Секундомер же, видимо, представляет собой полностью независимую часть со своим механизмом.

Итак, проект распался на две независимые части:

· Часы и будильник

· Секундомер

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

Итак, мы разделили проект на части и определили порядок выполнения частей:

1. Часы

2. Будильник

3. Секундомер

Беремся за часы. И тут опять пошла дележка. Чем раньше заниматься - временем суток (будем называть это просто часами), датой или днем недели? Шестеренки смутно подсказывают нам, что дата переключается в полночь, а значит момент переключения зависит от часов. Значит дату будем делать позже часов. А день недели, ясно, определяется датой.

Итак, окончательная последовательность такая:

1. Часы (время суток)

2. Дата

3. День недели

4. Будильник

5. Секундомер

 

Лирическое отступление (утешение): Если вам не удалось разделить ваш проект на части, или вы чувствуете, что разделили неправильно, это не значит, что нужно от проекта отказываться. Программируйте напропалую! В процессе программирования отдельные части постепенно (не без мучений и многократных досадных переделок) встанут на свои места.

 

Ну что ж, "Задачи ясны, цели определены, за работу, товарищи!", как говорили при социализме.

Делаем часы

Создаем новый проект. Первым делом берем элемент управления Shape и рисуем вокруг будущего циферблата часов красивую рамочку. Если вы хотите сделать что-нибудь более выдающееся, чем скромная рамочка на картинке, и украсить часы необыкновенными шедеврами графики, то на здоровье, пожалуйста!. Но не сейчас. Потом. Когда все заработает. Иначе потонете в подробностях и не откопаете в золотых лепесточках нужную вам стальную шестеренку.

Я не дал объекту Shape1 никакого другого имени, потому что не собираюсь упоминать его в программе. Все свойства я ему установил в режиме проектирования и не хочу, чтобы рамочка как-то себя "вела" в режиме работы.

Внутрь рамочки я поместил текстовое поле для того, чтобы компьютер показывал в нем время. Для красоты я свойством BorderStyle убрал у него бордюрчик, а свойство Alignment настроил так, чтобы любой текст внутри поля располагался по центру. Настроил название, размер, стиль и цвет шрифта.

Дадим текстовому полю имя Циферблат_часов. Еще раз предупреждаю: не надо сокращенных имен типа Циф_час. Сэкономив сейчас копейку, вы потом проиграете рубль.

 

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

Циферблат_часов.Text = Time

и на часах оно будет. Попробуйте. Но как сделать, чтобы оно менялось? Нужно, чтобы этот оператор выполнялся не реже раза в секунду. Может быть, использовать оператор цикла? Но я должен вас предупредить, что на этом пути вас ждут разнообразные трудности, в суть которых не так-то легко вникнуть. Назову только одну. Вот, предположим, у вас заработал оператор цикла для часов. Тут вам захотелось включить секундомер. Значит, нужно, чтобы заработал другой оператор цикла - для секундомера. Значит, нужно выходить из цикла для часов. Так что же - часам останавливаться?

Ввиду вышеизложенного программисты для таких дел используют таймеры. Для часов свой, для секундомера свой.

 

Поместим на форму таймер и дадим ему имя Таймер_часов. Зададим ему интервал = 2500. Напишем такую программу:

Private Sub Таймер_часов_Timer()

Циферблат_часов.Text = Time

End Sub

Запустите проект. Вы увидите, что часы показывают правильное время, но обновляется оно раз в две-три секунды. Зададим интервал = 100. В этом случае процедура будет выполняться около 10 раз в секунду и не пропустит момента смены системного времени. Проверьте.

Ну что, все! Часы готовы.

Занимаемся датой

Размещаем на форме рамочку и текстовое поле для даты. Даем полю имя Циферблат_даты. Чтобы там появилась дата, достаточно выполнить оператор

Циферблат_даты.Text = Date

Если его поместить в ту же процедуру таймера часов, то задача будет решена. Но мне жаль компьютер. 24 часа в сутки по 10 раз в секунду он будет спрашивать у Windows, какое нынче число, и стараться вывести в текстовое поле одну и ту же дату, хотя делать это нужно только два раза - при запуске проекта и в полночь. Здесь еще и вопрос экономии: бегая, как белка в колесе, компьютер тратит ресурсы (силы), нужные в это же время для другого дела, для того же секундомера хотя бы. Вспоминаем: при запуске проекта вырабатывается событие FormLoad, а полночь - это когда показания часов равны нулю. Ага. Дописываем программу:

Private Sub Form_Load()

Циферблат_даты.Text = Date 'Это чтобы дата появлялась на циферблате при запуске проекта

End Sub

 

Private Sub Таймер_часов_Timer()

Циферблат_часов.Text = Time

If Time = 0 Then Циферблат_даты.Text = Date 'Это чтобы дата менялась в полночь

End Sub

Чтобы проверить, как работает проект, переставьте системные часы Windows на "Двенадцать без пяти". Только потом не забудьте переставить обратно.

Все работает, но мы начинаем допускать погрешности против правильного стиля программирования, которые в будущем могут выйти нам боком:

Первое. Показания часов напрашиваются быть переменной величиной. Ведь они нам еще понадобятся и для будильника. Мы их анализируем, а это лучше делать с переменной величиной, а не со свойством Циферблат_часов.Text или функцией Time. Поэтому придумаем переменную Время_на_часах, объявим ее, как имеющую тип Date и будем пользоваться только ей.

Второе. Как при запуске проекта, так и в полночь нам придется менять не только дату, но и день недели. Я предвижу повторяющийся фрагмент как минимум из двух операторов (пока это только один оператор Циферблат_даты.Text = Date). Поэтому оформим его, как процедуру с именем Смена_даты_и_дня_недели.

Третье. Поскольку у нас появились переменные, поручим Бэйсику при помощи оператора Option Explicit присматривать, не забыли ли мы какую-нибудь переменную объявить.

С учетом всего вышесказанного перепишем программу:

Option Explicit

Dim Время_на_часах As Date

 

Private Sub Form_Load()

Смена_даты_и_дня_недели

End Sub

 

Private Sub Таймер_часов_Timer()

Время_на_часах = Time

Циферблат_часов.Text = Время_на_часах

If Время_на_часах = 0 Then Смена_даты_и_дня_недели

End Sub

 

Private Sub Смена_даты_и_дня_недели()

Циферблат_даты.Text = Date

End Sub

В этот момент вы совершенно искренне и с большим чувством можете сказать: "Ну зачем все эти усложнения? Ведь все и так работает!" В ответ на это я могу только отослать вас к началу раздела.

Занимаемся днем недели

Копируем объекты. Нам не хочется трудиться, размещая на форме, а потом еще и настраивая рамку и текстовое поле для дня недели. Замечаем, что они почти такие же, как для даты. Скопируем их, как это принято в Windows. Можно копировать по-отдельности, а удобнее вместе. Для этого обведите их рамочкой, при этом оба объекта выделятся, после чего скопируйте их хотя бы при помощи пунктов Copy, Paste меню Edit. Visual Basic при этом задаст вам про каждый объект вопрос, смысл которого вы поймете позже, а пока он вам не нужен. Отвечайте No.

 

 

Даем полю имя Циферблат_дня_недели. Чтобы там появился правильный день недели, нужно выполнить оператор

Циферблат_дня_недели.Text = WeekdayName(DatePart("w", Date, vbMonday))

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

Сначала хорошенько еще раз разберитесь с функциями WeekdayName и DatePart из 11.1. А теперь нужно разобраться со скобками. Вы поняли, какая пара скобок к какой функции относится? Если нет, то я перепишу один оператор в два:

А = DatePart("w", Date, vbMonday)

Циферблат_дня_недели.Text = WeekdayName(А)

Здесь А будет равняться номеру дня недели (число от 1 до 7). Функция WeekdayName по номеру дня недели получает строковое название этого дня.

 

Раз мы организовали процедуру Смена_даты_и_дня_недели, то нам нет нужды вспоминать, в какие моменты времени проект должен менять день недели. Вставляем наш оператор в эту процедуру:

Private Sub Смена_даты_и_дня_недели()

Циферблат_даты.Text = Date

Циферблат_дня_недели.Text = WeekdayName(DatePart("w", Date, vbMonday))

End Sub

 

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

 

Итак, часы с датой и днем недели готовы.

Знакомимся с типом Boolean

Чтобы грамотно запрограммировать будильник, нам нужно познакомиться с новым типом переменных величин - Boolean. Это так называемый логический тип. В 5.7 вы уже позакомились с логическими выражениями. Их главная черта в том, что они могут принимать всего два значения - True и False (истина и ложь). Логическая переменная величина отличается той же чертой. Объявляется она так:

Dim С As Boolean

А зачем она нужна, выяснится из следующего параграфа.

Делаем будильник

Поместим на форму все объекты, необходимые для будильника. Дадим им имена:

· Метка_будильника

· Циферблат_будильника

· Кнопка_включения_выключения_будильника

· Кнопка_выключения_сигнала

В циферблат занесем в режиме проектирования текст 12:00:00. Просто для того, "чтобы было".

 

Давайте пока забудем о кнопках, метках и прочих подробностях, а возьмем быка за рога и заставим будильник исполнять мелодию при совпадении времени на часах и на будильнике.

Вспомните, как вы занимались музыкой в "Калькуляторе". Поместите на форму Microsoft Multimedia Control 6.0 и дайте ему имя "Плеер". Для работы с мелодией нам нужно 5 операторов:

Плеер.DeviceType = "Sequencer"

Плеер.FileName = "c:\Windows\Media\Canyon.mid"

Эти два достаточно выполнить один раз за время работы проекта при его запуске.

Плеер.Command = "Open"

Плеер.Command = "Play"

Эти два нужно выполнять каждый раз, когда нужно начать воспроизведение сигнала. А этот оператор:

Плеер.Command = "Close"

каждый раз, чтобы его закончить.

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

Вот как дополнился теперь наш проект (я показываю только те процедуры, которых коснулись дополнения):

Private Sub Form_Load()

Плеер.DeviceType = "Sequencer"

Плеер.FileName = "c:\Windows\Media\Canyon.mid"

Смена_даты_и_дня_недели

End Sub

 

Private Sub Таймер_часов_Timer()

Время_на_часах = Time

Циферблат_часов.Text = Время_на_часах

If Время_на_часах = 0 Then Смена_даты_и_дня_недели

If Время_на_часах = Циферблат_будильника.Text Then Включить_сигнал_будильника

End Sub

 

Private Sub Включить_сигнал_будильника()

Плеер.Command = "Open"

Плеер.Command = "Play"

Здесь жирным шрифтом выделены новые по сравнению с предыдущим вариантом строки проекта.

Запустите проект и установите время на циферблате будильника. Устанавливайте аккуратно, по одной цифре за раз. Не трогайте двоеточия. Почему - скажу чуть позже. Дождитесь, когда будильник зазвонит. Завершите работу проекта, не дожидаясь конца мелодии.

 

Пора заняться кнопками и меткой. Подумаем вот о чем. Во время работы проекта будильник в каждый момент времени может находиться в одном из двух состояний: установлен или не установлен. Компьютер должен в любой момент времени знать, в каком именно, чтобы принять решение - звонить или не звонить. В таких случаях, когда компьютер должен в любой момент что-то знать или помнить, программист всегда придумывает переменную величину, дает ей подходящее имя и тип и организует хранение в этой переменной нужной компьютеру информации. Так было у нас с переменной Время_на_часах, которая нужна была компьютеру для перестановки дат.

Придумаем переменную и дадим ей имя Будильник_установлен. Каким типом ее объявить? Для этого надо понять, а какие значения будет принимать эта переменная? По смыслу задачи значений два - "да" и "нет". Можно придать ей тип String, но хорошая практика программирования диктует тип Boolean. Вы скоро поймете, что он удобнее.

 

Теперь попробуем вообразить, что должно происходить при включении будильника:

· Нужно сообщить компьютеру, что будильник установлен

· Нужно, чтобы текст над циферблатом был такой: "Будильник установлен на:"

· Нужно, чтобы текст на кнопке был такой: "Выключить будильник"

В соответствии с этим пишем процедуру:

Private Sub Включить_будильник()

Будильник_установлен = True

Метка_будильника.Caption = "Будильник установлен на:"

Кнопка_включения_выключения_будильника.Caption = "Выключить будильник"

End Sub

 

Теперь попробуем вообразить, что должно происходить при выключении будильника:

· Нужно сообщить компьютеру, что будильник не установлен

· Нужно, чтобы текст над циферблатом был такой: "Будильник отключен"

· Нужно, чтобы текст на кнопке был такой: "Включить будильник"

В соответствии с этим пишем процедуру:

Private Sub Выключить_будильник()

Будильник_установлен = False

Метка_будильника.Caption = "Будильник отключен"

Кнопка_включения_выключения_будильника.Caption = "Включить будильник"

End Sub

 

Теперь нам удивительно легко написать процедуру щелчка по кнопке включения-выключения будильника:

Private Sub Кнопка_включения_выключения_будильника_Click()

If Будильник_установлен Then Выключить_будильник Else Включить_будильник

End Sub

Обратите внимание, что благодаря удачному выбору имен и структуры программы язык программы по своей понятности приближается к естественному, человеческому.

Поскольку переменная Будильник_установлен имеет логическое значение True или False, ее можно использовать подобно функции IsNumeric из 5.9 в качестве условия оператора If. Поэтому совсем необязательно было писать If Будильник_установлен = True Then ….

Процедуру щелчка по кнопке выключения сигнала приведу без пояснений:

Private Sub Кнопка_выключения_сигнала_Click()

Плеер.Command = "Close"

End Sub

 

В последних четырех процедурах заключена вся механика работы кнопок и метки. Последнее, о чем надо позаботиться, это о том, чтобы будильник звенел только тогда, когда включен:

If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then Включить_сигнал_будильника

 

Будильник готов. Приведу полностью программу всего нашего проекта, причем строки, касающиеся секундомера, выделю курсивом, чтобы вы пока не обращали на них внимания:

Option Explicit

 

Private Enum типРежим_работы_секундомера

считает

пауза

в_нуле

End Enum

Dim Режим_работы_секундомера As типРежим_работы_секундомера

Dim Время_на_часах As Date

Dim Будильник_установлен As Boolean

Dim Время_на_секундомере As Single

Dim Время_запуска_секундомера As Single

Dim Время_на_паузе_секундомера As Single

Dim Цифра_десятых As Long

 

 

'НАЧАЛЬНАЯ УСТАНОВКА МЕХАНИЗМА

 

Private Sub Form_Load()

Плеер.DeviceType = "Sequencer"

Плеер.FileName = "c:\Windows\Media\Canyon.mid"

Смена_даты_и_дня_недели

Выключить_будильник

Секундомер_обнулить

End Sub

 

 

'ПРОЦЕДУРЫ РАБОТЫ ЧАСОВ

 

Private Sub Таймер_часов_Timer()

Время_на_часах = Time

Циферблат_часов.Text = Время_на_часах

If Время_на_часах = 0 Then Смена_даты_и_дня_недели

If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then Включить_сигнал_будильника

End Sub

 

Private Sub Смена_даты_и_дня_недели()

Циферблат_даты.Text = Date

Циферблат_дня_недели.Text = WeekdayName(DatePart("w", Date, vbMonday))

End Sub

 

 

'ПРОЦЕДУРЫ РАБОТЫ БУДИЛЬНИКА

 

Private Sub Кнопка_включения_выключения_будильника_Click()

If Будильник_установлен Then Выключить_будильник Else Включить_будильник

End Sub

 

Private Sub Включить_будильник()

Будильник_установлен = True

Метка_будильника.Caption = "Будильник установлен на:"

Кнопка_включения_выключения_будильника.Caption = "Выключить будильник"

End Sub

 

Private Sub Выключить_будильник()

Будильник_установлен = False

Метка_будильника.Caption = "Будильник отключен"

Кнопка_включения_выключения_будильника.Caption = "Включить будильник"

End Sub

 

Private Sub Включить_сигнал_будильника()

Плеер.Command = "Open"

Плеер.Command = "Play"

End Sub

 

Private Sub Кнопка_выключения_сигнала_Click()

Плеер.Command = "Close"

End Sub

 

Private Sub Form_Terminate()

Кнопка_выключения_сигнала_Click

End Sub

 

 

'ПРОЦЕДУРЫ РАБОТЫ СЕКУНДОМЕРА

 

Private Sub Таймер_секундомера_Timer()

Время_на_секундомере = Timer - Время_запуска_секундомера + Время_на_паузе_секундомера

Цифра_десятых = Int(10 * (Время_на_секундомере - Int(Время_на_секундомере)))

Циферблат_секундомера.Text = DateAdd("s", Время_на_секундомере, #12:00:00 AM#) & "." & Цифра_десятых

End Sub

Private Sub Кнопка_пуска_паузы_секундомера_Click()

If Режим_работы_секундомера <> считает Then Секундомер_запустить Else Секундомер_остановить

End Sub

Private Sub Кнопка_обнуления_секундомера_Click()

Секундомер_обнулить

End Sub

Private Sub Секундомер_запустить()

Время_запуска_секундомера = Timer

Режим_работы_секундомера = считает

Таймер_секундомера.Enabled = True

Кнопка_пуска_паузы_секундомера.Caption = "ПАУЗА"

End Sub

Private Sub Секундомер_остановить()

Время_на_паузе_секундомера = Время_на_секундомере

Режим_работы_секундомера = пауза

Таймер_секундомера.Enabled = False

Кнопка_пуска_паузы_секундомера.Caption = "ПУСК"

End Sub

Private Sub Секундомер_обнулить()

Время_на_паузе_секундомера = 0

Режим_работы_секундомера = в_нуле

Таймер_секундомера.Enabled = False

Кнопка_пуска_паузы_секундомера.Caption = "ПУСК"

Циферблат_секундомера.Text = "0:00:00.0"

End Sub

Обратите внимание, что при запуске проекта я в процедуре Form_Load выключаю для удобства будильник, а в процедуре Form_Terminate закрывается звуковой файл, если пользователь не удосужился закрыть его кнопкой. В ней мы обращаемся к процедуре обработки события Кнопка_выключения_сигнала_Click, как к процедуре пользователя. Это вполне допустимо.

Напоминаю, что событие Form_Terminate наступает только тогда, когда мы завершаем работу проекта, щелкнув по кнопке Close (крестику в правом верхнем углу формы), а не кнопкой End на панели инструментов.

 

Знакомимся с перечислимым типом данных

Чтобы грамотно запрограммировать секундомер, нам нужно познакомиться с новым типом переменных величин. Необходимость в нем в нашем проекте вытекает вот откуда. Если будильник в каждый момент времени может находиться в одном из двух состояний (установлен или не установлен), то секундомер - в трех: считает, стоит в паузе, стоит в нуле. Придумаем переменную и дадим ей имя Режим_работы_секундомера. Объявить ее типом Boolean явно недостаточно, ведь в типе Boolean всего два возможных значения, а нам нужно три. Можно придать ей тип String, но хорошая практика программирования диктует другое.

В Visual Basic нет типа данных, в котором переменная имела бы ровно три значения, зато Visual Basic, как и многие языки, позволяет программисту создавать собственные типы. Наша задача - создать тип, в котором переменная принимает ровно три значения: считает, пауза, в нуле - а затем объявить этим типом переменную Режим_работы_секундомера.

В нашем случае для создания такого типа достаточно записать выше процедур конструкцию:

Private Enum типРежим_работы_секундомера

считает

пауза

в_нуле

End Enum

Это не процедура, хоть и похожа. Слово Enum означает, что тип - перечислимый. Это означает, что мы обязаны были придумать и в этой конструкции перечислить имена всех возможных значений переменной этого типа, что мы и сделали. Поскольку каждый тип должен иметь свое имя (Integer, String, …), нам тоже пришлось придумать имя новому типу (типРежим_работы_секундомера) и указать его в заголовке.

Теперь, когда новый тип определен, можно любую переменную объявить этим типом, что мы и делаем:

Dim Режим_работы_секундомера As типРежим_работы_секундомера

 

Замечание для новичков: Новички с трудом воспринимают смысл строк со стоящими рядом похожими именами, таких, например, как только что написанная. Новичку кажется, что эти имена означают одно и то же. Должен сказать, что профессиональные программисты специально обозначают близкие вещи похожими именами, им так больше нравится (можете себе вообразить?!). Например, у них может встретиться кнопка с именем cmdПодать_сюда_Ляпкина_Тяпкина и в этом же проекте переменная с именем 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, строку ". " и число Цифра_десятых.

 

Ну вот, пожалуй, и все объяснения. В остальном вы сможете разобраться сами, прочитав текст программы, так как ничего нового по сравнению с будильником не обнаружите.

 

После этого самые дотошные скажут мне, что нечего было огород городить - создавать новый тип данных, когда можно было обойтись логической переменной Секундомер_считает. Верно, но неправильно. Потому что нужно оставлять простор для дальнейшего развития проекта. Например, вы можете захотеть, чтобы во время паузы цифры на секундомере мигали, а в нуле – нет. Отличить одно состояние от другого вам и поможет переменная Режим_работы_секундомера.

Недостатки проекта

Мой проект работает на первый взгляд нормально, я проверял и часы, и будильник, и секундомер. Но делал я это не так тщательно, как положено при тестировании, поэтому в нем вполне возможны ошибки. Вот те, что я сумел заметить, но не стал исправлять, предоставив это вам:

 

· В будильнике, если сигнал завершился сам, без нажатия кнопки "Выключить сигнал", то в следующий раз он не прозвучит, так как файл не закрыт. Справиться с этим недостатком вам поможет событие Плеер_Done, которое возникает при завершении воспроизведения мелодии. А можно, наверное, выкрутиться еще проще, как мы сделали в Калькуляторе.

· В будильнике пользователь может изменять время сигнала, не дождавшись конца мелодии, что может привести к необходимости нового запуска мелодии, пока она еще не отзвучала. Здесь вам поможет событие Циферблат_будильника_GotFocus.

· Самое досадное то, что если вы во время установки времени будильника случайно хоть на мгновение сотрете двоеточие или напишете вместо цифры букву или как-нибудь по-другому нарушите правильный формат времени, то проект аварийно завершит работу, потому что оператор (If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then Включить_сигнал_будильника) не сможет понять, чему равно время Циферблат_будильника.Text. Здесь вам понадобятся средства, предотвращающие ввод в текстовое окно неправильных данных. Самое простое из них - функция IsDate, которая подобно функции IsNumeric из 5.9 определяет, можно ли считать ее аргумент правильным временем и правильной датой или это белиберда. Подумайте, вам вполне по силам справиться с этой проблемой.

· В течение той секунды, когда время на часах и будильнике одинаково, процедура таймера выполняется с десяток раз, а значит и сигнал будильника запускается с десяток раз. Это нехорошо.

· В циферблате даты месяц хорошо бы показывать не числом, а текстом.

· Я не запускал секундомер в полночь.

Таймер и моделирование

Теперь о роли таймера в компьютерном конструировании поведения реальных механизмов. Заглянем-ка еще раз в процедуру таймера часов:

Private Sub Таймер_часов_Timer()

Время_на_часах = Time

Циферблат_часов.Text = Время_на_часах

If Время_на_часах = 0 Then Смена_даты_и_дня_недели

If Будильник_установлен And Время_на_часах = Циферблат_будильника.Text Then Включить_сигнал_будильника

End Sub

Вы видите, что эта процедура является главным мотором, приводящим в движение весь механизм часов с будильником, главным штабом, планирующим всю их работу. Таймер, как сердце, несколько раз в секунду посылает импульсы, каждый из которых заставляет выполнится эту процедуру. А что за операторы составляют тело процедуры? Это как раз главные операторы проекта, которые, выполняясь, в свою очередь управляют выполнением вспомогательных процедур Смена_даты_и_дня_недели и Включить_сигнал_будильника. За один импульс таймера механизм совершает весь цикл своего функционирования с начала до конца и выполняет всю работу, которую положено выполнить: время на часах обновлено; если подошел момент, то именно на этом импульсе заменены дата и день недели; и если подошел момент, то именно на этом же импульсе отдан приказ на включение сигнала будильника. На следующем импульсе все повторяется. И так беспрерывно, бесконечно.

Этот принцип применения таймера подойдет для компьютерного конструирования работы бесчисленного множества других механизмов и аппаратов: холодильника, ситезатора, автоматической метеостанции, беспилотного космического аппарата и пр. и пр. Нужно только знать принцип их работы и вы сможете создать проект, "вживую" показывающий их деятельность на кухне или в космосе. Это называется моделированием. Вот, например, мы с вами создали модель будильника, которая настолько хороша, что работает не хуже оригинала. Однако, мы с вами не копировали работу реальных шестеренок бабушкиных ходиков, нам это было не нужно. Таким образом, нужно понимать, что мы создали модель только внешнего, а не внутреннего поведения механического будильника. Внутреннее поведение нашего будильника совсем другое.

Можно вполне моделировать и работу механизмов под управлением человека: автомобиля, корабля, самолета и др. В этом случае роль человека будете исполнять вы, щелкая мышкой по созданным вами кнопкам и рычагам управления автомобиля. Кстати, вы уже создали такой проект. Ведь будильник - это тоже механизм, работающий под управлением человека.

 

Задание 99-1: Это задание - на любителя. Его делать не нужно, если вы выполните проект "Шахматные часы" (см. ниже). Усовершенствуйте часы. Пусть они по вашему желанию показывают время в любом из нескольких десятков городов мира. Пользователю достаточно ввести название города в текстовое поле. Вам для программирования нужно самим знать поясное время в других городах. Для этого можете сделать двойной щелчок мышкой по индикатору времени на панели задач и в открывшемся окне загляните в список Time Zone. Основой для программирования можете сделать большой оператор Select Case. Будильник не должен обращать внимание на чужое время на циферблате, он все равно должен звонить по нашему местному времени.

Задание 99-2: " Шахматные часы". Это задание нужно сделать обязательно. По шахматным правилам шахматист не может думать над ходом бесконечно. На обдумывание первых, скажем, 40 ходов ему дается, скажем, 2 часа. Таким образом, партия, дошедшая до 41 хода, не может продолжаться дольше 4 часов. На следующие ходы тоже отводится какой-то лимит времени. Чтобы шахматисты могли следить за тем, сколько времени им осталось на обдумывание, существуют специальные шахматные часы. Они представляют собой единый корпус с двумя циферблатами - счетчиками времени, двумя кнопками и двумя флажками. Перед началом партии шахматистов А и В каждый счетчик показывает 2 часа. Пусть шахматисту А выпало начинать. Как только партия начинается, судья запускает счетчик А, который начинает обратный отсчет времени, уменьшая свои показания. Таким образом, в каждый момент партии счетчик показывает, сколько времени осталось шахматисту на обдумывание. Пока работает счетчик А, счетчик В, естественно, стоит. Как только шахматист А сделал ход, он тут же нажимает кнопку А, которая останавливает его счетчик и запускает счетчик В. За обдумывание принимается шахматист В. Сделав ход, он нажимает кнопку В, которая останавливает его счетчик и запускает счетчик А. И так далее. Если шахматист просрочит время, его флажок падает - он проиграл.

Для удобства шахматистов добавьте к часам счетчик ходов.




Поделиться с друзьями:


Дата добавления: 2014-12-23; Просмотров: 525; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.35 сек.