Главная страница ЕЖИдневника


Уроки по Delphi

Урок 6
Внеурочье
Урок 5 Графические часы
Урок 4
Внекласс-1
Урок 3
Урок 2
Урок 1
Урок 0
Уроки по Dephi

Внеклассный урок 0. Куда сбежала кнопка? ;о)

Исходные тексты программы здесь


Хех, Вы видели хоть раз преподавателя, не готового к уроку? Нет?! Так полюбуйтесь!

А ведь всё так хорошо начиналось: 0-й, 1-й, 2-й и 3-й уроки прошли «на ура», 4-й и 5-й были на подходе – почти полностью написанные и (опять-таки почти) полностью готовые к отправке на малую родину. И тут я получаю пренеприятное известие от господина Старогодова.

Письмецо всего в две строки:

As> У меня, похоже, проблемка – я, кажется, 4 и 5-й уроки сумел стереть.
As>Теперь заново писать. :-(

Раз педагог ушёл в несознанку, видно, судьба мне на правах учительского любимчика ;) провести один-два урока вместо него.

Но сначала немного истории. Как-то кто-то мне по мылу было раз вдруг прислал программы для просмотра. Одна была сложная, а другая смешная. Согласитесь, человеку, работающему за 100 баксов в месяц, было очень смешно услышать вопрос: «Вас устраивает Ваша заработная плата?» - и обнаружить, что вариантов ответа на него не 2 (по числу кнопок), а всего один: «Да». При нажатии на эту кнопку высвечивалась надпись: «Мы знали, что Вы так ответите!». Что ни говори, на злобу дня, мировая прога… А господин Старогодов мне в ответ на моё восторженное: «Посмотри, какой кайф!!!» ответил занудно-рассудительно: «Бред. Любой новичок в области программирования может такое написать, не отвлекай меня по таким пустякам». Но программа всё-таки запала мне в душу, и сегодня я хочу проверить на практике, действительно ли её может соорудить даже полный чайник. Вы со мной? ;о)

1.Итак, для начала нам нужен провокационный вопрос. Его мы можем задать, воспользовавшись уже упомянутым в предыдущем уроке TMemo. Настроив все интересующие свойства устраивающим нас способом (тип, размер и цвет шрифта, цвет фона, убрав или оставив «бордюр»), вводим заранее придуманный «вопрос на засыпку».

2.Для ответа на него нам понадобятся как минимум две кнопки с различными фразами (также придуманными в меру вашей испорченности). Для того, чтобы эти кнопки появились на форме, нужно в палитре компонентов Standard найти кнопку Button (прямоугольничек с буквосочетанием ОК внутри) и дважды щёлкнуть по нему мышкой. Первая кнопка готова. Теперь нужно поменять слово Button1, написанное на ней, на другое, более подходящее к нашей ситуации. Для этого, как Вы знаете, существует свойство Caption. Аналогично этому создаём необходимое количество кнопок. Готово?

А вот теперь начинается самое интересное! :о) Не знаю, как у вас, а у меня получилось две кнопки, с которыми нужно обязательно что-то делать, а то они хоть есть и не просят, но пользы от них тоже что-то совсем не наблюдается. :о(

1. По задумке нажать можно только одну из кнопок – с тем ответом, который устраивает нас, авторов программы. Вторая кнопка должна быть неуловимой, как те мстители из старого известного советского фильма. То есть попросту кнопка при наведении на неё курсора должна «сбегать». Куда? Куда угодно - в любое место формы, где нет курсора вашей мыши (и бешеной в том числе ;о) ).

У кнопки имеется событие OnMouseMove, которое возникает каждый раз (во время исполнения программы) как курсор мыши оказывается над кнопкой. Двойной щелчок на этом событии (как и на любом другом) в инспекторе объектов создаёт заготовку процедуры, которая будет вызываться при возникновении этого события. Что должна делать программа при наведении курсора на кнопку? Присваивать ей новые значения относительно внешнего объекта-контейнера!

У каждого компонента (кнопки, формы и т.д.) имеются свойства Top, Left, Width, Height, означающие соответственно положение верхнего края, левого края, ширины и высоты. Положение измеряется всегда относительно внешнего объекта-контейнера, для кнопки это клиентская часть формы, для формы - экран монитора (кстати, форма состоит из двух частей - заголовка и клиентской части, кнопки и другие компоненты нужно расположить на клиентской части). Ширина и высота клиентской части определяются свойствами ClientWidth и ClientHeight. Все эти свойства можно изменять на этапе проектирования программы – в инспекторе объектов и на этапе исполнения программы (то есть непосредственно в тексте) обращаясь к ним например так:

Form1.ClientWidth или Button2.Top. Хотя это свойства, а не переменные, работа с ними ни чем не отличается от работы с обычными переменными.

Какое значение мы должны присваивать «убегающей кнопке»? Случайное? Увы и ах: случайных чисел в кампутере нема. :о( Но зато есть процедура Randomize, которая устанавливает новую последовательность псевдослучайных чисел. Её нужно использовать где-нибудь в начале. Если эту процедуру не использовать, то при каждом запуске программы будут получаться одинаковые "случайные" числа. Чтобы получить случайное число нужно вызвать функцию random(n:integer):integer или random:extended.

Если вызвать эту функцию без параметров, то получишь вещественное число в диапазоне [0,1], если укажешь в скобках целое число n, то на выходе получишь целое число в диапазоне от 0 до n-1.

Изучаем Delphi. Рис.1a.2. Вторая кнопка статична. Её можно нажать, и это нажатие провоцирует какое-то событие. За это отвечает событие OnClick всё на той же закладке Events. Какое это должно быть событие? Какая-то провокационная надпись. В моём случае это фраза: «А ничего другого от тебя, Лемешкин, я и не ожидала!». Как её оформить? В каком виде вывести? Мне в голову не пришло ничего лучше, чем известное системное окошко с сообщением об ошибке (Error). Для того, чтобы вывести его на экран, нужно дать такую команду:

messagedlg ('А ничего другого от тебя, Лемешкин, я и не ожидала!', mterror, [mbok], 0)

Короче, алгоритм действий такой:

1. Помещаем на форму кнопку, в инспекторе объектов создаём процедуру для обработки события OnMouseMove.

2. В получившейся процедуре вызываем процедуру Randomize. Присваиваем свойствам Top и Left кнопки случайные значения из такого диапазона, чтобы кнопки не вылезали за пределы формы (если сложно - нарисуйте на бумаге - отметьте все размеры, подумайте. Если совсем никак (а вдруг?) - присваивайте хоть что-нибудь, экспериментируйте).

3. Смотрим на получившиеся при компиляции ошибки, чешем репу, если не помогает, лезем на форум или задаём вопрос по мылу. Исправляем ошибки.

4. Наслаждаемся.

А у меня вопросы к Александру:

1. Чив ли я? ;о)))

2. У меня иногда кнопка «сбегает» в аккурат под другую кнопку – иногда прячется в ней полностью. Как можно сделать так, чтобы (как минимум) значения левого верхнего края Button1 и Button2 не совпадали?

3. И как сделать так, чтобы эта кнопка не бегала по тексту с вопросом? :)

4. Расскажи поподробнее, какие ещё можно вызывать "окна-уведомления" (кроме фатальной ошибки), и как именно это делается?

Думаю, моя версия программы далека от идеала, так что буду рада любым интересным советам и предложениям по её усовершенствованию! ;о)

Старогодов: А что же ты не закрыла надписи от редактирования? Если уж используешь компоненты TMemo, так выстави у них свойство ReadOnly в true!

Alёna: От редактирования я закрыла, но курсор по прежнему мигает, если щёлкнуть левой кнопкой мышки по надписям. :о( Можно ведь от этого мастдая как-то избавиться? Я уверена, можно. Колись, как это можно сделать!!!

Комментарии Старогодова к факультативу 0

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

Alёna: Ну вот, и повыпендриваться перед читателями не дали - продали не за грош! :о(

Отвечаю на вопросы:

1. Чив! (Правда я не в курсе, что сие означает, но почему бы не согласиться с хорошим человеком?)

Alёna: Сразу видно человека, который в детстве не читал сказок вообще и сказок про хвастливого воробьишку в частности. А ведь это было хрестоматийное произведение для уроков по чтению у первоклашек! ;-р

2, 3. Гы. А вот это - тема 4 урока. Оператор if.

4. В действительности процедура MessageDlg предназначена для вывода диалогового окна. Первый параметр - текст сообщения. "Тип" диалогового окна, а фактически заголовок и картинка, рисуемая слева на этом окне, зависит от второго параметра. Если указать mtError, то нарисует красный кружок с белым крестиком и напишет "Error", а укажешь mtInformation - получишь информационное окно. Третий параметр - набор кнопок на этом окне (в квадратных скобках через запятую). Возможны, например, значения mbOk, mbYes, mbNo. Полный список всех возможных типов окон и кнопок для них можно узнать из помощи: поставь курсор внутри слова MessageDlg и нажми F1. Кстати, четвёртый параметр - номер раздела помощи к твоей программе, связанной с этим диалоговым окном. Пока ты не озаботилась написанием развёрнутой системы помощи, ставь здесь 0.

И, кстати, фатальная ошибка - это когда твоя программа не может продолжить исполнение.

А вот это уже совсем некстати... :-( У тебя в программе всё-таки есть глюки.

1.Главный глюк в твоей убегающей кнопке - её всё-таки можно нажать! Ведь нажимать не обязательно мышкой. Можно передать кнопке фокус с помощью клавиши Tab (в кнопке появится рамочка) и нажать клавишу Enter или пробел.

Alёna: А если передавать фокус второй кнопке? Прокатит? ;о) Только вот обработчик какого события надо использовать? Это ведь не OnClick и не OnMouseMove... ;о( Ладно, нафига так заморачиваться? Нужно использовать системное уведомление mtInformation и заявить, что "Ай, молодца! Убедил!! Верю!!!". Так веселее будет. Так я сейчас и сделаю, пожалуй. :D

Старогодов: Конечно, "прокатит". Я тебе писАл уже - нужно использовать обработчик события OnEnter Но вторая идея, ты права, гораздо удачнее! :-)

2.Ты не закрыла надписи в компонентах memo от редактирования, так что написанный тобой текст легко можно "стереть". Чтобы этого не случилось, нужно свойство ReadOnly выставить в True.

Alёna: От редактирования я закрыла, но курсор по прежнему мигает, если щёлкнуть левой кнопкой мышки по надписям. Можно ведь от этого мастдая как-то избавиться? Я уверена, можно. Колись, как это можно сделать!!! :~(

Старогодов: Можно. Не "пущать" курсор в эти компоненты. Есть такое понятие: фокус. Когда ты щёлкаешь по компоненту memo, он получает фокус (курсор оказывается в этом компоненте) и после этого возникает событие OnEnter. Если в обработчике этого события передать фокус какому-нибудь другому компоненту, то курсор в memo не окажется. Что бы передать фокус второй кнопке нужно дать команду Button2.SetFocus;

Alёna: Са, у меня проблема. Я использовала Button2.SetFocus на обоих компонентах Memo, и после этого клавиша Tab перестала передавать фокус первой кнопке. Нафига я тогда делала второе системное уведомление?! Можно сделать что-нибудь, чтобы и на Memo не пускать курсор, и можно было с помощью табуляции на клавиатуре умудриться выделить первую кнопку? А? Ну скажи! Ну пажалуста-а-а-а!!! :~(

Старогодов: Можна-а! Я тут понял, что слишком всё усложнил! :( У тебя произошло вот что: клавиша Tab передаёт фокус компонентам в определённом порядке согласно свойству TabOrder. Так вот, со второй кнопки фокус должен был быть передан в первый memo, октуда ты его отфутболиваешь опять на вторую кнопку. Следовательно на первую кнопку фокус никак попасть не может. Поигравшись с TabOrder, OnEnter и SetFocus можно добиться нужной функциональности. Только есть более простой и правильный путь! Свойство TabStop! Если оно установлено в false, то фокус этому компоненту передаваться не будет. Следовательно не нужно было писать никаких обработчиков событий, достаточно было у твоих memo выставить TabStop в false.

Alёna: Угу, угу... типа того... При его выставлении на обоих memo в false фокус действительно бегает только между двумя кнопками. Но вот курсор мышкой на текст наводится, как миленький, текст выделяется, курсорчик мигает по-прежнему. Короче, та проблема, с которой мы начинали, к ней же мы снова и вернулись. Какие ещё будут идеи? ;о(

Старогодов: Объединить первое и второе предложение. Закрыть memo от кнопки Tab с помощью указанных свойств, а от мышки с помощью соответствующих обработчиков событий. А мне на будущее не торопиться с ответами...

Alёna: Последняя проблема: фокус-то бегает между двумя кнопками, но изначально, сразу после запуска программы, он находится на первой кнопке, а не на второй. На вторую он переключается только после попытки щёлкнуться мышкой по одному из компонентов memo. Человек догадливый даже гоняться за кнопкой не станет, а просто нажмёт Энтер или пробел. Без труда всех моих рыб, глубоко на дне заныканных, повыловит. Са, мне это не нравится. :о(

Старогодов: Юзверь, а, юзверь? Ты долго ещё нелепые вопросы задавать будешь? Тебе TabOrder на что выдали? Если ты выставишь значение этого свойства у первой кнопки больше, чем у второй, изначально будет выделена именно вторая кнопка! Выставь TabOrder у Button1 в значение единицы, а TabOrder у Button2 сделай равным нулю. И наслаждайся плодами трудов своих тяжких.

Alёna: Ур-ра! Заработало!!! :о))) А знаешь, наша с тобой убегающая кнопка гораздо удачнее своего прототипа. Мы исправили все мелкие недочёты и сделали это с юмором. :о) Чив ли мы? ;о)

Письма наших читателей...


Александр:
А> При помощи 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, но думаю, что в Вашей версии (кстати, какой версией Вы пользуетесь?) существенных отличий не будет.

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

пользовательское соглашение, политика конфиденциальности