Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Уроки по Delphi для чайника, который хочет стать самоваром Урок 5. Графические часы


Информационный Канал Subscribe.Ru

Другие материалы на сайте "ЕЖИдневник" 

Урок 5. Графические часы

Исходных текстов программы здесь ещё нет. Думайте над прогой пока сами! %)


Свойство Canvas. Свойства доступные только на этапе выполнения программы. Использование помощи. Создание в проекте новой формы. Тип double. Типизированные константы. Функция Pi. Функция округления и преобразования типа Round. Команды рисования (методы компонента Image). Подключение модулей. Тип TRect.

Delphi позволяет рисовать почти где угодно. Можно рисовать на форме, можно рисовать на метке, можно даже рисовать непосредственно на экране! Но лучше использовать всё-таки специальный компонент TImage, уже хотя бы потому, что этот компонент сам будет следить затем, что бы при перерисовке окна твоей программы, твои каракули тоже перерисовывались. Если ты нарисуешь что-то, например, на форме, то при сворачивании окна программы твой рисунок сотрётся.

Собственно за рисование отвечает свойство Canvas. Если у компонента есть такое свойство, то на нём можно рисовать, если нет - увы. Кстати, это свойство ты не найдёшь в инспекторе объектов. Это свойство не доступно во время разработки программы. Оно станет доступно только во время исполнения. Разумеется, к нему можно обращаться в тексте программы - ведь текст программы начнёт работать только после запуска. Как же определить есть свойство Canvas у компонента или нет? Если ты выделишь компонент на форме, свойство или событие в инспекторе объекта или поставишь курсор внутрь какого-либо слова в тексте программы и нажмёшь клавишу F1, то откроется система помощи, причём откроется она на описании выделенного объекта, свойства, события или слова (разумеется, если этот элемент в помощи имеется). Если элементу соответствует несколько записей в системе помощи, то тебя поросят выбрать нужный (в Delphi7 очень многим компонентам соответствует по 2 записи - нужно выбирать ту в которой есть аббревиатура VCL). У всех компонентов в помощи имеется ссылка Properties, при нажатии на которую откроется окно со списком всех свойств этого объекта. Тебе останется только найти нужное свойство в этом списке или установить факт его отсутствия.

Поскольку на твоей форме места для таких часов нет, предлагаю включить в нашу программу ещё одну форму.

0. С помощью кнопки «New Form» или пункта меню «File/New Form» создаёшь новую форму. Жмёшь на кнопочку «Save». Вводишь какое-нибудь имя для этого нового модуля (конечно можно оставить и Unit2, но лучше использовать осмысленные имена).

1. Помещаешь на новую форму компонент Image (палитра Additional, 6-я кнопка в виде картинки) на форму. Свойство Align выставляешь в alClient, в результате Image будет растекаться по всей форме.

2. Кладёшь на форму Timer. Создаёшь ему обработчик события OnTimer.

3. Объявляешь новые переменные:

var xc,yc : integer;
    u : double;

Обрати внимание. Объявление переменных может занять несколько строк, но слово var пишешь только один раз, то есть var помечает весь блок переменных, а не отдельную его строку. Тип переменных double - это вещественный тип, в нём можно хранить дробные числа. Переменные xc, yc используем для хранения координат центра часов, а в переменную u будем записывать угол поворота стрелок.

4. Объявляешь типизированные константы:

const lh : integer = 60;
      lm : integer = 100;
      ls : integer = 80;

Хотя они и называются константы, в действительности это переменные, только у них с самого начала устанавливаются значения. Значения этих элементов - длина часовой, минутной и секундной стрелок в пикселях. Ну, синтаксис, по-моему, вполне понятен.

5. В теле процедуры пишем:

  xc := Image1.Width div 2;
  yc := Image1.Height div 2;
  u := Pi/2 - Time*2*2*Pi;
  Image1.Canvas.MoveTo(xc,yc);
  Image1.Canvas.LineTo(xc+Round(lh*cos(u)),yc-Round(lh*sin(u)));

В первых двух строчках рассчитываем координаты центра Image, это и будет центр наших часов.

В третьей строчке считаем угол для часовой стрелки. Символ «/» означает деление, символ «*» умножение, «-» знак минус. Порядок действий стандартный для математических выражений, то есть сначала делим/умножаем, потом складываем/вычитаем. Функция Pi возвращает число пи. Объяснять, почему угол считается именно так, надо? Или твоих знаний геометрии хватит? Ах, да, сначала Великая Эроида, затем тяжкое наследие гуманитарного образования… Значит, угол в математике (и в Delphi) принято отсчитывать от горизонтали и против часовой стрелки. А на часах - от вертикали и по часовой. Знак «-» перед Time меняет направление отсчёта, а «пи пополам» смещает начало отсчёта на четверть оборота против часовой стрелки (то есть на цифру 12 на часах). За сутки Time меняет своё значение от 0 до 1, а часовая стрелка должна за это время сделать 2 оборота, то есть «2*2*Pi» радиан. Таким образом, мы получаем угол u в радианах. Именно в радианах этот угол и нужно подставлять в функции синуса и косинуса.

Четвёртая строка: помещаем указатель в центр.

Пятая строка: проводим линию от текущего положения указателя до точки с координатами xc+Round(lh*cos(u)),yc-Round(lh*sin(u)). Координату x получаем, сложив xc и длину стрелки, умноженную на косинус угла. Поскольку косинус угла - величина вещественная, а координаты должны быть целочисленными, нужно использовать функцию Round - округление. Вопрос на засыпку: почему нельзя писать lh*Round(cos(u))? Минус при вычислении координаты y связан с тем, что ось у направлена не вверх (как обычно принято в математике), а вниз, ну для тебя, как специалиста по Бейсику это не должно удивить.

6. Последние три строчки скопируй два раза и модифицируй для отображения минутной и секундной стрелок. Справишься?

7. Теперь нужно подключить эту новую форму к нашей программе. Перейди в Unit1.pas и найди блок, который начинается со слова uses. В этом списке указываются все модули подключённые к твоей программе. В конец этого списка через запятую поставь имя своего нового модуля. При запуске программы будет открываться твоя первая форма, чтобы открыть вторую, нужно дать команду

  Form2.Show;

Подумай, где можно дать эту команду. Кнопочку поставить или на какое событие у формы повесить (например, у формы тоже есть событие OnClick). Важно, что бы это событие вызывалось достаточно редко (один раз или по действию пользователя) - событие OnTimer не подойдёт.

Запусти программу. Часы работают. Только стрелки не стираются. Нужно очищать Image перед каждой новой отрисовкой. Самый простой способ очистить Image - закрасить его весь цветом фона.

1. Объяви новую переменную Rect типа TRect. Этот тип содержит внутри себя 4 поля - left, top, right, bottom целочисленного типа, и предназначен для определения координат прямоугольника.

2. В самом начале тела процедуры нужно присвоить полям Rect значения координат прямоугольника, который мы хотим закрасить и вызвать метод для закраски:

  Rect.Left := 0;
  Rect.Top := 0;
  Rect.Right := Image1.Width-1;
  Rect.Bottom := Image1.Height-1;
  Image1.Canvas.FillRect(Rect);

Домашнее задание. Сделать пункт 6. Сделать стрелки фигурными, а не просто состоящими из одной линии.

На следующем уроке мы сделаем нашим часам циферблат.


Мысли вслух по прошлому уроку...


Alёna: Крокодил не ловится, не растёт кокос... :о( Чёт не ладится у меня программа, много у меня к ней вопросов. Причём сплошь нецензурных... :о((

Aлександр:Ну дык это... Может как-нибудь упрОстим и ускОрим процесс? Народ ведь ждёт! Вопросы-то озвучь.

Alёna: 1. При уменьшении размера окна (после компиляции) это окно начинает дрыгаться, произвольно то увеличиваясь, то уменьшаясь.

Aлександр: Угу. Совсем неправильно! Все, которые выше метки с датой и временем - к верхнему и левому краям. Те, которые ниже - к нижнему и левому. А Memo1 к нижнему, левому и правому краям. Разве я не так с самого начала говорил?

Alёna: 2. Метки при изменении размеров формы в ту или иную сторону начинают двигаться куды им заблагорассудится, перекрывая друг друга... Я их неправильно привязала? Может, половину стОит привязать к верху, а другую к низу? И изменять размер шрифта в них всех - соответственно изменению размера формы, по образу и подобию изменения в таймере? А какой обработчик использовать?

Aлександр:
A> И изменять размер шрифта в них всех - соответственно
A> изменению размера формы, по образу и подобию изменения в таймере?
Можно и так. Хотя я говорил о центрировании меток.
A> А какой обработчик использовать?
??? Если ты хочешь добиться эффекта плавного изменения размера используй TForm1.Timer1Timer, если сразу при изменении формы - TForm1.FormResize.

Alёna: 3. Собсно метка со временем/датой тоже отплясывает чечётку. Я тебе на это уже жаловалась. Подрагивание, о котором ты мне говорил, я видела в программе Ценителя. А у меня всё совсем не так. Напомни, плиз, что ты мне предлагал сделать?

Aлександр: Упс. Кажется я слегка перестарался.

Сравни варианты:

w := Label4.Width+2*Label4.Left;
   if w <= Form1.ClientWidth
   then begin
     Label4.Font.Size := Label4.Font.Size+1;
   end;
   if w > Form1.ClientWidth
   then begin
     Label4.Font.Size := Label4.Font.Size-1;
   end;

и:

if Label4.Width+2*Label4.Left <= Form1.ClientWidth
   then begin
     Label4.Font.Size := Label4.Font.Size+1;
   end;
   if Label4.Width+2*Label4.Left > Form1.ClientWidth
   then begin
     Label4.Font.Size := Label4.Font.Size-1;
   end;

В первом случае метка должна дёргаться. Во втором - нет. Точнее во втором случае она тоже будет дёргаться, только *очень* быстро. Именно это соображение заставляет написать код с одним условием if. Но тогда в обоих случаях метка будет дёргаться:

w := Label4.Width+2*Label4.Left;
   if w <= Form1.ClientWidth
   then begin
     Label4.Font.Size := Label4.Font.Size+1;
   end
   else begin
     Label4.Font.Size := Label4.Font.Size-1;
   end;

Или:

if Label4.Width+2*Label4.Left <= Form1.ClientWidth
   then begin
     Label4.Font.Size := Label4.Font.Size+1;
   end;
   else begin
     Label4.Font.Size := Label4.Font.Size-1;
   end;

A> Напомни, плиз, что ты мне предлагал сделать?
Чтобы она не дёргалась нужно исправить условие. Вместо "Label4.Width+2*Label4.Left <= Form1.ClientWidth" написать что-то другое. Более сложное. Причём чуть-чуть за рамками того, что я уже объяснял. Ещё раз "Упс". Я в задании указал, что это "за рамками", и будет хорошо, если ты опишешь условие хотя бы словами (если в литературе не найдёшь нужного слова)? Или забыл? Впрочем, можно решить так как это сделал <забыл как зовут, ты его ещё поругала, что, дескать, всю малину тебе испортил, прислав программу на следующий же день>. Тогда всё будет в рамках. Но это не самое эффективное решение. Итого: два раза "упс"... Хреновенький из меня преподаватель. :-( Всё-таки задание оказалось слишком сложным и это моя вина. Исправляться надо.


Из писем наших читателей (избранное)...

Александр:
А> При помощи shift+tab перевожу фокус со второй на первую кнопку,
А> нажимаю enter и получаю сообщение, предусмотренное для второй кнопки ->
А> ?????????????????
Проблема в следующем. Вы написали для каждой кнопки свой обработчик события OnClick: TForm1.Button1Click и TForm1.Button2Click, но обеим кнопкам поставили в соответствие одну и ту же процедуру Button2Click. Исправьте у 1 кнопки в инспекторе объектов на Button1Click и всё заработает.

А> Но не это главное. Подскажите, ради Бога, как создать новый проект.
А> То есть, создаться он создается. Но при сохранении спрашивает, как
А> его сохранить: как файл проекта (dpr) или как программный
А> модуль (pas). В первом случае отсутствует программный
А> модуль, а при открытии файла проекта открывается текст программы, и не
А> открывается форма (несмотря на наличие файла формы).
А> Во втором случае начисто отсутствует файл dpr.
????? :-0
Когда Вы запускаете Delphi создаётся новый проект. Автоматически. Если только Вы не выставили галочку ProjectDesktop в меню "Tools/Environment Options" в блоке "Autosave options". В этом случае будет открываться тот проект с которым Вы работали раньше. Я советую эту галочку не выставлять. Если Вы уже работаете в каким-то проектом и хотите создать новый, то с помощью кнопочки "New" или пункт меню "File/New..." откроется окно, в котором нужно выбрать что создавать. В левом верхнем углу есть значок "Application" (он даже выбран по умолчанию). После нажания "Ok", новое приложение будет создано. Или выберите пункт меню "File/New Application". Если старый проект не был сохранён, то Delphi спросит сохранять или как. (Да, старый проект можно предварительно закрыть "Close all"). Новый проект лучше всего сохранять с помощью кнопочки "Save all", тогда Delphi спросит как сохранить сам проект и как сохранить модуль. А вот сохранение уже существующего проекта под новым именем - задача хитрая. С помощью кнопки "Save project as" будет сохранён только сам проект и он будет использовать те же модули (не в новой папке, а в старой, то есть физически те же), что и старый проект. Простое копирование в нужную папку модулей (или сохранение их в новую папку или под новым именем с помощью "Save as") проблему не решит. Надо будет переподключать модули к проекту. Не стоит использовать этот путь если он действительно не нужен. Названия пунктов меню приведены для Delphi5, но думаю, что в Вашей версии (кстати, какой версией Вы пользуетесь?) существенных отличий не будет.

Алексей Соловьёв:
>Цитата:
>Результат действия команды subst исчезнет после перезагрузки, так что необходимости предпринимать какие-то действия нет (возможно и есть какая-нибудь антикоманда, но я её не знаю). И кстати, эта команда работает так же в Windows NT/2000/XP, которые к DOS никакого отношения не имеют.
Решение:
Если раньше я родил устройство, например, «F:»
Удаляется «subst f: /d» только предварительно надо уйти с него например командой «C:».

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


http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.soft.prog.samovar
Отписаться

В избранное