В основе первых трех изданий книги лежит идея о том, что компьютер можно рассматривать как иерархию уровней, каждый из которых выполняет какую-либо определенную функцию. Это фундаментальное утверждение сейчас столь же правомерно, как и в момент выхода в свет первого издания, поэтому мы по-прежнему берем его за основу, на этот раз уже в четвертом издании. Как и в первых трех изданиях, в этой книге мы подробно описываем цифровой логический уровень, уровень архитектуры команд, уровень операционной системы и уровень языка ассемблера (хотя мы изменили некоторые названия, чтобы следовать современным установившимся обычаям). В целом структура электронной книги осталась прежней, но в четвертое издание внесены некоторые изменения, что объясняется стремительным развитием компьютерной промышленности. Например, все программы, которые в предыдущих изданиях были написаны на языке Pascal, в четвертом издании переписаны на язык Java, чтобы продемонстрировать популярность языка Java в настоящее время. Кроме того, в качестве примеров в книге рассматриваются более современные машины (Intel Pentium II, Sun UltraSPARC II и Sun picojava II).
Прежде всего, микроконтроллер это процессор со всеми его "атрибутами", плюс встроенная, энергонезависимая память (программ и данных), что позволяет отказаться от внешней памяти программ и поместить программу в его энергонезависимую память.
Это позволяет создавать очень простые (в схемотехническом отношении) и компактные устройства, выполняющие, тем не менее, достаточно сложные функции. Иногда даже диву даешься: эта маленькая "штучка" заменяет целую "груду старого железа"
Любой микроконтроллер, по своим возможностям, конечно же, уступает процессору компьютера, но тем не менее, существует весьма обширный класс устройств, которые преимущественно реализуются именно на микроконтроллерах. И в самом деле, компьютер в карман не положишь и от батареек его не запитаешь. Поэтому, во многих случаях, микроконтроллерам просто нет альтернативы. "Сердцем" микроконтроллера является арифметико - логическое устройство (АЛУ).
Проще всего его представить в виде банального калькулятора, кнопками которого управляет программа, написанная на языке ассемблер (то есть, программист). Если вдуматься, то ничего особо сложного, в механизме управления такого рода калькулятором, нет. И в самом деле, если нужно, например, сложить числа А и В, то в тексте программы сначала задаются константы А и В, а затем дается команда "сложить". Программисту вовсе не обязательно знать, что происходит с нулями и единицами (разве только только для общего развития), ведь калькулятор он на то и калькулятор, чтобы избавить пользователя от "возни" с машинными кодами и прочими "неудобоваримостями".
Когда Вы работаете с компьютером, Вам и не нужно детально знать, что происходит в дебрях операционной системы.
Если Вы туда "полезете", то "с ума сойдете", а микроконтроллер, по своей сути, есть тот же самый компьютер, но только простой. Программисту только нужно детально знать, каким именно образом "приказать железяке" сделать то, что необходимо для достижения задуманного. Микроконтроллер можно представить себе как некий универсальный "набор" многофункциональных модулей (блоков), "рычаги управления" которыми находятся в руках программиста. Этих "рычагов" достаточно большое количество, и естественно, их нужно освоить и точно знать, что именно произойдет, если "дернуть" (дать команду на языке ассемблер) за тот или иной "рычаг". Вот здесь-то уже нужно знать, как "отче наше", каждую деталь и не жалеть на это "узнавание" времени. Только таким образом пустую "болванку" (незапрограммированый ПИК) можно "заставить"
выполнять какие-то "осмысленные" действия, результат большей части которых можно проверить в симуляторе MPLAB (об этом - позднее), даже не записывая программу в ПИК.
Итак, необходим переход к "модульному" мышлению. Любой микроконтроллер можно уподобить детскому конструктору, в состав которого входит множество всяких предметов, манипулируя с которыми, можно получить тот или иной конечный "продукт". Давайте с ними разберемся и "разложим все по полочкам". В качестве примера я буду использовать один из самых распространенных PIC контроллеров PIC16F84A. Он является как бы "проматерью" более сложных ПИКов, содержит минимальный "набор" модулей и как нельзя лучше подходит для первичного "въезда в м/контроллеры".
Энергонезависимая память.
Начнем с энергонезависимой памяти (память программ и память данных).
Информация, заложенная в энергонезависимую память, сохраняется при выключении питания, и поэтому именно в нее записывается программа.
То "место" энергонезависимой памяти, куда записывается программа, называется памятью программ. Объем памяти программ может быть различен. Для PIC16F84A, он составляет 1024 слова. Это означает, что он предназначен для работы с программами, объем которых не превышает 111024 слов.
Слово памяти программ не равно одному байту (8 бит), а больше его (14 бит). Отдельная команда, которую ПИК будет в дальнейшем выполнять, занимает одно слово в памяти программ. В зависимости от названия этой команды в ассемблере, слово принимает то или иное числовое значение в машинном коде. После записи в ПИК "прошивки" программы, слова памяти программ (машинные коды) как бы "превращаются" в команды, которые располагаются, в памяти программ, в том же порядке, в котором они следуют в исходном тексте программы, написанном на языке ассемблер, и в том же порядке им присваиваются адреса, при обращении к которым, та или иная команда "извлекается" из памяти программ для ее выполнения. Последовательность же их выполнения определяется логикой программы. Это означает то, что выполнение команд может происходить не в порядке последовательного возрастания их адресов, с шагом в одну позицию (так называемый инкремент), а "скачком". Дело в том, что только уж самые простейшие программы, в пределах одного их полного цикла, обходятся без этих "скачков", называемых переходами, и выполняются строго последовательно. В остальных же случаях, так называемая (мной) "рабочая точка программы" "мечется по тексту программы как угорелая" (как раз благодаря этим самым переходам).
Термин "рабочая точка программы" - моя "самодеятельность". В свое время, я был очень сильно удивлен отсутствием чего-то подобного в информации, связанной с объяснением работы программ. Казалось бы, чего проще, по аналогии, например, с рабочей точкой транзистора, сделать более комфортным "въезд в механику" работы программ? Так нет же, как будто специально, придумываются такие "головокружительные заменители", причем, в различных случаях, разные, что запутаться в этом очень просто. Итак, рабочую точку программы можно представить себе в виде некоего "шарика от пинг-понга", который "скачет" по командам текста программы в соответствии с алгоритмом (логикой) исполнения программы. На какую команду "шарик скакнул", та команда и исполняется. После этого он "перескакивает" на другую команду, она исполняется, и т.д. Эти "скачки" происходят непрерывно и в течение всего времени включения питания устройства (исполнения программы).
Любая более-менее сложная программа разбивается на части, которые выполняют отдельные функции (своего рода программки в программе) и которые называются подпрограммами. Атрибут любой подпрограммы - функциональная законченность производимых в ней действий.
По сути своей, эта "выдумка" введена в программирование для удобства реализации принципа "разделяй и властвуй": "врага" ведь гораздо легче "разгромить по частям, чем в общей массе". Да и порядка больше.
Безусловные переходы (переходы без условия) между подпрограммами (если они последовательно не переходят одна в другую), осуществляются при помощи команд безусловных переходов, в которых обязательно указывается адрес команды в памяти программ (косвенно - в виде названия подпрограммы или метки), на которую нужно перейти. Существуют также переходы с условием (условные переходы), то есть, с задействованием так называемого стека. Более подробно о переходах я расскажу позднее. Адреса команд определяются счетчиком команд (он называется PC). То есть, каждому состоянию счетчика команд соответствует одна из команд программы. Если команда простая, то счетчик просто инкрементируется (последовательно выполняется следующая команда), а если команда сложная (например, команда перехода или возврата), то счетчик команд изменяет свое состояние "скачком", активируя соответствующую команду.
Примечание: инкремент - увеличение на единицу величины числа, с которым производится эта операция, а декремент - уменьшение на единицу (так называемые комплиментарные операции). В простейшем случае, то есть в случае отсутствия в программе переходов, счетчик команд PC, начиная с команды "старта" (нулевой адрес), многократно инкрементируется, 12 последовательно активизируя все команды в памяти программ. Это означает, что в большинстве случаев, за каждый так называемый машинный цикл (такт работы программы: для ПИКов он равен четырем периодам тактового генератора) работы ПИКа, происходит исполнение одной команды. Есть и команды исполнение которых происходит за 2 машинных цикла (м.ц.), но их меньше. Команд, которые исполняются за 3 м.ц. и более нет. Таким вот образом, на большинстве участков программы (я их называю "линейными участками"), последовательно и перебираются адреса в памяти программ (команды последовательно исполняются).
В более сложных программах, с большим количеством условных и безусловных переходов, работу счетчика команд PC можно охарактеризовать фразой "Фигаро здесь, Фигаро там". 1 машинный цикл (м.ц.) равен 4-м периодам тактового генератора ПИКа. Следовательно, при использовании кварца на 4 Мгц., 1 м.ц.=1 мкс. Выполнение программы, в рабочем режиме (кроме работы в режиме пониженного энергопотребления SLEEP), никогда не останавливается, то есть, за каждый машинный цикл (или за 2, если команда исполняется за 2 м.ц.) должно выполняться какое-либо действие (команда). Тактовый генератор, формирующий машинные циклы, работает постоянно. Если его работу прервать, то исполнение программы прекратится.
Может сложиться ложное представление о том, что работу программы можно на какое-то время остановить, используя одну или несколько команд – "пустышек", не производящих полезных действий (есть такая команда NOP). Это представление не верно, так как в этом случае, речь идет только о задержке выполнения следующих команд, а не об остановке исполнения программы. Программа исполняется и в этом случае, так как "пустышка" есть та же самая команда программы, только не производящая никаких действий (короткая задержка). Если же нужно задержать выполнение каких-либо последующих команд на относительно длительное время, то применяются специальные, циклические подпрограммы задержек, о которых я расскажу позднее. Даже тогда, когда программа "зависает" ("глюк"), она исполняется, просто только не так, как нужно. Остановить (в буквальном смысле этого слова) исполнение программы можно только прекратив работу тактового генератора. Это происходит при переходе в режим пониженного энергопотребления (SLEEP), который используется в работе достаточно специфических устройств. Например, пультов дистанционного управления (и т.д.).
Отсюда следует вывод: программы, не использующие режим SLEEP (а таких - большинство), для обеспечения непрерывного выполнения команд программы, обязательно должны быть циклическими, то есть, иметь так называемый полный цикл программы, причем, многократно повторяющийся в течение всего времени включения питания. Проще говоря, рабочая точка программы должна непрерывно (не останавливаясь) "мотать кольца" полного цикла программы (непрерывно переходить с одного "кольца" на другое).
Общие выводы:
1. Команды программы "лежат" в памяти программ в порядке расположения команд в тексте программы.
2. Адреса этих команд находятся в счетчике команд PC и каждому адресу соответствует одна из команд программы.
3. Команда активируется (исполняется), если в счетчике команд находится ее адрес.
4. Активация команд происходит либо последовательно (на "линейном" участке программы), либо с переходом ("скачком") на другую команду (при выполнении команд переходов), с которой может начинаться как подпрограмма (переход на исполнение подпрограммы), так и группа команд, выделенная меткой (переход на исполнение группы команд, которой не присвоен "статус" подпрограммы).
5. Выполнение команд программы никогда не останавливается (за исключением режима SLEEP), и поэтому программа должна быть циклической.
Кроме памяти программ, PIC16F84A имеет энергонезависимую память данных (EEPROM память данных). Она предназначена для сохранения данных, имеющих место быть на момент выключения питания устройства, в целях их использования в дальнейшем (после следующего включения питания). Так же, как и память программ, память данных состоит из ячеек, в которых "лежат" слова. Слово памяти данных равно одному байту (8 бит). В PIC16F84A, объем памяти данных составляет 64 байта. Байты, хранящиеся в памяти данных, предназначены для их считывания в стандартные 8-битные регистры, речь о которых пойдет далее. Данные из этих регистров могут быть записаны в EEPROM память данных, то есть, может быть организован обмен данными между памятью данных и регистрами. Например, именно EEPROM память данных я использовал в своем частотомере для сохранения последних, перед выключением питания, настроек. Она же используется и для установки значений промежуточной частоты. Во многих программах, память данных вообще не используется, но это "вещь" исключительно полезная, и далее я расскажу о ней подробнее.
Данная статья предназначена для начинающих программистов, которые никогда не работали с потоками, и хотели бы узнать основы работы с ними. Желательно, чтоб читатель знал основы ООП и имел какой-нибудь опыт работы в Delphi.
Для начала давайте определимся, что под словом "поток" я подразумеваю именно Thread, который еще имеет название "нить". Нередко встречал на форумах мнения, что потоки не нужны вообще, любую программу можно написать так, что она будет замечательно работать и без них. Конечно, если не делать ничего серьёзней "Hello World" это так и есть, но если постепенно набирать опыт, рано или поздно любой начинающий программист упрётся в возможности "плоского" кода, возникнет необходимость распараллелить задачи. А некоторые задачи вообще нельзя реализовать без использования потоков, например работа с сокетами, COM-портом, длительное ожидание каких-либо событий, и т.д.
Всем известно, что Windows система многозадачная. Попросту говоря, это означает, что несколько программ могут работать одновременно под управлением ОС. Все мы открывали диспетчер задач и видели список процессов. Процесс - это экземпляр выполняемого приложения. На самом деле сам по себе он ничего не выполняет, он создаётся при запуске приложения, содержит в себе служебную информацию, через которую система с ним работает, так же ему выделяется необходимая память под код и данные. Для того, чтобы программа заработала, в нём создаётся поток. Любой процесс содержит в себе хотя бы один поток, и именно он отвечает за выполнение кода и получает на это процессорное время. Этим и достигается мнимая параллельность работы программ, или, как её еще называют, псевдопараллельность. Почему мнимая? Да потому, что реально процессор в каждый момент времени может выполнять только один участок кода. Windows раздаёт процессорное время всем потокам в системе по очереди, тем самым создаётся впечатление, что они работают одновременно. Реально работающие параллельно потоки могут быть только на машинах с двумя и более процессорами.
Для создания дополнительных потоков в Delphi существует базовый класс TThread, от него мы и будем наследоваться при реализации своих потоков. Для того, чтобы создать "скелет" нового класса, можно выбрать в меню File - New - Thread Object, Delphi создаст новый модуль с заготовкой этого класса. Я же для наглядности опишу его в модуле формы. Как видите, в этой заготовке добавлен один метод - Execute. Именно его нам и нужно переопределить, код внутри него и будет работать в отдельном потоке. И так, попробуем написать пример - запустим в потоке бесконечный цикл:
Запустите пример на выполнение и нажмите кнопку. Вроде ничего не происходит - форма не зависла, реагирует на перемещения. На самом деле это не так - откройте диспетчер задач и вы увидите, что процессор загружен по-полной. Сейчас в процессе вашего приложения работает два потока - один был создан изначально, при запуске приложения. Второй, который так грузит процессор - мы создали по нажатию кнопки. Итак, давайте разберём, что же означает код в Button1Click:
тут мы создали экземпляр класса TNewThread. Конструктор Create имеет всего один параметр - CreateSuspended типа boolean, который указывает, запустить новый поток сразу после создания (если false), или дождаться команды (если true).
свойство FreeOnTerminate определяет, что поток после выполнения автоматически завершится, объект будет уничтожен, и нам не придётся его уничтожать вручную. В нашем примере это не имеет значения, так как сам по себе он никогда не завершится, но понадобится в следующих примерах.
Свойство Priority, если вы еще не догадались из названия, устанавливает приоритет потока. Да да, каждый поток в системе имеет свой приоритет. Если процессорного времени не хватает, система начинает распределять его согласно приоритетам потоков. Свойство Priority может принимать следующие значения:
tpTimeCritical - критический
tpHighest - очень высокий
tpHigher - высокий
tpNormal - средний
tpLower - низкий
tpLowest - очень низкий
tpIdle - поток работает во время простоя системы
Ставить высокие приоритеты потокам не стоит, если этого не требует задача, так как это сильно нагружает систему.
Ну и собственно, запуск потока.
Думаю, теперь вам понятно, как создаются потоки. Заметьте, ничего сложного. Но не всё так просто. Казалось бы - пишем любой код внутри метода Execute и всё, а нет, потоки имеют одно неприятное свойство - они ничего не знают друг о друге. И что такого? - спросите вы. А вот что: допустим, вы пытаетесь из другого потока изменить свойство какого-нибудь компонента на форме. Как известно, VCL однопоточна, весь код внутри приложения выполняется последовательно. Допустим, в процессе работы изменились какие-то данные внутри классов VCL, система отбирает время у основного потока, передаёт по кругу остальным потокам и возвращает обратно, при этом выполнение кода продолжается с того места, где приостановилось. Если мы из своего потока что-то меняем, к примеру, на форме, задействуется много механизмов внутри VCL (напомню, выполнение основного потока пока "приостановлено"), соответственно за это время успеют измениться какие-либо данные. И тут вдруг время снова отдаётся основному потоку, он спокойно продолжает своё выполнение, но данные уже изменены! К чему это может привести - предугадать нельзя. Вы можете проверить это тысячу раз, и ничего не произойдёт, а на тысяча первый программа рухнет. И это относится не только к взаимодействию дополнительных потоков с главным, но и к взаимодействию потоков между собой. Писать такие ненадёжные программы конечно нельзя.
Синхронизации потоков
Если вы создали шаблон класса автоматически, то, наверное, заметили комментарий, который дружелюбная Delphi поместила в новый модуль. Он гласит: "Methods and properties of objects in visual components can only be used in a method called using Synchronize". Это значит, что обращение к визуальным компонентам возможно только путём вызова процедуры Synchronize. Давайте рассмотрим пример, но теперь наш поток не будет разогревать процессор впустую, а будет делать что-нибудь полезное, к примеру, прокручивать ProgressBar на форме. В качестве параметра в процедуру Synchronize передаётся метод нашего потока, но сам он передаётся без параметров. Параметры можно передать, добавив поля нужного типа в описание нашего класса. У нас будет одно поле - тот самый прогресс:
Вот теперь ProgressBar двигается, и это вполне безопасно. А безопасно вот почему: процедура Synchronize на время приостанавливает выполнение нашего потока, и передаёт управление главному потоку, т.е. SetProgress выполняется в главном потоке. Это нужно запомнить, потому что некоторые допускают ошибки, выполняя внутри Synchronize длительную работу, при этом, что очевидно, форма зависает на длительное время. Поэтому используйте Synchronize для вывода информации - то самое двигание прогресса, обновления заголовков компонентов и т.д.
Вы наверное заметили, что внутри цикла мы используем процедуру Sleep. В однопоточном приложении Sleep используется редко, а вот в потоках его использовать очень удобно. Пример - бесконечный цикл, пока не выполнится какое-нибудь условие. Если не вставить туда Sleep мы будем просто нагружать систему бесполезной работой.
Надеюсь, вы поняли как работает Synchronize. Но есть еще один довольно удобный способ передать информацию форме - посылка сообщения. Давайте рассмотрим и его. Для этого объявим константу:
В объявление класса формы добавим новый метод, а затем и его реализацию:
Используя функцию SendMessage, мы посылаем окну приложения сообщение, один из параметров которого содержит нужный нам прогресс. Сообщение становится в очередь, и согласно этой очереди будет обработано главным потоком, где и выполнится метод SetProgressPos. Но тут есть один нюанс: SendMessage, как и в случае с Synchronize, приостановит выполнение нашего потока, пока основной поток не обработает сообщение. Если использовать PostMessage этого не произойдёт, наш поток отправит сообщение и продолжит свою работу, а уж когда оно там обработается - неважно. Какую из этих функций использовать - решать вам, всё зависит от задачи.
Вот, в принципе, мы и рассмотрели основные способы работы с компонентами VCL из потоков. А как быть, если в нашей программе не один новый поток, а несколько? И нужно организовать работу с одними и теми же данными? Тут нам на помощь приходят другие способы синхронизации. Один из них мы и рассмотрим. Для его реализации нужно добавить в проект модуль SyncObjs.
Критические секции
Работают они следующим образом: внутри критической секции может работать только один поток, другие ждут его завершения. Чтобы лучше понять, везде приводят сравнение с узкой трубой: представьте, с одной стороны "толпятся" потоки, но в трубу может "пролезть" только один, а когда он "пролезет" - начнёт движение второй, и так по порядку. Еще проще понять это на примере и тем же ProgressBar'ом. Итак, запустите один из примеров, приведённых ранее. Нажмите на кнопку, подождите несколько секунд, а затем нажмите еще раз. Что происходит? ProgressBar начал прыгать. Прыгает потому, что у нас работает не один поток, а два, и каждый из них передаёт разные значения прогресса. Теперь немного переделаем код, в событии onCreate формы создадим критическую секцию:
У TCriticalSection есть два нужных нам метода, Enter и Leave, соответственно вход и выход из неё. Поместим наш код в критическую секцию:
Попробуйте запустить приложение и нажать несколько раз на кнопку, а потом посчитайте, сколько раз пройдёт прогресс. Понятно, в чем суть? Первый раз, нажимая на кнопку, мы создаём поток, он занимает критическую секцию и начинает работу. Нажимаем второй - создаётся второй поток, но критическая секция занята, и он ждёт, пока её не освободит первый. Третий, четвёртый - все пройдут только по-очереди.
Критические секции удобно использовать при обработке одних и тех же данных (списков, массивов) разными потоками. Поняв, как они работают, вы всегда найдёте им применение.
В этой небольшой статье рассмотрены не все способы синхронизации, есть еще события (TEvent), а так же объекты системы, такие как мьютексы (Mutex), семафоры (Semaphore), но они больше подходят для взаимодействия между приложениями. Остальное, что касается использования класса TThread, вы можете узнать самостоятельно, в help'е всё довольно подробно описано. Цель этой статьи - показать начинающим, что не всё так сложно и страшно, главное разобраться, что есть что. И побольше практики - самое главное опыт!
Компоненты Delphi для работы с базами данных были созданы в расчете на работу с SQL и архитектурой клиент/сервер. При работе с ними вы можете воспользоваться характеристиками расширенной поддержки удаленных серверов. Delphi осуществляет эту поддержку двумя способами.
1. Введение
Во-первых, непосредственные команды из Delphi позволяют разработчику управлять таблицами, устанавливать пределы, удалять, вставлять и редактировать существующие записи.
Второй способ заключается в использовании запросов на языке SQL, где строка запроса передается на сервер для ее разбора, оптимизации, выполнения и передачи обратно результатов.
Данный документ делает акцент на втором методе доступа к базам данных, на основе запросов SQL (pass-through). Авторы не стремились создать курсы по изучению синтаксиса языка SQL и его применения, они ставили перед собой цель дать несколько примеров использования компонентов TQuery и TStoredProc. Но чтобы сделать это, необходимо понимать концепцию SQL и знать как работают selects, inserts, updates, views, joins и хранимые процедуры (stored procedures). Документ также вскользь касается вопросов управления транзакциями и соединения с базой данных, но не акцентирует на этом внимание. Итак, приступая к теме, создайте простой запрос типа SELECT и отобразите результаты.
2. Компонент TQuery
Если в ваших приложениях вы собираетесь использовать SQL, то вам непременно придется познакомиться с компонентом TQuery. Компоненты TQuery и TTable наследуются от TDataset. TDataset обеспечивает необходимую функциональность для получения доступа к базам данных. Как таковые, компоненты TQuery и TTable имеют много общих признаков. Для подготовки данных для показа в визуальных компонентах используется все тот же TDatasource. Также, для определения к какому серверу и базе данных необходимо получить доступ, необходимо задать имя псевдонима. Это должно выполняться установкой свойства aliasName объекта TQuery.
Свойство SQL
Все же TQuery имеет некоторую уникальную функциональность. Например, у TQuery имеется свойство с именем SQL. Свойство SQL используется для хранения SQL-запроса. Ниже приведены основные шаги для составления запроса, где все служащие имеют зарплату свыше $50,000.
Создайте объект TQuery
Задайте псевдоним свойству DatabaseName. (Данный пример использует псевдоним IBLOCAL, связанный с демонстрационной базой данных employee.gdb).
Выберите свойство SQL и щелкните на кнопке с текстом - '...' (три точки, Инспектор Объектов - В.О.). Должен появиться диалог редактора списка строк (String List Editor).
Введите:
. Нажмите OK.
Выберите в Инспекторе Объектов свойство Active и установите его в TRUE.
Разместите на форме объект TDatasource.
Установите свойство Dataset у TDatasource в Query1.
Разместите на форме TDBGrid.
Установите его свойство Datasource в Datasource1.
Свойство SQL имеет тип TStrings. Объект TStrings представляет собой список строк, и чем-то похож на массив. Тип данных TStrings имеет в своем арсенале команды добавления строк, их загрузки из текстового файла и обмена данными с другим объектом TStrings. Другой компонент, использующий TStrings - TMemo. В демонстрационном проекте ENTRSQL.DPR (по идее, он должен находится на отдельной дискетте, но к "Советам по Delphi" она не прилагается - В.О.), пользователь должен ввести SQL-запрос и нажать кнопку "Do It" ("сделать это"). Результаты запроса отображаются в табличной сетке. В Листинге 1 полностью приведен код обработчика кнопки "Do It".
Листинг 1
Свойство Params
Этого должно быть достаточно для пользователя, знающего SQL. Тем не менее, большинство пользователей не знает этого языка. Итак, ваша работа как разработчика заключается в предоставлении интерфейса и создании SQL-запроса. В Delphi, для создания SQL-запроса на лету можно использовать динамические запросы. Динамические запросы допускают использование параметров. Для определения параметра в запросе используется двоеточие (:), за которым следует имя параметра. Ниже приведе пример SQL-запроса с использованием динамического параметра:
Если вам нужно протестировать, или установить для параметра значение по умолчанию, выберите свойство Params объекта Query1. Щелкните на кнопке '...'. Должен появиться диалог настройки параметров. Выберите параметр Dept_no. Затем в выпадающем списке типов данных выберите Integer. Для того, чтобы задать значение по умолчанию, введите нужное значение в поле редактирования "Value".
Для изменения SQL-запроса во время выполнения приложения, параметры необходимо связать (bind). Параметры могут изменяться, запрос выполняться повторно, а данные обновляться. Для непосредственного редактирования значения параметра используется свойство Params или метод ParamByName. Свойство Params представляет из себя массив TParams. Поэтому для получения доступа к параметру, необходимо указать его индекс. Для примера,
Query1.params[0].asInteger := 900;
Свойство asInteger читает данные как тип Integer (название говорит само за себя). Это не обязательно должно указывать но то, что поле имеет тип Integer. Например, если тип поля VARCHAR(10), Delphi осуществит преобразование данных. Так, приведенный выше пример мог бы быть записан таким образом:
Query1.params[0].asString := '900';
или так:
Query1.params[0].asString := edit1.text;
Если вместо номера индекса вы хотели бы использовать имя параметра, то воспользуйтесь методом ParamByName. Данный метод возвращает объект TParam с заданным именем. Например:
Query1.ParamByName('DEPT_NO').asInteger := 900;
В листинге 2 приведен полный код примера.
Листинг 2
Обратите внимание на процедуру, первым делом подготовливающую запрос. При вызове метода prepare, Delphi посылает SQL запрос на удаленный сервер. Сервер выполняет грамматический разбор и оптимизацию запроса. Преимущество такой подготовки запроса состоит в его предварительном разборе и оптимизации. Альтернативой здесь может служить подготовка сервером запроса при каждом его выполнении. Как только запрос подготовлен, подставляются необходимые новые параметры, и запрос выполняется.
[pagebreak]
Источник данных
В предыдущем примере пользователь мог ввести номер отдела, и после выполнения запроса отображался список сотрудников этого отдела. А как насчет использования таблицы DEPARTMENT, позволяющей пользователю легко перемещаться между пользователями и отделами?
Примечание: Следующий пример использует TTable с именем Table1. Для Table1 имя базы данных IBLOCAL, имя таблицы - DEPARTMENT. DataSource2 TDatasource связан с Table1. Таблица также активна и отображает записи в TDBGrid.
Способ подключения TQuery к TTable - через TDatasource. Есть два основных способа сделать это. Во-первых, разместить код в обработчике события TDatasource OnDataChange. Например, листинг 3 демонстрирует эту технику.
Листинг 3 - Использования события OnDataChange для просмотра дочерних записей
Техника с использованием OnDataChange очень гибка, но есть еще легче способ подключения Query к таблице. Компонент TQuery имеет свойство Datasource. Определяя TDatasource для свойства Datasource, объект TQuery сравнивает имена параметров в SQL-запросе с именами полей в TDatasource. В случае общих имен, такие параметры заполняются автоматически. Это позволяет разработчику избежать написание кода, приведенного в листинге 3 (*** приведен выше ***).
Фактически, техника использования Datasource не требует никакого дополнительного кодирования. Для поключения запроса к таблице DEPT_NO выполните действия, приведенные в листинге 4.
Листинг 4 - Связывание TQuery c TTable через свойство Datasource
Выберите у Query1 свойство SQL и введите:
Выберите свойство Datasource и назначьте источник данных, связанный с Table1 (Datasource2 в нашем примере)
Выберите свойство Active и установите его в True
Это все, если вы хотите создать такой тип отношений. Тем не менее, существуют некоторые ограничения на параметризованные запросы. Параметры ограничены значениями. К примеру, вы не можете использовать параметр с именем Column или Table. Для создания запроса, динамически изменяемого имя таблицы, вы могли бы использовать технику конкатенации строки. Другая техника заключается в использовании команды Format.
Команда Format
Команда Format заменяет параметры форматирования (%s, %d, %n и пр.) передаваемыми значениями. Например,
Format('Select * from %s', ['EMPLOYEE'])
Результатом вышеприведенной команды будет 'Select * from EMPLOYEE'. Функция буквально делает замену параметров форматирования значениями массива. При использовании нескольких параметров форматирования, замена происходит слева направо. Например,
Результатом команды форматирования будет 'Select * from EMPLOYEE where EMP_ID=3'. Такая функциональность обеспечивает чрезвычайную гибкость при динамическом выполнении запроса. Пример, приведенный ниже в листинге 5, позволяет вывести в результатах поле salary. Для поля salary пользователь может задавать критерии.
Листинг 5 - Использование команды Format для создания SQL-запроса
В этом примере мы используем методы Clear и Add свойства SQL. Поскольку "подготовленный" запрос использует ресурсы сервера, и нет никакой гарантии что новый запрос будет использовать те же таблицы и столбцы, Delphi, при каждом изменении свойства SQL, осуществляет операцию, обратную "подготовке" (unprepare). Если TQuery не был подготовлен (т.е. свойство Prepared установлено в False), Delphi автоматически подготавливает его при каждом выполнении. Поэтому в нашем случае, даже если бы был вызван метод Prepare, приложению от этого не будет никакой пользы.
Open против ExecSQL
В предыдущих примерах TQuerie выполняли Select-запросы. Delphi рассматривает результаты Select-запроса как набор данных, типа таблицы. Это просто один класс допустимых SQL-запросов. К примеру, команда Update обновляет содержимое записи, но не возвращает записи или какого-либо значения. Если вы хотите использовать запрос, не возвращающий набор данных, используйте ExecSQL вместо Open. ExecSQL передает запрос для выполнения на сервер. В общем случае, если вы ожидаете, что получите от запроса данные, то используйте Open. В противном случае допускается использование ExecSQL, хотя его использование с Select не будет конструктивным. Листинг 6 содержит код, поясняющий сказанное на примере.
Листинг 6
Все приведенные выше примеры предполагают использования в ваших приложениях запросов. Они могут дать солидное основание для того, чтобы начать использовать в ваших приложениях TQuery. Но все же нельзя прогнозировать конец использования SQL в ваших приложених. Типичные серверы могут предложить вам другие характеристики, типа хранимых процедур и транзакций. В следующих двух секциях приведен краткий обзор этих средств.
[pagebreak]
3. Компонент TStoredProc
Хранимая процедура представляет собой список команд (SQL или определенного сервера), хранимых и выполняемых на стороне сервера. Хранимые процедуры не имеют концептуальных различий с другими типами процедур. TStoredProc наследуется от TDataset, поэтому он имеет много общих характеристик с TTable и TQuery. Особенно заметно сходство с TQuery. Поскольку хранимые процедуры не требуют возврата значений, те же правила действуют и для методов ExecProc и Open. Каждый сервер реализует работу хранимых процедур с небольшими различиями. Например, если в качестве сервера вы используете Interbase, хранимые процедуры выполняются в виде Select-запросов. Например, чтобы посмотреть на результаты хранимой процедуры, ORG_CHART, в демонстрационной базе данных EMPLOYEE, используйте следующих SQL-запрос:
При работе с другими серверами, например, Sybase, вы можете использовать компонент TStoredProc. Данный компонент имеет свойства для имен базы данных и хранимой процедуры. Если процедура требует на входе каких-то параметров, используйте для их ввода свойство Params.
4. TDatabase
Компонент TDatabase обеспечивает функциональность, которой не хватает TQuery и TStoredProc. В частности, TDatabase позволяет создавать локальные псевдонимы BDE, так что приложению не потребуются псевдонимы, содержащиеся в конфигурационном файле BDE. Этим локальным псевдонимом в приложении могут воспользоваться все имеющиеся TTable, TQuery и TStoredProc. TDatabase также позволяет разработчику настраивать процесс подключения, подавляя диалог ввода имени и пароля пользователя, или заполняя необходимые параметры. И, наконец, самое главное, TDatabase может обеспечивать единственную связь с базой данных, суммируя все операции с базой данных через один компонент. Это позволяет элементам управления для работы с БД иметь возможность управления транзакциями.
Транзакцией можно считать передачу пакета информации. Классическим примером транзакции является передача денег на счет банка. Транзакция должна состоять из операции внесения суммы на новый счет и удаления той же суммы с текущего счета. Если один из этих шагов по какой-то причине был невыполнен, транзакция также считается невыполненной. В случае такой ошибки, SQL сервер позволяет выполнить команду отката (rollback), без внесения изменений в базу данных. Управление транзакциями зависит от компонента TDatabase. Поскольку транзакция обычно состоит из нескольких запросов, вы должны отметить начало транзакции и ее конец. Для выделения начала транзакции используйте TDatabase.BeginTransaction. Как только транзакция начнет выполняться, все выполняемые команды до вызова TDatabase.Commit или TDatabase.Rollback переводятся во временный режим. При вызове Commit все измененные данные передаются на сервер. При вызове Rollback все изменения теряют силу. Ниже в листинге 7 приведен пример, где используется таблица с именем ACCOUNTS. Показанная процедура пытается передать сумму с одного счета на другой.
Листинг 7
И последнее, что нужно учесть при соединении с базой данных. В приведенном выше примере, TDatabase использовался в качестве единственного канала для связи с базой данных, поэтому было возможным выполнение только одной транзакции. Чтобы выполнить это, было определено имя псевдонима (Aliasname). Псевдоним хранит в себе информацию, касающуюся соединения, такую, как Driver Type (тип драйвера), Server Name (имя сервера), User Name (имя пользователя) и другую. Данная информация используется для создания строки соединения (connect string). Для создания псевдонима вы можете использовать утилиту конфигурирования BDE, или, как показано в примере ниже, заполнять параметры во время выполнения приложения.
TDatabase имеет свойство Params, в котором хранится информация соединения. Каждая строка Params является отдельным параметром. В приведенном ниже примере пользователь устанавливает параметр User Name в поле редактирования Edit1, а параметр Password в поле Edit2. В коде листинга 8 показан процесс подключения к базе данных:
Листинг 8
Этот пример показывает как можно осуществить подключение к серверу без создания псевдонима. Ключевыми моментами здесь являются определение DriverName и заполнение Params информацией, необходимой для подключения. Вам не нужно определять все параметры, вам необходимо задать только те, которые не устанавливаются в конфигурации BDE определенным вами драйвером базы данных. Введенные в свойстве Params данные перекрывают все установки конфигурации BDE. Записывая параметры, Delphi заполняет оставшиеся параметры значениями из BDE Config для данного драйвера. Приведенный выше пример также вводит такие понятия, как сессия и метод GetTableNames. Это выходит за рамки обсуждаемой темы, достаточно упомянуть лишь тот факт, что переменная session является дескриптором database engine. В примере она добавлена только для "показухи".
Другой темой является использование SQLPASSTHRU MODE. Этот параметр базы данных отвечает за то, как натив-команды базы данных, такие, как TTable.Append или TTable.Insert будут взаимодействовать с TQuery, подключенной к той же базе данных. Существуют три возможных значения: NOT SHARED, SHARED NOAUTOCOMMIT и SHARED AUTOCOMMIT. NOT SHARED означает, что натив-команды используют одно соединение с сервером, тогда как запросы - другое. Со стороны сервера это видится как работа двух разных пользователей. В любой момент времени, пока транзакция активна, натив-команды не будут исполняться (committed) до тех пор, пока транзакция не будет завершена. Если был выполнен TQuery, то любые изменения, переданные в базу данных, проходят отдельно от транзакции.
Два других режима, SHARED NOAUTOCOMMIT и SHARED AUTOCOMMIT, делают для натив-команд и запросов общим одно соединение с сервером. Различие между двумя режимами заключаются в передаче выполненной натив-команды на сервер. При выбранном режиме SHARED AUTOCOMMIT бессмысленно создавать транзакцию, использующую натив-команды для удаления записи и последующей попыткой осуществить откат (Rollback). Запись должна быть удалена, а изменения должны быть сделаны (committed) до вызова команды Rollback. Если вам нужно передать натив-команды в пределах транзакции, или включить эти команды в саму транзакцию, убедитесь в том, что SQLPASSTHRU MODE установлен в SHARED NOAUTOCOMMIT или в NOT SHARED.
5. Выводы
Delphi поддерживает множество характеристик при использовании языка SQL с вашими серверами баз данных. На этой ноте разрешите попрощаться и пожелать почаще использовать SQL в ваших приложениях.
Из приложений Delphi вы можете получить доступ к .MDB-файлам Microsoft Access, используя драйверы ODBC. Delphi действительно может дать все необходимое, но некоторые вещи не столь очевидные. Вот шаги для достижения вашей цели.
Что вам нужно: Первое: проверьте, установлен ли ODBC Administrator (файл ODBCADM.EXE в WINDOWS\SYSTEM, вам также необходим файл DBCINST.DLL для установки новых драйверов и ODBC.DLL). Администратор ODBC должен присутствовать в Панели Управления в виде иконки ODBC. Если у вас его не было, то после установки Delphi он должен появиться. Если вы получаете сообщение типа "Your ODBC is not up-to-date IDAPI needs ODBC greater then 2.0", у вас имеется старая версия администратора и вы должны обновить ее до версии, включенной в поставку Delphi. Проверьте, имеете ли вы доступ к драйверу Access ODBC, установленному в Windows. Вы можете сделать это, щелкнув на "Drivers" в диалоговом окне "Data Sources", появляющемся при запуске ODBC Administrator. Delphi должна в диалоге добавить пункты Access Files (*.mdb) и Access Data (*.mdb), работающие с файлами Access 1.10 и использующие драйвер SIMBA.DLL (имейте в виду, что для данного DLL необходимы также файлы RED110.DLL и SIMADMIN.DLL, устанавливаемые для вас Delphi). Данные файлы должны поставляться с дистрибутивом вашей программы как часть ReportSmith Runtime библиотеки. Если вы хотите работать с файлами Access 2.0 или 2.5, вам необходимо иметь другой набор драйверов от Microsoft. Ключевой файл - MSAJT200.DLL, также необходимы файлы MSJETERR.DLL и MSJETINT.DLL. В США набор ODBC Desktop Drivers, Version 2.0. стоит $10.25. Он также доступен в январском выпуске MSDN, Level 2 (Development Platform) CD4 \ODBC\X86 как часть ODBC 2.1 SDK. Очевидно есть обновление этих драйверов для файлов Access 2.5 на форуме MSACCESS CompuServe. Имейте в виду, что драйвер Access ODBC, поставляемый с некоторыми приложениями Microsoft (например, MS Office) могут использоваться только другими MS-приложениями. К сожалению, они могут сыграть с вами злую шутку: сначала заработать, а потом отказать в совершенно неподходящий момент! Поэтому не обращайте внимания (запретите себе обращать внимание!) на строчку "Access 2.0 for MS Office (*.mdb)" в списке драйверов ODBC Administrator. Вы можете установить новые ODBC драйверы с помощью ODBC Administrator в Панели Управления.
Добавление источника данных ODBC (Data Source): если у вас имеются все необходимые файлы, можете начинать. Представленный здесь пример использует драйвер Access 1.10, обеспечиваемый Delphi. Используя ODBC Administrator, установите источник данных для ваших файлов Access: щелчок на кнопке "Add" в окне "data sources" выведет диалог "Add Data Source", выберите Access Files (*.mdb) (или что-либо подходящее, в зависимости от установленных драйверов). В диалоге "ODBC Microsoft Access Setup" необходимо ввести имя в поле "Data Source Name". В данном примере мы используем "My Test". Введите описание "Data Source" в поле Description. Щелкните на "Select Database" для открытия диалога "Select Database". Перейдите в директорию, где хранятся ваши Access .MDB-файлы и выберите один. Мы выберем файл TEST.MDB в директории C:\DELPROJ\ACCESS. Нажмите OK в диалоге "Setup". Теперь в списке источников данных (Data Sources) должен появиться "My Test" (Access Files *.mdb). Нажмите Close для выхода из ODBC Administrator. Используя этот метод, вы можете установить и другие, необходимые вам, источники данных.
Настройка Borland Database Engine: загрузите теперь Borland Database Engine (BDE) Configuration Utility. На странице "Drivers" щелкните на кнопке New ODBC Driver. Имейте в виду, что это добавит драйвер Access в BDE и полностью отдельное управление дополнительно к драйверам Access в Windows, устанавливаемым при помощи ODBC Administrator. В открывшемся диалоге Add ODBC Driver в верхнем поле редактировании введите ACCESS (или что-то типа этого). BDE автоматически добавит на первое место ODBC_. В combobox, расположенном немного ниже, выберите Access Files (*.mdb). Выберите Data Source в следующем combobox (Default Data Source Name), это должен быть источник данных, который вы установили с помощью ODBC Administration Utility. Здесь можно не беспокоиться о вашем выборе, поскольку позднее это можно изменить (позже вы узнаете как это можно сделать). Нажмите OK. После установки драйвера BDE, вы можете использовать его более чем с одним источником данных ODBC, применяя различные псевдонимы (Alias) для каждого ODBC Data Source. Для установки псевдонима переключитесь на страницу "Aliases" и нажмите на кнопку "New Alias". В диалоговом окне "Add New Alias" введите необходимое имя псевдонима в поле "Alias Name". В нашем примере мы используем MY_TEST (не забывайте, что пробелы в псевдониме недопустимы). В combobox Alias Type выберите имя ODBC-драйвера, который вы только что создали (в нашем случае ODBC_ACCESS). Нажмите OK. Если вы имеете более одного ODBC Data Source, измените параметр ODBC DSN ("DSN" = "Data Source Name") в списке "Parameters" псевдонима на подходящий источник данных ODBC Data Source, как установлено в ODBC Administrator. Имейте в виду, что вы не должны ничего добавлять в параметр Path (путь), так как ODBC Data Source уже имеет эту информацию. Если вы добавляете параметр Path, убедитесь, что путь правильный, в противном случае ничего работать не будет! Теперь сохраните конфигурацию BDE, выбирая пункты меню File|Save, и выходите из Database Engine Configuration Utility.
В Delphi: Создайте новый проект и расположите на форме компоненты Table и DataSource из вкладки Data Access палитры компонентов. Затем из вкладки Data Controls выберите компонент DBGrid и также расположите его на форме. В Table, в Инспекторе Объектов, назначьте свойству DatabaseName псевдоним MY_TEST, установленный нами в BDE Configuration Utility. Теперь спуститесь ниже и раскройте список TableName. Вас попросят зарегистрироваться в базе данных Access MY_TEST. Обратите внимание, что если бюджет не установлен, то User Name и Password можно не заполнять, просто нажмите на кнопку OK. После некоторой паузы раскроется список, содержащий доступные таблицы для ODBC Data Source указанного псевдонима BDE. Выберите TEST. В DataSource, в Инспекторе Объектов, назначьте свойству DataSet таблицу Table1. В DBGrid, также в Инспекторе Объектов, назначьте свойству DataSource значение DataSource1. Возвратитесь к таблице, и в том же Инспекторе Объектов установите свойство Active в True. Данные из таблицы TEST отобразятся в табличной сетке. Это все! Одну вещь все-таки стоит упомянуть: если вы создаете приложение, использующее таблицы Access и запускаете его из-под Delphi IDE, то при попытке изменения данных в таблице(ах) вы получите ошибку. Если же вы запустите скомпилированный .EXE-файл вне Delphi (предварительно Delphi закрыв), то все будет ОК. Сообщения об ошибках ODBC, к несчастью, очень туманные и бывает достаточно трудно понять его источник в вашем приложении, в этом случае проверьте установку ODBC Administrator и BDE Configuration Utility, они также могут помочь понять источник ошибки. Для получения дополнительной информации обратитесь к ODBC 2.0 Programmer's Reference или SDK Guide от Microsoft Press (ISBN 1-55615-658-8, цена в США составляет $24.95). В этом документе вы получите исчерпывающую информацию о возможных ошибках при использовании Access-файлов посредством ODBC. Также здесь вы можете найти рапорты пользователей о найденных ошибках, в том числе и при использовании Delphi. Более того, я выяснил, что большинство описанных проблем возникает при неправильных настройках ODBC, т.е. те шаги, которые я описал выше. Надеюсь, что с развитием технологии доступа к базам данных такие сложности уйдут в прошлое. Кроме того, имейте в виду, что если вам необходимо создать новую таблицу Access 1.10, вы можете воспользоваться Database Desktop, включаемый в поставку Delphi.
Авторы данной технологии Ralph Friedman (CompuServe 100064,3102), Bob Swart и Chris Frizelle.
Взято из Советов по Delphi от Валентина Озерова
--------------------------------------------------------------------------------
Может кто-нибудь, предпочтительно из персонала Borland, ПОЖАЛУЙСТА, дать мне ПОЛНЫЙ рассказ о том, как с помощью Delphi и сопутствующего программного обеспечения получить доступ и работать с базами данных MS Access. Среди прочего, мне необходимо узнать...
Нижеследующая инструкция в точности повторяет ту технологию, с которой я работаю на данный момент, надеюсь, что это поможет.
Драйвер ODBC, предусмотренный для доступа к Access 2.0, разработан только для работы в пределах среды Microsoft Office. Для работы со связкой ODBC/Access в Delphi, вам необходим Microsoft ODBC Desktop Driver kit, part# 273-054-030, доступный через Microsoft Direct за $10.25US (если вы живете не в США, воспользуйтесь службой WINEXT). Он также доступен в январском выпуске MSDN, Level 2 (Development Platform) CD4 \ODBC\X86 как часть ODBC 2.1 SDK. Имейте в виду, что смена драйверов (в частности Desktop Drivers) может негативно сказаться на работе других приложений Microsoft. Для информации (и замечаний) обращайтесь в форум WINEXT.
Также вам необходимы следующие файлы ODBC:
Минимум:
ODBC.DLL 03.10.1994, Версия 2.00.1510
ODBCINST.DLL 03.10.1994, Версия 2.00.1510
ODBCINST.HLP 11.08.1993
ODBCADM.EXE 11.08.1993, Версия 1.02.3129
Рекомендуется:
ODBC.DLL 12.07.1994, Версия 2.10.2401
ODBCINST.DLL 12.07.1994, Версия 2.10.2401
ODBCINST.HLP 12.07.1994
ODBCADM.EXE 12.07.1994, Версия 2.10.2309
Нижеследующие шаги приведут вас к искомой цели:
1. Используя администратора ODBC, установите источник данных (datasource) для вашей базы данных. Не забудьте задать путь к вашему mdb-файлу. Для нашего примера создайте источник с именем MYDSN.
2. Загрузите утилиту BDE Configuration.
3. Выберите пункт "New Driver".
4. Назначьте драйверу имя (в нашем случае ODBC_MYDSN).
5. В выпадающем списке драйверов выберите "Microsoft Access Driver (*.mdb)
6. В выпадающем списке имен выберите MYDSN
7. Перейдите на страницу "Alias" (псевдонимы).
8. Выберите "New Alias" (новый псевдоним).
9. Введите MYDSN в поле имени.
10. Для Alias Type (тип псевдонима) выберите ODBC_MYDSN.
11. На форме Delphi разместите компоненты DataSource, Table, и DBGrid.
12. Установите DBGrid1.DataSource на DataSource1.
13. Установите DataSource1.DataSet на Table1.
14. Установите Table1.DatabaseName на MYDSN.
15. В свойстве TableName компонента Table1 щелкните на стрелочку "вниз" и вы увидите диалог "Login". Нажмите OK и после короткой паузы вы увидите список всех имен ваших таблиц. Выберите одно.
16. Установите свойство Active Table1 в True и данные вашей таблицы появятся в табличной сетке.
С появлением и продвижением микрософтом OLE DB и реализацией в Дельфи ADO (начиная с версии 5.0) работа с MS Access через ODBC перестала быть актуальной. За исключением особых случаев рекомендуется пользоваться именно ADO линейкой компонентов для связи с MS Access
В последние время в сети все чаще и чаще появляются некие калькуляторы. Например, многие хостинговые компании предлагает вам калькулятор для расчета стоимости услуг размещения сайта. Все что вам необходимо - выбрать тариф, дополнительные услуги, ввести продолжительность хостинга и нажать кнопку "Рассчитать". После чего вы моментально получаете результат прямо на странице сайта хостера. Причем сама страница не обновляется! Это простой пример динамического изменения кода.
Ладно, хватит разговоров, давайте перейдем непосредственно к примерам. Для начала самый простой. Создайте новый HTML файл и пропишите в нем следующий код:
Код:
Теперь сохраните файл и откройте его браузером. Вы увидите кнопку с надписью "Нажми на меня". Нажимайте, не бойтесь, баннеры не появятся ?. Что мы видим? Код изменился. Под кнопкой появилась надпись "Спасибо!". Содержание страницы изменилось. Но если вы загляните в файл с кодом, он неизменен. Как такое возможно? Все очень просто. Дело в том, что JS является client-side технологией. Т.е. он исполняется на машине посетителя, а не на сервере. А теперь давайте разберемся с кодом. Итак, с начала ничего нового не видим: форма с кнопкой, при клике на которую исполняется функция “test_change”. Сама функция содержит одну - единую строчку:
Код:
Это и есть команда JS на вставку. Все что остается добавить это место, куда делать вставку. Место мы обозначили идентификатором "resultat". Идентификатор вы можете изменять вольно по своему усмотрению. Теперь создаем новый слой и связываем его с идентификатором "resultat":
Код:
Ну как, неплохо? Сразу хочу вас обрадовать, динамически можно вставлять не только простой текст, но и HTML теги! А теперь рассмотрим более сложный пример. Создадим калькулятор, который по веденным данным подсчитает вашу месячную зарплату ?. Итак создайте новый HTML файл и наберите следующее:
Код:
Сохраняем файл и открываем его браузером. Что мы видим? Три поля для ввода и кнопку с надписью "Подсчитать", при клике на которую запускается функция "getmoney". Она получает введенные значения, вычисляет месячную зарплату и выводит ее в браузере. Введите значения и нажмите кнопку "Подсчитать". Обратите внимание на значение месячной зарплаты. А теперь измените одно или несколько введенных значений и снова нажмите кнопку "Подсчитать". Как вы видите новое значение заменило старое. Очень удобно. А можно, например, выводить новое значение под старым, для этого нужно изменить строчку
Код:
на
Код:
Теперь новые данные будут прибавляться к более ранним, и все они вместе будут выведены на экран. Обратите внимание на добавленный HTML тег в конце кода. Это перевод строки. Вот мы и протестировали использование HTML кода в динамически изменяемых страницах.
Конечно, калькуляторы нужны далеко не каждому сайту. И правильно, не стоит на одном зацикливаться. Подключите свое воображение. Возможность динамически добавлять HTML код открывает воистину огромный потенциал для разработчика. Конечно, меню на таком коде вряд ли сможет превзойти выпадающее меню JS - кликать надоест. А вот для экономии места и для организации пояснений к разным элементам сайта такие возможности JS идеально подходят. Простой пример - страница контактов на сайте компании. Там, как правило, размещена контактная информация компании, дистрибьюторов, представителей, разработчика сайта. Куча адресов, телефонов и e-mail'ов. Так и заблудиться можно. А вот если разместить ссылки "Компания", "Дистрибьюторы", "Представители", "Разработчик", посетитель быстро сориентируется и выберет нужную ссылку. Осталось только написать JS код, который при клике на ссылку выводит под ссылкой соответствующую информацию и убирал ранее выведеную. Многие могут возразить, мол можно сделать просто ссылку на нужный файл и не нужно динамическое изменение текущего. Но, во-первых, возможно посетитель заинтересован не одной ссылкой - ему придется возвращаться, а во-вторых, намного приятнее кликнуть и моментально (!!!) увидеть нужную информацию (загруженную вместе со страницей), чем ожидать загрузку очередной страницы. Давайте я дам простой примерчик, а разработка подобного кода останется вам как домашнее задание для закрепления материала. Итак, код:
Код:
При открытии файла, содержащего вышеуказанный код, в окне браузера можно будет увидеть две кнопки: "Включить" и "Выключить". При клике на первую из них функции "test_on_off" передается значение 1, а при клике на вторую - 0. В зависимости от полученного значения функция "test_on_off" либо выводит таблицу, либо убирает ее. Это лишь простой пример. Его можно немного улучшить, убрав одну кнопку и подправив код:
Код:
Теперь кнопка работает как выключатель: кликнул - включил, еще раз кликнул - выключил, снова кликнул - опять включил...
Если кто чего недопонимает - смело пишите. Ну, а если кому облом код набивать, тоже пишите, вышлю.
В общем теперь вы имеете немалый арсенал средств для сайтостроителя, так что удачной работы!
Итак, что нам понадобится. В первую очередь - Delphi 5-7 (у меня стоит 7-я версия, и весь код тестировался именно в этой версии). Это вызвано тем, что компонент TWebBrowser впервые "прописался" на вкладке Internet именно в 5-й версии (в 4-й его надо было устанавливать как компонент ActiveX).
Сначала нам надо перевести WebBrowser в режим редактирования. Для этого у каждого документа (согласно объектной модели это document) существует свойство DesignMode. Если установить его в 'On', то наша компонента автоматически переключается в режим редактирования, а если установить его в 'Off', то компонент вернется в режим просмотра.
Проверим это! Создадим новую форму, разместим на ней компоненту TWebBrowser и несколько компонент TSpeedButton. Затем напишем такой код:
Код:
Теперь по порядку о том, что мы написали. В событии OnCreate формы мы загружаем в браузер простую страницу (напомню, что протокол About позволяет загружать в браузер HTML строку). Это необходимо для того, чтобы в последующем мы могли обращаться к документу. Сразу после этого будет вызван обработчик события OnDocumentComplete. Но пока еще ничего не произошло. Внимательный читатель мог обратить внимание, что для перевода браузера в режим редактирования надо нажать кнопку 1. Editor - это экземпляр нашего документа (document). Его свойство DesignMode устанавливается в 'On'. Теперь наш редактор практически готов. Он уже умеет править текст, копировать/вырезать/вставлять текст и картинки, делать текст жирным/подчеркнутым/наклонным. Для этого есть соответствующие комбинации клавиш.
Ctrl + C Копировать
Ctrl + X Вырезать
Ctrl + V Вставить
Ctrl + B Жирный текст
Ctrl + I Наклонный текст
Ctrl + U Подчеркнутый текст
Ctrl + Z Отменить
Ctrl + Y Повторить
Ctrl + K Гиперссылка
Ctrl + F Найти
Ctrl + A Выделить всё
Ctrl + Left-Click Выделить блок
"Это, конечно, хорошо, что есть горячие клавиши, но мне не хотелось бы все их запоминать" - можете сказать вы. Хорошо. Тогда давайте разберем, как из Delphi заставить WebBrowser выполнять все эти действия. Для этого есть метод Command интерфейса IHTMLTxtRange (он описан в модуле MSHTML_TLB). Рассмотрим простой пример.
Код:
Сначала в этой процедуре создается объект Range. После этого вызывается метод Command:
Код:
cmdID – это строка идентификатор команды (в нашем примере 'bold' заставляет редактор переключаться между жирным и обычным начертанием текста); полный список команд смотри в приложении.
ShowUI – Show User Interface - показывать интерфейс пользователя (если таковой имеется, как правило это различные диалоговые окна). Если параметр равен False, то команда выполняется без предупреждения.
value – содержит дополнительную информацию в зависимости от команды.
Несколько слов об объекте Range. Помимо уже знакомого нам Command этот объект обладает еще рядом свойств и методов, некоторые из которых сейчас рассмотрим.
Text - Содержит текст выделения (без тегов HTML)
HTMLText - Полный текст выделения
Код:
procedure - Перемещает начальную позицию выделения на count символов вправо (если count<0, то влево), unit_-единицы измерения смещения (чаще всего используется 'character': 1 символ). При этом конечная позиция не смещается.
Код:
То же самое, только для конечной позиции выделения.
Код:
Вставляет HTML-строку
Код:
Отображает помощь по команде, указанной в cmdID
Пожалуй, на сегодня всё. Об остальных объектах (картинки, таблицы, элементы управления) поговорим в другой раз. Будут вопросы - пишите: [email=samum2000@mail15.com?subject=Question about visualhtml part1]samum2000@mail15.com[/email].
Приложение. Доступные команды:
BackColor - Устанавливает или получает цвет фона текущего выделения. Value должно содержать имя цвета или его шеснадцитиричный RGB эквивалент (например, #FFCC00).
Bold - Переключает начертание текста текущего выделения между полужирным и нормальным.
Copy - Копирует выделение в буфер обмена
CreateBookmark - Получает имя якоря или создает его для текущего выделения. Value - строка, содержащая имя якоря.
CreateLink - Получает URL ссылки или создает новую ссылку. Параметр Value должен содержать URL.
Cut - Вырезает текущее выделение в буфер обмена.
Delete - Очищает текущее выделение (удаляет всё его содержимое).
Find - Находит текст, заданный в параметре Value в текущем выделении.
FontName - Устанавливает шрифт для текущего выделения. Value содержит описание этого шрифта (как в теге FONT).
FontSize - Устанавливает размер шрифта. Value - число от 1 до 7 включительно.
ForeColor - Устанавливает цвет текста. Value должно содержать имя цвета или его шеснадцитиричный RGB эквивалент (например, #FFCC00)
FormatBlock - Устанавливает или получает форматирование текущего блока. Value может содержать теги-описатели.
Indent - Увеличивает отступ выделенного текста на одну единицу приращения
InsertButton - Перезаписывает идентификатор кнопки вместо текущего выделения. Value - строка, содержащая идентификатор кнопки.
InsertFieldset - То же для поля ввода.
InsertHorizontalRule - То же для горизонтальной полосы.
InsertIFrame - То же для встроеных фреймов (IFRAME).
InsertImage - То же для изображений.
InsertInputButton - То же для кнопки.
InsertInputCheckbox - То же для чекбоксов (checkBox).
InsertInputFileUpload - То же для элемента выбора файла.
InsertInputHidden - То же для скрытого поля (hidden)
InsertInputImage - То же для изображения.
InsertInputPassword - То же для поля ввода пароля.
InsertInputRadio - То же для радио-кнопок (Radio)
InsertInputReset - То же для кнопки reset.
InsertInputSubmit - То же для кнопки Submit.
InsertInputText - То же для поля ввода текста.
InsertParagraph - Вставляет новый раздел (абзац).
InsertOrderedList - Переключает стиль текущего выделения между списком и простым текстом.
InsertUnorderedList - То же самое.
InsertSelectDropdown - Записывает элемент Drop-down вместо текущего выделения. Value должно содержать идентификатор элемента.
InsertTextArea - То же для элемента TextArea.
Italic - Переключает начертание текста текущего выделения между наклонным и обычным.
JustifyCenter - Устанавливает выравнивание по центру для всего блока, в котором расположено текущее выделение.
JustifyLeft - Устанавливает выравнивание по левому краю для всего блока, в котором расположено текущее выделение.
JustifyRight - Устанавливает выравнивание по правому краю для всего блока, в котором расположено текущее выделение.
Outdent - Уменьшает отступ для всего блока, в котором расположено выделение, на одну единицу.
OverWrite - Переключается между режимами вставки текста и замены текста при вводе. Value: true - замена, false - вставка.
Paste - Вставляет текст из буфера обмена вместо текущего выделения.
Refresh - Обновляет текущий документ.
RemoveFormat - Удаляет из текущего фрагмента все теги форматирования
SelectAll - Выделяет все содержимое документа.
UnBookmark - Удаляет все закладки из текущего выделения.
Underline - Переключает начертание текста текущего выделения между подчеркнутым и обычным.
Unlink - Удаляет все гиперссылки из текущего выделенного фрагмента.
Unselect - Снимает выделение.
Убрать значки с "Рабочего стола" и создать ОЧЕНЬ быструю панель можно нажав на "трэе" (там, где часы) правую кнопку мыши. В меню выбрать "Панели инструментов" - поставить галочку "Рабочий стол" (если там такового нет - нажать "создать" и там выбрать "Рабочий стол"). При этом слева от трэя появится оная перемещаемая панелька со стрелкой, разворачивающей всё содержимое "Стола". После этого зайти в свойства экрана, вкладка "Эффекты" и там установить галку "Прятать содержимое стола, если...".
Всё! Вся фишка в том, что:
1. не захламляются ваши обои и
2. (самое главное!) через эту менюшку можно практически мгновенно добраться до ЛЮБОЙ точки вашего компа НЕ ЗАПУСКАЯ ПРИ ЭТОМ ВСЯКИХ "ПРОВОДНИКОВ", "КОММАНДЕРОВ" и т.д., (заодно и ресурсы экономятся!) Попробуйте, не пожалеете!
З.Ы. Для 98 Выньды. Для остальных - не знаю, есть ли там такое... ;)
Если вам надо постоянно обращаться к какому-то элементу Панели управления, то можно создать ярлык на "Рабочем столе" с такой командной строкой: "CONTROL.EXE desk.cpl,,3". Desk.cpl указывает вызываемую функцию "Панели управления". Например, appwiz.cpl откроет окно "Установка и удаления программ", а цифра следующая после двух запятых - номер нужной вкладки, начиная с нуля.
Отключение Error Reporting - навязчивого желания ХР постоянно отсылать сообщения разработчикам по случаю возникновения каждой ошибки (например, зависания того или иного приложения). Эти желания режутся на корню просто, но со вкусом:
В Контрольной Панели выбирается Система (System).
Закладка Advanced, далее - Error Reporting.
Включается опция Disable Error Reporting. Опцию But Notify me When critical errors occur (уведомлять меня о возникновении критических ошибок) я оставил, чтобы не пребывать в неведении по поводу неожиданных сбоев (пока бог миловал).
Для того, чтобы вставить любое слово рядом с часами нужно сделать следующее:
1. Зайти на "Панель управления", затем "Язык и стандарты"
2. Перейти на вкладку "Время"
3. В поле "Формат времени" измените формат на "ЧЧ:мм:сс оо" (оо набирать русскими), в полях обозначения времени до и после полудня введите ваше слово.
После установки Windows можно переместить системную папку "Мои документы" на рабочий диск, предназначенный для хранения всего содеянного пользователем (я обычно присваиваю ему метку тома Documents). Все необходимые изменения в реестре Windows сделает самостоятельно.
Вы пользуетесь пунктом контекстного (всплывающего) меню файла или папки "Отправить"? Если нет, то вы много теряете! Когда вы кликаете правой кнопкой на файле или папке выбираете пункт "Отправить" и там выбираете, куда хотите отправить, то выбранный вами файл копируется (перемещается) в ту папку, которую вы выбрали. Здорово, правда?
Так вот ярлычки для пунктов назначения (папок) хранятся в X:\%windir%\SendTo, т.е. если у вас Windows установлена на диске C, то путь к папке SendTo будет выглядеть так: C:\windows\SendTo. Очень удобно!
Так вот суть этого совета:
1) Вы создаёте ярлык для папки SendTo.
2) Извращаетесь над ним (меняете иконку, переименовываете).
3) Копируете этот ярлык в папку SendTo.
Теперь, когда вы много всего копируете(перемещаете) в определённую папку, то создаёте для неё ярлык и отправляете в Отправить! Очень удобно! Сам испытал!
По сути не совет, а прикол для тех кому понравился вариант использования функции "Послать".
Если вы работаете в Инете, то слово "Послать" у вас ассоциируется с отправкой письма, а если вы к тому же и гражданин бывшего СССР, то и ещё кое с чем 8). Берём, создаём ярлык ссылающийся на корзину и переименовываем его в "На...*". Потом переносим этот ярлык в папку ..windows/SendTo/
Пользуйтесь на здоровье.
---
Сноска * - зависит только от вашей фантазии :)
В этой статье мы рассмотрим технику создания инифайлов их назначение и применение. Начнем с ответа на вопрос зачем же нужны эти инифайлы?! Предположим, что вы создали приложение, в котором пользователь может настраивать цвет фона, шрифт надписей и так далее. Когда он повторно включит вашу программу он очень сильно разочаруется, так как всего его старания по настройке интерфейса вашей программы пропали даром - программа будет иметь такой вид, который сделали вы при проектировании программы. Так вот чтобы эти настройки сохранять, лучше всего пользоваться инифайлами.
Одно из главных преимуществ инифайлов заключается в том, что эти файлы подерживают переменные разных типов (String, Integer, Boolean). В этих файлах очень удобно хранить различные настройки, например параметры шрифта, цвет фона, какие checkbox'ы выбрал пользователь и многое другое.
Теперь начнем разбираться с этими инифайлами. Для начала создайте новое приложение. Добавьте в секцию uses слово inifiles. Сохраните и откомпилируйте ваше приложение. Теперь сделаем, чтобы при каждом открытии программы форма имела такие размеры, какие установил пользователь последний раз. Для начала нам надо создать объект типа Inifile. Создается он методом Create(Filename:string); причем если в переменной Filename не указан путь к фалу, то он создаться в директории Windows, что не очень-то удобно. Поэтому мы создадим этот файл в директории нашей программы. Напишем это в обработчик события OnDestroy для формы:
Если файл с таким именем существует, то он откроется для чтения, а если нет - то он будет создан. Это очень удобно, так как не надо обрабатывать возможные исключительные ситуации, которые могут возникнуть при обращении к файлу.
Вот файл MyIni.ini после завершения работы программы (у вас естественно значения будут другими):
Теперь подробно разберемся как записывать информацию в инифайлы:
После того, как вы создали инифайл, в него можно записывать три вида переменных: Integer, String, Boolean, это осуществляется соответствующими процедурами: WriteInteger, WriteString, WriteBool. У всех этих процедур одинаковые параметры. В общем объявление этих процедур выглядит так:
Здесь Section -это имя секции, куда будут помещены параметры и значения. В файле имена секций заключены в квадратные скобки. Обычно в секции объединяют схожие параметры.
Ident - это название параметра, которому будет присваиваться какое-нибудь значение.
Value - это собственно значение, которое будет присвоено параметру. В файле оно стоит после знака равно.
Теперь напишем обработчик события OnCreate для формы, в котором будем считывать значения из файла и изменять размеры формы в соответствии с полученными значениями. Код должен иметь такой вид:
В этом коде все просто: открыли файл, прочитали из соответствующих секций необходимые параметры и присвоили их форме. Чтение значений из инифайла по сути ничем не отличается от записи в них. Указываете секцию, где хранится необходимый параметр, указываете параметр и читаете его значение. Как вы видите все просто!
Теперь я отвечу еще на один вопрос, который может появиться - почему не обычные текстовые файлы и не реестр? Отвечаю: из текстового файла очень сложно получить и обработать необходимую информацию. Многие рекомендуют для Win95/98/2000/Me, короче для всех 32-разрядных ОС использовать именно реестр, но лично я считаю, что инифайлы удобнее, так как при при переносе программы на другой компьютер, нужно перенести только один инифайл, а во-вторых, если вы что-нибудь в реестре случайно удалите, то может случиться каюк.
В данной статье рассмотрены принципы, помогающие компилятору Delphi генерировать более оптимальный с точки зрения скорости код. Если Вы не хотите вникать в подробности, в конце статьи есть «свод правил», которые рекомендуется соблюдать при написании программ.
Компилятор Delphi относится к разряду оптимизирующих. Но насколько качественно проводится оптимизация? Как «помочь» компилятору создать более быстрый код? Давайте разберемся с этим на экспериментах.
Оптимизация константных выражений
Пример 1:
С точки зрения оптимизации код можно упростить еще на этапе компиляции до
Но написанный выше листинг преобразуется в
С одной стороны компилятор не «сообразил», что значение переменной «a» можно преобразовать в константу и сложить с другой константой (которая, заметим, подставлена именно как константа) на этапе компиляции, с другой стороны был применен весьма хитрый трюк с LEA (об этом ниже). Тем не менее, код
в любом случае быстрее и короче.
Пример 2:
Скомпилированный код будет выглядеть
А ведь значение, присвоенной переменной «а» являлось константой и наш пример можно было бы переписать как:
Пример 3:
После компиляции получаем:
Т.е. компилятор преобразовал код так, как он был написан, а ведь можно было бы просто записать:
Оптимизация алгебраических выражений
Пример 4:
После компиляции эти переменные будут удалены, причем с предупреждением
Пример 5:
Код скомпилируется как есть! Таким образом мы обманули компилятор псевдо использованием переменных. Delphi не исправляет нашей «кривости», поэтому эта задача ложится исключительно на плечи программиста.
Пример 6:
Данный код можно оптимизировать до
И этого Delphi за нас не сделает.
Пример 7:
В данном примере первую строчку можно безболезненно удалить, что Delphi делать умеет.
Пример 8:
В данном случае можно избавится от одной операции умножения, присвоив значение выражения a*b временной переменной. Анализ ассемблерного листинга показывает, что компилятор именно так и поступает. Тем не менее, поменяв второе подвыражение на ((b*a)>0), компилятор принимает выражения за разные и генерирует умножение для обоих случаев, не смотря на то, что результат одинаков.
Оптимизация арифметических операций
Сложение и вычитание
Применение инструкции LEA вместо ADD позволяет производить сумму 3х операндов (двух переменных и одной константы) за один такт. Трюк заключается в том представление ближних указателей эквивалентно их фактическому значению, поэтому результат, возвращенный LEA равен сумме ее операндов. При возможности Delphi производит такую замену.
Деление
Операция деления требует гораздо больше тактов процессора, нежели умножение, поэтому замена деления на умножение может значительно ускорить работу. Существуют формулы, позволяющие выполнять такое преобразование. Тем не менее, Delphi не использует такую оптимизацию. Деление на степень двойки можно заменять сдвигом вправо на n бит, но даже в этом случае получаем следующий код:
Здесь учитывается особенность самой операции div – округление в большую сторону. Поэтому, если можно пренебрегать округлением, используйте c:=a shr 1 вместо с:=a div 2.
Умножение
Умножение на степень двойки можно заменять сдвигами битов. Delphi заменяет умножение сдвигами при умножении на 4,8,16 итд. При умножении на 2 производится суммированием переменной с собой.
Умножать на 3,5,6,7,8,10 и т. д. можно и без операции умножения – расписав выражение по формуле (a shl n)+a, где n – показатель степени двойки. Например, при умножении на 3 n=1. Delphi при возможности прибегает к этому трюку. Заметим, операнд LEA умеет умножать регистр на 2,4,8, что также при возможности используется компилятором. Например, умножение на 3 преобразуется в инструкцию
Оптимизация case of
Анализ скомпилированного кода показывает, что Delphi проводит утрамбовку дерева. Т.е. значения case сортируются и выбор нужного элемента производится при помощи двоичного поиска.
В случае, если элементы case of выстраиваются в арифметической прогрессии, компилятор формирует таблицу переходов. Т.е. создается массив указателей с индексами элементов, поэтому выбор нужно элемента выполняется за одну итерацию независимо от количества элементов.
Оптимизация циклов
Разворачивание циклов – не производится. Разворачивание циклов весьма спорный момент в оптимизации, поэтому принять грамотное решение может только человек. Delphi не производит разворачивания ни больших, ни маленьких циклов.
Слияние циклов – не производится. Если два цикла, следующие друг за другом имеют одинаковые границы итерационной переменной, разумно оба цикла объединить в один.
Вынесение инвариантного кода за пределы цикла – не выносится. Наиболее распространенный недочет – условие цикла записывается как:
Delphi будет при каждой итерации вызывать метод count, вычитать из результата 1 и потом уже сверять. Настоятельно рекомендуется переписывать подобный код как
Весь код VCL написан с нарушением этого правила. Очевидно, что проще подобного рода оптимизацию встроить в компилятор, нежели переписывать VCL :)
Замена циклов с предусловием на циклы с постусловием – производится. Циклы с постусловием имеют главное преимущество над другими видами циклов (с предусловием и с условием в середине) – они содержат всего одно ветвление. Delphi производит такую замену.
Замена инкремента на декремент – не производится. Более того, даже декрементный цикл компилируется в неоптимальный код, т.к. не используется флаг ZF. Вместо этого происходит сравнивание значения регистра с 0.
Удаление ветвлений – не производится.
Вывод:
1. Не используйте переменные для временного хранения констант или обязательно объявляйте «магические» числа как const, либо подставляйте в код непосредственные значения
2. Неиспользуемыми объявлениями и присвоениями можно безболезненно пренебрегать – Delphi умеет их вычищать.
3. Внимательно следите за использованием переменных, в частности лишним присвоениям их значений друг другу. Такого рода оптимизации Delphi делать не умеет.
4. Используйте свернутые математические выражения. (например, (3*a - a) /2 упрощается до a). Delphi не умеет упрощать математические выражения. (Да и что говорить, даже MathCAD не всегда грамотно умеет делать такие преобразования).
5. Не используйте конструкции типа a:=10*sin(45*pi/180); Delphi не вычислит эту константу на этапе компиляции, напротив, будет послушно вызывать sin и pi по ходу выполнения программы! В случае, если угол является переменной, по крайней мере pi можно заменить константой 3,1415...
6. Delphi прекрасно справляется с выражениями, полностью составленных из констант – они вычисляются на этапе компиляции.
7. Внимательно следите за условиями и их границами. Компилятор Delphi не умеет обнаруживать заведомо ложных условий. Также он не умеет удалять заведомо лишние условия. Например, (a>0) and (a<15616) and (a<>0)
8. Если в условии несколько раз проверяется одно и тоже выражение, следите, чтобы оно было выражено во всех конструкциях одинаково. В противном случае скомпилированный код будет не оптимален. Например, if ((a*b)>0) and ((a*b)<1024) then... При перестановке во втором случае b*a смысл выражения не изменится, но код будет иметь уже на одну операцию умножения, а две. Можно временно присвоить проверяемое выражение временной переменной, а затем уже проверять полученное значение.
9. Сообщение «Combining signed and unsigned types – widened both operands» сообщает не только о потенциальной ошибке – также вследствие преобразования мы теряем производительность. Например, z – объявлена как ineteger. условие if z>$abcd6123 then z:= $abcd6123; несмотря на его правильность вызовет данное предупреждение. Сгенерированный код будет, выполнять преобразования величин до 64-х бит, и дальнейшее уже сравнение 64-х битных операндов. Если изменить тип z на cardinal, мы избавимся от предупреждения и получим 3 строки кода, вместо 8 !
10. Delphi умеет оптимизировать сложение, умножение и частично деление. При делении на степень двойки, если не важно округление до большего, рекомендуется пользоваться shr 1 вместо div 2.
11. В case of при возможности используйте элементы, расположенные в арифметической прогрессии. Тем не менее, даже при невыполнении данного условия мы получим качественный код после утрамбовки дерева.
12. Выносите инвариантный код за тело цикла. Наиболее частая ошибка – for i:=1 to length(str) do... Дело в том, что при каждой итерации будет вызываться функция length, что пагубно скажется на производительности. Рекомендуется длину строки заранее присвоить переменной. Также не включайте в тело цикла код, заведомо не зависящий от изменения итерационной переменной.
Сравнивая Delphi с компиляторами Visual C++, WATCOM, Borland C++ (тестирование данных компиляторов приведено в [1]) приходим к выводу, что Delphi по своим оптимизирующим свойствам аналогичен Borland C++ (а кто сомневался? ;) ). Учитывая, что Borland C++ по итогам сравнения оказался последним, делаем несложный вывод. Весьма печален и тот факт, что большинство кода VCL написано с точки зрения «красоты» кода, а не его оптимальности с точки зрения скорости. Например, не соблюдается правило 12.
Создать гиперссылку в Delphi довольно просто. На простом примере разберемся с созданием ссылки в Delphi, а затем оформим все в виде компонента.
Алгоритм создания такой: ставим на форму метку (TLabel), приводим ее внешний вид к привычному нам виду гиперссылки в нашем браузере и пишем обработчик события OnClick.
А чтобы можно было постоянно использовать гиперссылку в программах, мы создадим компонент. Начнем с того, что поставим на форму нашего проекта метку (TLabel), пусть ее имя останется Label1. Теперь мы напишем обработчик события OnClick, для нее:
Теперь поясню что мы здесь написали. Функция ShellExecute предназначена для открытия или печати файла, как исполняемого, так и документа. Первый параметр - это handle родительского окна, второй параметр - строка, указывает, что надо сделать с файлом, третий параметр содержит имя открываемого файла, четвертый параметр указывает дополнительные параметры запуска исполняемого файла, пятый параметр определяет директорию по умолчанию, последний параметр определяет где будет отображен файл после октрытия.
Если Вы уже попробовали запустить приведенный код, то скорее всего у Вас ничего не вышло, потому что функция ShellExecute, находится в модуле ShellAPI, который конечно же надо добавить в секцию uses, кода нашего приложения.
Теперь разберем параметры относительно нашего случая:
handle - это дескриптор главной формы (аналогично Form1.handle)
open - тип действия с файлом. Нам надо его открыть.
http://delphiworld.narod.ru/ - имя файла, который надо открыть. У нас это может быть гиперссылка, содержащая абсолютный URI.
nil - здесь никаких дополнительных параметров открытия файла не должно быть, поэтому nil.
nil - директория по умолчанию нас так же не интересует.
SW_SHOW - активирует окно и отображает его с текущими размерами и положением. Об остальных режимах можно узнать в хелпе (о функции ShellExecute).
Второй и третий параметры функции являются нуль терминированными строками, т.е. строками типа PChar, поэтому для использования в функции имени файла, полученного из OpenDialog1, нужно использовать PChar(OpenDialog1.Filename).
В браузере (при настройках по умолчанию) ссылка меняет цвет в зависимости от своего состояния и действий пользователя, мы тоже сделаем так. Для этого создадим три константы (в них будут определяться цвета), которые надо поместить в раздел Implementation:
Теперь в обработчике события формы OnCreate нужно написать:
В обработчике события метки OnMouseDown мы напишем:
А в обработчике события OnMouseUp нашей метки напишем:
Для придания полной реалистичности нашей ссылке, нужно установить свойство метки Cursor в crHandPoint. При наведении на ссылку указатель будет иметь вид привычной нам кисти руки с вытянутым указательным пальцем и ссылка будет подчеркнутой.
Ну вот и разобрались, а теперь напишем компонент. Там все предельно просто и понятно, поэтому объяснения напишу только в виде комментариев в коде.
В качестве родительского класса (Ancestor Type) мы конечно же должны выбрать TLabel. Привожу полный код модуля компонента Link класса Tlink (текст модуля надо сохранить в файле Link.pas):
Вот мы и разобрались с созданием гиперссылок в Delphi, как оказалось все очень просто.
Часто работая в Фотошоп, приходится выполнять целый ряд постоянно повторяющихся операций. Такие операции выстраиваясь определенным образом, образуют цепочку команд - макропоследовательностей. Чтобы не повторять последовательность одних и тех же команд снова и снова, можно задействовать инструменты автоматизации. Одним из таких инструментов является Actions.
Автоматизация работы в Photoshop.
Часто работая в Фотошоп, приходится выполнять целый ряд постоянно повторяющихся операций. Такие операции выстраиваясь определенным образом, образуют цепочку команд - макропоследовательностей. Чтобы не повторять последовательность одних и тех же команд снова и снова, можно задействовать инструменты автоматизации. Одним из таких инструментов является Actions. Action - это записанный набор команд, которые можно быстро повторить. За такими макропоследовательностями также можно закреплять клавиатурные сокращения, то есть для их выполнения достаточно нажать клавишу или сочетание клавиш.
Для работы с Actions используется одноименная палитра. Чтобы увидеть эту палитру, следует выполнить команду Window>Actions или нажать клавишу F9.
Стандартные Actions
По умолчанию в Photoshop уже есть стандартный набор записанных команд, поэтому списки последовательно выполняющихся команд можно создавать самостоятельно, а можно воспользоваться уже готовыми. Например, используя стандартный макрос для создания виньетки (Vignette), достаточно выделить область на изображении и нажать кнопку его запуска.
Таким образом, все макропоследовательности состоят из команд, которые являются последними ступеньками в раскрывающемся списке.
Для удобства работы, макропоследовательности объединяются в наборы - Sets. Например, можно создать набор, в котором будут собраны макросы для работы с текстом, для создания различных текстур и т.д. По умолчанию в Photoshop есть набор Default Actions.
Палитра Actions содержит наборы различных последовательностей. Чтобы их увидеть, необходимо щелкнуть на треугольнике, расположенном слева от названия набора. После этого перед вами появится список действий, любое из которых может быть выполнено. Если вы хотите увидеть, из каких операций состоят действия, то следует щелкнуть на треугольнике, расположенном слева от них.
Внизу палитры расположены кнопки, позволяющие управлять действиями - создавать новые, запускать и останавливать запись и т.д.
Просмотрите все предлагаемые Actions на изображении с небольшим разрешением (на нем макрокоманды будут выполняться быстрее). После такого просмотра можно сформировать свою палитру, оставив понравившиеся элементы и удалив те, которые вам никогда не пригодятся. Можно также изменить названия, чтобы стало понятнее, где какая возможность скрывается.
Если какое-нибудь действие вам очень понравилось, то посмотрите, как оно было сделано. Для этого следует обратиться к палитре Actions и, раскрыв все списки, просмотреть последовательность выполняемых операций.
Лучше всего не только просмотреть, как выполнялись операции, но и попытаться добиться такого же эффекта, проделав самостоятельно все действия, особенно если вы неопытный пользователь. Это поможет лучше узнать программу, а также ознакомиться с возможностями, которые она предоставляет. Кроме того, в процессе работы можно будет изменить некоторые параметры, подобрав их для конкретного изображения.
Создание собственных макрокоманд
Можно не только пользоваться готовыми макрокомандами, но и создавать свои собственные.
Приведем пример ситуации, в которой они могу оказаться полезными. Предположим, вы хотите выложить фотографии в интернете. Прежде чем публиковать их в сети, нужно выполнить цветокоррекцию и уменьшить размер. Допустим, вы решили делать цветокоррекцию в режиме Lab Color.
Рассмотрим список действий, которые необходимо выполнить с каждым изображением:
1) Выполнить команду Image>Mode>Lab Color, чтобы перевести снимок в цветовое пространство Lab Color.
2) Повысить четкость изображения, для чего нужно перейти на палитру Channels, выделить канал Lightness и применить к нему фильтр Sharpen>Unsharp Mask. Благодаря тому, что фильтр применяется в канале освещенности, можно повысить резкость изображения, не затрагивая цветовую составляющую.
3) Улучшить цвета на изображении, для чего нужно выделить цветовой канал b, вызвать окно редактирования кривых, выполнив команду Image>Adjustments>Curves или же используя сочетание клавиш CTRL+M, и изменить форму кривой. Чем круче график кривой, тем более контрастным становится изображение в канале, и тем более насыщенными становятся цвета на снимке.
4) Выделить цветовой канал a и проделать те же действия с изменением формы кривой.
5) Наконец, нужно изменить размер изображения, используя команду Image>Image Size.
Если после этого отобразить палитру History, можно увидеть, что пришлось выполнить целый ряд команд и действий, чтобы обработать фотографию. Эти действия нужно было бы повторять с каждой следующей фотографией, которую вы хотите выложить в интернете.
Задачу можно существенно упростить, используя Actions. Вернемся к исходному изображению, используя палитру History, и создадим собственный макрос. Сначала создадим новую группу для хранения макросов. Для этого нужно нажать кнопку Create New Set в нижней части палитры Actions.
В окне New Set введим название набора команд. Создадим в новой группе первый макрос, нажав кнопку Create New Action. В появившемся окне New Action выберем название макроса и клавишу, при нажатии которой он будет запускаться, например, F11. Для вызова макропоследовательностей можно использовать клавиши F2-F12, а также их сочетания с клавишами CTRL и Shift.
Для начала записи необходимо нажать кнопку Record в нижней части палитры Actions. Теперь любая команда, выполненная в Photoshop, будет запоминаться до тех пор, пока вы не остановите запись макроса.
Скроем палитру Actions, чтобы она не мешала, и повторим выполненные ранее действия: переведем изображение в Lab Color, увеличим резкость, изменим цветопередачу и уменьшим размер изображения при помощи команды Image Size.
После этого вернемся на палитру Actions и остановим запись, нажав кнопку Stop Playing/Recording. В списке нашего макроса можно увидеть записанную последовательность команд. Также рядом с названием макроса отображается горячая клавиша, которая выбрана для его запуска.
Чтобы проверить действие макроса, вернемся к исходному изображению, используя палитру History, и нажмем горячую клавишу, которую вы выбрали для выполнения макроса. Можно убедиться, что все действия выполняются автоматически и довольно быстро. Макрос также можно запускать, нажимая кнопку Play Selecтion в нижней части палитры Actions, если предварительно выделить его в списке макрокоманд.
Записав макрос, посмотрите, как макрокоманда сработает на другой фотографии. Откройте изображение в Photoshop и снова запустите макрос горячей клавишей. Как вы можете убедиться, макрос работает.
Таким образом, благодаря макросам на подготовку фотографий для публикации в интернете может уходить гораздо меньше времени. Достаточно нажать горячую клавишу - и можно сохранять изображение.
Сохранение макрокоманд
В ряде случаев, например, при переустановке системы или при необходимости использовать созданные ранее макросы на другом компьютере, возникает необходимость сохранить Actions. Такая возможность предусмотрена в Photoshop.
Actions нельзя сохранять по отдельности, только в наборах. Поэтому для сохранения макропоследовательностей выделите на палитре Actions тот набор, в который они входят, после чего нажмите миниатюрную кнопку, расположенную в верхней правой части палитры (под кнопкой для сворачивания палитры) и выберите команду Save Actions. Если при этом будет выделен не набор, а отдельный макрос, эта команда будет неактивна.
Наборы макросов сохраняются в файлы с расширением ATN. Для загрузки сохраненного ранее набора в Photoshop необходимо щелкнуть по той же кнопке на палитре Actions и выбрать команду Load Actions.
Инструмент Batch
В Photoshop есть еще одно средство для автоматизации, которое удобно использовать вместе с Actions. Это - инструмент пакетной обработки Batch. С его помощью можно применить макропоследовательность к группе файлов, даже не открывая их в Photoshop.
Приведем простой пример использования этого инструмента. Предположим, необходимо перед публикацией фотографий в интернете защитить свое авторское право. Для этого можно добавить на фотографии какой-нибудь текст, например, адрес сайта.
Для начала запишем в макрос все действия, которые необходимо выполнить. Создадим новый Action и начнем запись. Макрос будет состоять из трех действий: добавление на изображение текста, сохранение изображения в формате JPEG и закрытие исходного файла. После выполнения этих операций остановим запись макроса.
Выполним команду File>Automate>Batch. В окне Batch необходимо установить настройки пакетной обработки файлов. В разделе Play выбирается группа, в которую сохранен макрос и его название.
В разделе Source необходимо указать путь к папке, содержащей исходные файлы. Если установить флажок Include All Subfolders, то будут обработаны и файлы, которые находятся во вложенных папках.
В разделе Destination устанавливаются параметры сохранения файлов. Если выбрать в этом списке вариант None, то файлы не будут сохраняться автоматически. Вместо этого Photoshop будет выдавать запрос на сохранение каждого файла. При выборе варианта Save and close файлы будут сохранены в ту же папку, где хранятся исходные изображения.
Для того чтобы файлы были автоматически сохранены, нужно выбрать в списке Destination вариант Save and close. При выборе варианта Folder появляется возможность указать папку для сохранения обработанных файлов. Кроме этого, можно переименовать их, используя маску. Название файла может включать исходное имя, представленное строчными или заглавными буквами, дату выполнения операции в разных форматах, порядковый номер, букву алфавита, расширение.
Флажок Override Action Save As Commands нужно использовать осторожно. Если он установлен, то файлы будут сохранены только в том случае, если операция сохранения является одним из шагов макроса.
Для запуска пакетной обработки необходимо закрыть окно Batch, нажав кнопку OK. После этого файлы будут один за другим открываться в Photoshop, к ним будут применяться заданные операции, после чего они будут закрываться и сохраняться.
Batch и Actions - это очень полезные инструменты Photoshop, которые могут сэкономить не один час времени. Кроме этого, Actions могут стать хорошим пособием для изучения программы - загрузите в Photoshop наборы макропоследовательностей, созданные опытными пользователями, и пошагово разберите, как они работают.
Эта статья рассчитана на тех, кто только начал не давно осваивать векторный графический редактор и не всегда всё получается. Рисовать одуванчик не так уж сложно. Можно получить прекрасный результат и при этом потратить минимальное количество сил.
Нарисуем пушистый цветок.
Эта статья рассчитана на тех, кто только начал не давно осваивать векторный графический редактор и не всегда всё получается. Рисовать одуванчик не так уж сложно. Можно получить прекрасный результат и при этом потратить минимальное количество сил.
Сначала нарисуем хохолок одуванчика. Для этого заходим в Banner Shapes и выбираем звездочку:
Затем кликаем правой кнопкой мыши на объекте и выбираем Convert to Curves:
Выберем для звездочки PaleYellow цвет на цветовой шкале справа; уберем обводку. Изгибаем прямые стороны: щелкаем правой кнопкой мышки на линии и выбираем To Curve:
Дорисовываем карандашом коричневую ножку:
Размножаем объект (Ctrl+C - Ctrl+V) и располагаем по кругу ножкой к центру.
Затем рисуем стебель одуванчика. И используя такой вот линейный градиент, зарисовываем стебель:
Вот и получился полноценный одуванчик! Для завершения композиции скопируем одуванчик несколько раз и добавим голубой фон:
Эта первая статья, которая открывает цикл статей о работе с графическим редактором Corel Draw. Думаю, они помогут всем желающим обучится работать с векторной графикой. Раскрыть для себя все возможности вектора.
Попробуем нарисовать время, а верней его оболочку в виде песочных часов.
Corel Draw. Рисуем часы.
Эта первая статья, которая открывает цикл статей о работе с графическим редактором Corel Draw. Думаю, они помогут всем желающим обучится работать с векторной графикой. Раскрыть для себя все возможности вектора.
Попробуем нарисовать время, а верней его оболочку в виде песочных часов.
1. Инструментом Ellipse Tool (F7) рисуем круг, для ровного круга зажимаем Ctrl, затем конвертируем круг в кривую Arrange-Convert to curves(Ctrl+Q)
взяв нижнюю точку, приспустим её вниз, как на рисунке!
2. Копируем этот овал (Shift+ тащим Pick Tool к середине объекта) и немного дорабатываем его форму, сделаем его немного тупее (кстати, скопируйте этот внутренний овал и положите его в уголок, он нам еще пригодится!).
3. выделим первые два объекта и скопируем их вниз (выделяем Pick tool, зажимаем Ctrl и тащим)
полученные объекты немного вытянем из верхней точки, как рисунке. На нём, кстати, не очень видно у вас должно быть поострее! Делаем мы это специально потому, что на наши будущие часы мы будем смотреть немного сверху!
4. Теперь инструментом Elipse Tool (F7) нарисуем овал, скопируем его и расположим как на рисунке, сверху инструментом Rectangle Tool рисуем прямоугольник!
5. Обрезаем кружками квадрат, для этого сначала выделяем круг, затем квадрат (зажав Shift) и щелкаем по кнопке Trim.
Повторяем это всё со вторым кругом.
6. Теперь выделим два больших овала и нажмем Weld! Смотрим, что получилось!
кстати, на этом этапе при объединении могут получиться лишние точки их надо удалить, и если надо поменять тип точки
7. Такой же трюк проделываем с внутренними овалами.
У нас уже есть колба для песка она конечно одна но будем называть её двумя объектами большой колбой и малой (внутренней).
Возьмите внутреннюю "колбу" и вырежьте отверстие. Но её не удаляйте!
8. Теперь берем опять инструмент Elise Tool (F7), рисуем овал, затем копируем его и размещаем две копии наверху,
чем выше круг тем он должен быть уже! Дорисуем два прямоугольника, как на рисунке!
9. Выделим пять последних фигур, скопируем и утащим вниз объекта ( отзеркалив их) и забудем о них на некоторое время!
10. Сосредоточим внимание на верхней части!
Объединим нижний овал с большим прямоугольником. Как объединять объекты вы уже знаете.
Теперь объединим средний овал и небольшой прямоугольник. Если вы зальёте эти объекты, получится как на рисунке.
11. Заливаем объекты инструментом Fountain Fill Dialog (F11) или кликаем по палитре сбоку левой кнопкой мыши.
Если вдруг у вас объекты будут расположены в другой последовательности, то для того что бы верхний овал был самым верхним, жмем Shift+Page Up (самым нижним Shift+Page Down) если надо сместить только на один слой Ctrl+Page Up (Down)
Не уделяйте сейчас внимание заливке, о ней поговорим потом.
12. Теперь пошли вниз. Почему мы просто не копируем верхнюю часть? Потому что линия горизонта над предметом низ нам видно больше и вид там будет совершенно другой.
Рисуем заново все объекты или используете то, что вначале утащили вниз в запасник!
Берем овал, обратите внимание, как он расположен и растяните его по высоте, теперь мы видим его больше. Смело копируйте его в середину!
Вырежьте меньшим дырку в большем. Он обозначен желтым цветом! Меньший положим сбоку, потом еще попользуем!
13. Скопируем колечко и уменьшим его немного, смотрите как на рисунке. Для того, что бы было понятнее, назовём колечки 1(большее) и 2(меньшее).
Берем колечко 1-нарисуйте любую форму и обрежьте им колечко, можно обрезать тем овалом, что мы отложили. Обратите внимание на уголки!
14. Берем запасное кольцо 2, располагаем, как на рисунке затем рисуем овал и обрезаем им колечко, с помощью Intersectin. Полученная форма выделена зеленым. Теперь совместим их.
Это будет наше преломление стеклом. Если бы у нас был фон с узорами, он бы тоже должен был преломиться!
15. Дальше уже известным нам методом объединения и обрезки, получаем из прямоугольника и овала, нижнюю часть подставки. Помним что овал у нас теперь очень широкий.
16. Дорисовываем основание подставки тем же методом, что и верх. Здесь используется объединение готовых форм потому что нам важно сделать симметричный объект. Возьмем отложенный овал и подложим его в самый низ!
17. Вот почти всё готово! Меньшую "колбу" заливаем и делаем ей прозрачность инструментом Interactive transparenci Tool
Заливаем все, чтоб нам было понятно, что это у нас такое! А это часы без песка!
18. Теперь рисуем сам песок. Сначала рисуем овал, наклоняем его, на рисунке песок сыплется немного наклонно.
Затем рисуем прямоугольник. Сначала выделяем малую ”колбу”, затем новоиспеченный прямоугольник и жмем Intersect!
Подгоняем точки по узкой форме и заливаем цветом песка. Можно продлить этот объект вниз и нарисовать как бы падающую струйку песка. Добавим несколько песчинок для декоративности.
19.На самом верхнем овале песка нарисуем поменьше, зальем более темным цветом и применим интерактивное перетекание Interactive Blend Tool.
Ниже нарисуем небольшую горку упавшего песка. И заливаем просто градиентной заливкой.
20. А теперь самое важное - рисуем блики. Чем больше бликов, тем лучше! Мы можем скопировать меньшую колбу и деформируя её по нескольким точкам также добавить блики. Очень удобно брать её как основу, потому что блики должны идти именно по её форме. Здесь нам пригодится отложенный в начале овал, его тоже удобней подгонять по форме колбы.
Рисуем мелкие блики, деформируя круги по точкам! Затем заливаем и применяем прозрачность.
21. Теперь дорабатываем заливку.
Добавим тени!
Рисуем овал, добавляем тень инструментом Interactive Droр Shadow Tool. Отделим тень от объекта Ctrl+K, удалим овал.
Таким же способом нарисуем тень на верхней подставке.
Еще можно добавить опорные ножки. Рисуют их так же как всё, объединяя квадрат и овал.
В этом уроке изложен принцип рисования подобной формы вы можете усложнить или облегчить саму форму, добавить красивых деталей, прорисовать блики, инкрустировать корпус камушками... В общем, фантазируйте!