VR-online JOURNAL Фленов Михаил and VR-Team VR-online для программистов №7
VRML: Свойства примитивов ......................................................................................................3 Delphi: Очень полезный примерчик ............................................................................................7 Delphi: Изучаем Delphi................................................................................................................12 Delphi (Графика): Читаем PSD файлы.......................................................................................16 SQL: Ещё несколько операторов ...............................................................................................20 Delphi (ActiveX): Превращаем текст в речь ..............................................................................22 Программирование звука: Боевой комплект ............................................................................26 Java: События...............................................................................................................................33 VRML: Ещё чуть-чуть.................................................................................................................37 Форматы файлов: PSD (окончание)...........................................................................................40 Процедурное и ООП....................................................................................................................44
Copyright: VR-online Journal http://www.vr-online.ru
VR-online Journal (Horrific and VR-Team)
Почему-то многие люди, которые покупают себе компьютер, начинают думать, что в ближайшем времени они станут программистами или хакерами. Они считают, что если они прочтут книжку из серии «Для чайников» то сразу станут экспертами в какойнибудь области. Почему же многие сейчас стремятся стать хакерами/программистами? Именно этот вопрос я задаю своим друзьям внезапно загоревшимися такой идеей. Но ответ в основном один и тот же: «Ты че ламер, быть хакером это же круто!». И многие из них даже не знают отличия между понятием «хакер» и «cracker». Лично я такой тип людей просто не понимаю. Неужели если у тебя появляется компьютер, то ты просто обязан стать хакером? Ведь компьютер же создан не только для хакеров! Но многие этого просто не могут понять :( Сейчас в инете все больше и больше появляются сайты так называемых «хак-групп». И многие из них называют себя хак-группами только потому, что они взломали какой-то сервер с помощью чужого эксплоита. Разве таких людей можно назвать хакерами? Я думаю, что нет. Ну почему-то они так не думают… Подводя итог хочу сказать. Перед тем как начинать что-либо изучать задайте себе простой вопрос: «А оно мне надо?». Если вам это не интересно просто не тратьте свое время, займитесь лучше чем-нибудь другим. Тем самым вы сделаете лучше только себе… Spider NET
http://www.vr-online.ru
Для программистов №7
VR-online JOURNAL Horrific aka Фленов Михаил
INFO: ИДЕЯ И РЕАЛИЗАЦИЯ: Флёнов Михаил (Horrific) ГРАФИКА: Фленов Михаил, tr4sh
VR-Team: Crazy_Script, Del, Demogorgon, Fighter, Mish!, Negus, Spider NET, tr4sh INTERNET: WWW: http://www.vr-online.ru E-MAIL: horrific@vr-online.ru ДИЗАЙН САЙТА: tr4sh КОДИНГ САЙТА: Mish!
Данный журнал распространяется в виде PDF файлов. Вы можете выкладывать номера на любые носители без изменения внешнего вида журнала, без перевода в другие форматы, без изменения самого файла. В журнал запрещается вносить изменения. Перепечатка материалов запрещена. Журнал распространяется бесплатно, и ты можешь скачать его с нашего сайта, поэтому мы не видим смысла в перепечатывании материалов. Если ты хочешь стать автором журнала, то присылай свою статью на наш e-mail и мы обязательно включим её в очередной номер.
2
VR-online Journal (Horrific and VR-Team)
Для программистов №7
VRML: Свойства примитивов Сегодня мы продолжим знакомится с языком VRML. В прошлый раз я рассказал тебе о примитивах. Мы научились их создавать и отображать в окне браузера. Сегодня нам предстоит научится изменять их свойства, такие как цвет, материал и текстура. Возможно, что я расскажу ещё что-нибудь. Посмотрим как всё будет получаться. Для указания цвета и материала примитива используется одна и та же функция material. material { diffusecolor 0 0 1 emissivecolor 0 0 1 ambientcolor 0 0 1 specularcolor 0 0 1 transparency 0 } Если ты читал мои статьи про OpenGL или просто уже знаешь этот язык программирования, то наверно заметил сходство в названиях свойств материала. Это действительно одни и те же свойства. Четыре свойства материала diffusecolor, emissivecolor, ambientcolor, specularcolor задаются значением цвета в формате RGB. transparency - задаёт степень прозрачности и может быть в пределах от 0 до 1. Если ты поставишь 1, то объект будет абсолютно прозрачен. Вот пример синего кубика-рубика material { diffusecolor 0 0 1 emissivecolor 0 0 1 ambientcolor 0 0 1 specularcolor 0 0 1 transparency 0 } cube{ width 5 height 10 depth 8 } Для натягивания на объект текстуры используется команда Texture2. Она загружает графические данные из файла и использует их в качестве обоев для примитива. В качестве графического файла я бы посоветовал использовать gif, потому что его поддерживают большинство просмотрщиков VRML. Вот как выглядит команда: Texture2{ filename "filename.gif"
http://www.vr-online.ru
3
VR-online Journal (Horrific and VR-Team)
Для программистов №7
image 0 0 0 wrapS REPEAT wrapT REPEAT } Здесь опять же просматривается аналогия с OpenGL. Всё те же wrapS и wrapT, которые могут принимать значения REPEAT или CLAP. Для тех, кто не знает OpenGL поясняю, что эти параметры управляют натягиванием текстуры на примитив по горизонтали и вертикали. REPEAT заставляет текстуру повторятся по соответствующей оси, а CLAP растягиваться по всей оси. Параметр image указывает смещение текстуры по осям X, Y и Z. Теперь мы посмотрим, как можно изменять положение объекта. Без этого наша сцена глупый набор примитивов, потому что мы не можем задать их расположение. Для объекты нужно перемещать с помощью команды translation: Translation { Translation 1 7 5 } Эта команда перемещает объект по осям X, Y и Z. В данном примере я произвожу смещение по оси Х на 1, оси Y на 7 и оси Z на 5. Начало координат (точка 0 0 0) находится в центре экрана. Когда ты начинаешь рисовать, примитивы появляются именно там. После смещения относительно текущей точки с помощью команды translation объекты начнут рисоваться в новом месте. Если ты произведёшь ещё одно смещение, то оно произойдёт относительно текущей координаты, а не относительно начала координат. Для большей ясности рассмотрим пример: #VRML V1.0 ascii #первая сфера в центре экрана sphere { radius 1 } #сдвигаем центр координат на 1 по Х и 3 по Y. Translation { Translation 1 3 0 } #рисуем сферу в новом месте sphere { radius 1 } #сдвигаем центр координат на 1 по Х и -3 по Y. #относительно предыдущей оси координат Translation { Translation 1 -3 0 } #рисуем сферу в новом месте sphere { radius 1 } Реальное положение последней сферы будет рассчитываться как расположение первой (0 0 0) + первое смещение (1 3 0) + второе смещение (1 -3 0) и всё это равно (2 0 0). Вот оно реальное положение третей сферы относительно центра координат. В OpenGL, для уничтожения такого эффекта использовались glPushMatrix и glPopMatrox. Здесь есть подобная штучка - separator {} Рассмотрим пример #VRML V1.0 ascii
http://www.vr-online.ru
4
VR-online Journal (Horrific and VR-Team)
Для программистов №7
separator{ #здесь запоминается текущие координаты #сдвигаем центр координат на 1 по Х и 3 по Y. Translation { Translation 1 3 0 } #рисуем сферу в новом месте sphere { radius 1 } } #здесь восстанавливаются координаты. #сдвигаем центр координат на 1 по Х и 3 по Y. Translation { Translation 1 3 0 } #рисуем сферу в новом месте sphere { radius 1 } separator{ #здесь запоминается текущие координаты #сдвигаем центр координат на 1 по Х и -3 по Y. #относительно предыдущей оси координат Translation { Translation 1 -3 0 } #рисуем сферу в новом месте sphere { radius 1 } } #здесь восстанавливаются координаты. Перед входом в первый separator сохранились текущие координаты (0 0 0). Мы сдвинули первую сферу и нарисовали её внутри separator. После выхода текущая координата вернулась в ту, что была до входа (0 0 0). Потом я сдвигаю координату в точку (1 3 0) и рисую вторую сферу. Далее идёт ещё один separator, который сохраняет текущую координату (1 3 0). Внутри сепаратора я произвожу сдвиг относительно текущей координаты (1 3 0) на (1 -3 0) и рисую ещё одну сферу в новой точке (2 0 0). После выхода из separator текущая координата возвращается в позицию (1 3 0), которая была до входа. Если ты уже изучал OpenGL, то никаких проблем с пониманием происходящего не будет. Если нет, то возможны проблемы. Если что-то непонятно, то попробуй создать этот пример и поиграть с ним. Я думаю, что на сегодня хватит. Мы и так изучили достаточно много. В следующий раз мы уже наверно закончим с изучением основ VRML 1.0. Здесь нет ничего удивительного, потому что этот язык очень простой и имеет не так уж и много операторов. Хотя во второй версии добавлено достаточно много нового и возможно, что мы немного позже познакомимся с этими нововведениями. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
5
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Тебя мучает одна проблема, которую ты не можешь решить? Заходи на форум на нашем сайте, подумаем вместе!!! На нашем форуме ты можешь задать любой вопрос и получить ответ по следующим темам: • Программирование (Delphi, JBuilder, C++ Builder, Kylix, Visual C++Visual Basic и другие); • Технологии программирования (сети, мультимедиа, DirectX, OpenGL); • Администрирование; • Операционные системы; • Базы данных (SQL Server, язык SQL и другие); • Железо; • Internet технологии (Perl, PHP, Flash, XML); • Софт; • Сети; Адрес сайта http://www.vr-online.ru
http://www.vr-online.ru
6
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Delphi: Очень полезный примерчик Сегодня я решил собрать несколько пришедших мне вопросов под одной прогой. Для её реализации я написал очень полезный примерчик. В нём есть работа с реестром, запуск внешних прог, создание ссылок на свой сайт, элегантная перезагрузка компьютера. И всё это очень интересно запаковано. В качестве работы с реестром я покажу тебе как, правильно сохранять позицию и размер окна при закрытии проги. А самое главное это восстанавливать эти значения. На рисунке 1 ты как всегда можешь увидеть пример формы. Если ты любишь создавать примеры полностью сам, то нарисуй себе такую же. Если нет, то в конце статьи есть ссылка на исходник. Вообще-то я советую тебе всё делать своими руками. В этом случае всё лучше воспринимается и запоминается. Если что-то не получается, то тогда обращайся к моему примеру. Для начала рассмотрим, как я сохраняю и восстанавливаю позицию окна. Для сохранения я написал функцию SaveProgParam, которую вызываю по событию OnClose : Рис 1. Форма procedure TForm1.SaveProgParam; var FIniFile: TRegIniFile; begin FIniFile := TRegIniFile.Create('Software'); // Инициализирую реестр FIniFile.OpenKey('VR',true); // Открываю раздел FIniFile.OpenKey('VR-Online',true); // Открываю ещё один раздел if WindowState=wsNormal then begin FIniFile.WriteInteger('Option', 'Width', Width); FIniFile.WriteInteger('Option', 'Heigth', Height); FIniFile.WriteInteger('Option', 'Left', Left); FIniFile.WriteInteger('Option', 'Top', Top); end; FIniFile.WriteInteger('Option', 'WinState', Integer(WindowState)); FIniFile.Free; //Освобождаю реестр end; Как работать с реестром я уже рассказывал в ноябре 2000, поэтому сегодня я проскакиваю некоторые моменты. Если ты уже читал ноябрьский номер, то тебе достаточно будет моих комментариев к тексту. Если что-то не понял, то желательно перечитать.
http://www.vr-online.ru
7
VR-online Journal (Horrific and VR-Team) После инициализации реестра и подготовки разделов я делаю проверку, в каком состоянии находится окно. Если WindowState равно wsNormal, то я сохраняю параметры окна. Если нет, то этого делать не надо. Если у тебя стоит разрешение экрана 800х600, по при максимизированном окне значение ширины окна будет 802, а высоты 602. Эти значения больше реального разрешения и если ты установишь их при загрузке, то изменить размеры окна мышкой будет очень трудно.
Для программистов №7 Анекдот: Хакер, тыча клавиатурой в своего кота: "Зухель, коннект!". Кот:"Пшшшш..." Хочешь ещё? Читай на нашем сайте в разделе «Юмор»
После этого я сохраняю состояние окна - WindowState. Так как оно имеет тип ТwindowState, то мне приходится приводить этот тип к Integer с помощью Integer(WindowState). Процедуру для чтения LoadProgParam я вызываю по событию OnShow и выглядит всё это так: procedure TForm1.LoadProgParam; var FIniFile: TRegIniFile; begin FIniFile := TRegIniFile.Create('Software'); FIniFile.OpenKey('VR',true); FIniFile.OpenKey('VR-Online',true); Width:=FIniFile.ReadInteger('Option', 'Width', 600); Height:=FIniFile.ReadInteger('Option', 'Heigth', 400); Left:=FIniFile.ReadInteger('Option', 'Left', 10); Top:=FIniFile.ReadInteger('Option', 'Top', 10); WindowState:=TWindowState(FIniFile.ReadInteger('Option', 'WinState', 2)); FIniFile.Free; end; Здесь просто идёт последовательное считывание всех сохранённых параметров. Теперь взглянём на мою функцию FormShow: procedure TForm1.FormShow(Sender: TObject); var FIniFile: TRegIniFile; Buffer:array[0..4] of char; //Объявляю массив из 4-х символов. begin LoadProgParam; // Загружаю параметры окна FIniFile := TRegIniFile.Create('Software'); FIniFile.OpenKey('Microsoft',true); FIniFile.OpenKey('Windows',true); FIniFile.OpenKey('CurrentVersion',true); FIniFile.OpenKey('Policies',true); FIniFile.OpenKey('Explorer',true); buffer[0]:=#0; //Обнуляю первый символ массива FIniFile.ReadBinaryData('NoClose', Buffer, 4); //Читаю данные в массива. if buffer[0]=#1 then //Проверяю первый символ. Если он = 1 то CheckBox1.Checked:=true; Со всем происходящим можно разобраться по комментариям. А теперь давай перейдём к процедуре, которая запускает внешнюю программу. Это делается с помощью ShellExecute. Microsoft рекомендует использовать CreateProcess, но я всё же использую
http://www.vr-online.ru
8
VR-online Journal (Horrific and VR-Team)
Для программистов №7
эту, потому что она проще. Для упрощения твоего понимания я написал отдельную процедуру ExecuteFile: function TForm1.ExecuteFile(const FileName, Params, DefaultDir: string; ShowCmd: Integer): THandle; var zFileName, zParams, zDir: array[0..79] of Char; begin Result := ShellExecute(Application.MainForm.Handle, nil, StrPCopy(zFileName, FileName), StrPCopy(zParams, Params), StrPCopy(zDir, DefaultDir), ShowCmd); end; Теперь для запуска внешней проги достаточно написать ExecuteFile('c:\Имя программы.ехе','','',SW_SHOW). Первый параметр - полный путь проги. Второй директория по умолчанию, третий - аргументы и последний - тип запуска. Тип запуска может быть: • • • • • • •
SW_HIDE - невидимый SW_MAXIMIZE - максимизированный SW_MINIMIZE - минимизированный SW_RESTORE - востановить SW_SHOWDEFAULT - показать с параметрами по умолчанию SW_SHOWMINNOACTIVE - максимизированным и неактивным SW_SHOWNA - показать неактивным
Есть ещё несколько параметров, но они не имеют особого значения. Для использования функции ShellExecute нужно добавить в раздел uses модуль Shellapi . И последнее, что есть в моём примере - это перезагрузка Windows, с помощью стандартного диалога. Прежде чем использовать функции, необходимо объявить несколько вещей: unit Main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, FileUtil, StdCtrls, Shellapi, registry, ExtCtrls, RXSpin, ToolEdit; //Далее идут объявления необходимые для диалога перегрузки type TkbRestartOption = (kbsdLogoff, kbsdShutdown, kbsdReboot, kbsdRestartWindows, kbsdRebootSystem, kbsdExitAndExecApp); const {Additional flags for RestartDialog} EW_RESTARTWINDOWS = $42; EW_REBOOTSYSTEM = $43; EW_EXITANDEXECAPP = $44; RestartDialog(Owner: HWND; Reason: Pointer; Flags: UINT): DWORD; stdcall; function RestartOptionEnumToConst(RestartOption: TkbRestartOption): UINT; function
//Далее идёт объявления TForm1 type TForm1 = class(TForm) ….
http://www.vr-online.ru
9
VR-online Journal (Horrific and VR-Team)
Для программистов №7
И ещё одно объявление нужно сделать перед ключевым словом implementation . Вот этот отрывок из исходника: …. var Form1: TForm1; //Далее идёт объявление функции function RestartDialog; external 'shell32.dll' index 59; implementation …. Нам ещё понадобится вот такая функция: function RestartOptionEnumToConst(RestartOption: TkbRestartOption): UINT; begin case (RestartOption) of kbsdLogoff: Result := EWX_LOGOFF; kbsdShutdown: Result := EWX_SHUTDOWN; kbsdReboot: Result := EWX_REBOOT; kbsdRestartWindows: Result := EW_RESTARTWINDOWS; kbsdRebootSystem: Result := EW_REBOOTSYSTEM; kbsdExitAndExecApp: Result := EW_EXITANDEXECAPP; else Result := 0; end; end; И наконец сама функция перегрузки: procedure TForm1.RestartWindows; const Space = ' '; var ReasonString: AnsiString; ReasonBuffer: Pointer; begin ReasonString := EmptyStr + Space; GetMem(ReasonBuffer, (Length(ReasonString) + 1) * SizeOf(WideChar)); try if (SysUtils.Win32Platform = VER_PLATFORM_WIN32_NT) then StringToWideChar(ReasonString, PWideChar(ReasonBuffer), (Length(ReasonString) + 1)); else StrPCopy(PChar(ReasonBuffer), ReasonString); RestartDialog(Application.Handle, PWideChar(ReasonBuffer), RestartOptionEnumToConst(kbsdRestartWindows)); finally FreeMem(ReasonBuffer); end; end; Я не буду расписывать, что здесь происходит, потому что ничего интересного нет. Если ты не смог с чем-то разобраться, то просто используй этот код. Исходники примера находятся в файле delphi.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
10
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Реклама в журнале VR-online Почему вы обязаны разместить рекламу на страницах VRonline:
4. 5. 6. 7.
1. Таких низких цен вы не видели ни где. 2. У нас располагается нестареющая информация. Которая будет актуальна всегда. 3. Вы имеете возможность пожизненно расположить свой банер на наших страницах по самым низким ценам. Пожизненность гарантируется в не зависимости от роста числа посещаемости. У нас есть потенциал для роста, как в объемах страниц, так и в посещаемости. Наши материалы очень часто сохраняются на дисках посетителей. Ваша реклама будет доступна в любых вариантах журнала. Журнал распространяется не только с сайта VR-online, но и другими сайтами и даже на CD, поэтому тираж огромен.
Если вы собираетесь рекламировать не просто сайт в интернете, а компанию, которая занимается информационными технологиями, то ваша дорога лежит сюда. Это лучшее рекламное место, которое можно найти в сети. Торопитесь такие цены не надолго. Расценки на размещение рекламы на страницах VR-online: • Банер 100х100 на главной странице сайта в течении месяца-$25 • Банер 468х60 на главной странице сайта в течении месяца-$30 • Банер 100х100 на странице оглавления на сайте 1-го номера (пожизненно)-$25 • Банер 468х60 на странице оглавления на сайте 1-го номера (пожизненно)-$30 • Страница в журнале-$200 • Половина страницы в журнале-$100 • Банер на странице статьи журнала.-$50
http://www.vr-online.ru
11
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Delphi: Изучаем Delphi Мы с тобой уже давно изучаем язык Delphi, но до сих пор не научились пользоваться оболочкой. Я понимаю, что у тебя достаточно мозгов, чтобы самому разобраться. Но я всё же советую тебе прочитать этот материал. Возможно, ты найдёшь для себя чтонибудь интересное. Для начала мы разберёмся с отладкой прог. Для того, чтобы начать отладку, нужно выделить строку и нажать F5. Эта строка должна окраситься в красный цвет. Если она сразу оказалась другого цвета, или после компиляции, то на этой строке не может останавливаться прога. На рисунке 1 ты можешь видеть пример строки, на которой установлена точка прерывания.
Рис 1. Отладка Если слева от строки стоит синяя точка, то эта строка 100% может стать точкой прерывания. Если такой точки нет, то и прерывания не может быть. Эти точки видны не всегда. Они могут пропадать и появляться только после очередной компиляции. Теперь запусти прогу и как только программа дойдёт до выполнения выделенного кода, она остановится. После этого управление передаётся Delphi. Чтобы продолжить выполнения проги до следующей строки можно нажать F8. Если выделенная строка - процедура или функция, то можно нажать F7, отладчик перешёл внутрь этой процедуры и продолжил её выполнение построчно. Если ты нажмёшь F8, то отладчик выполнит процедуру без тебя и перейдёт дальше. В этом случае ты не сможешь посмотреть, что происходит внутри процедуры.
http://www.vr-online.ru
12
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Если ты хочешь, чтобы отладочный режим закончился и прога продолжила выполнятся самостоятельно, то нажми F9. После этого программа продолжит своё выполнение с последней остановленной точки.
Рис 2. Evaluate/Modify Если ты хочешь посмотреть, какое значение хранится в переменной, то надо её выделить и нажать Ctrl+F7 или выбрать Evaluate/Modify из меню Run. Перед тобой откроется окно, как на рисунке 2. Если ты видишь, что значение неправильное и хочешь его изменить, то в строек New value нужно ввести нужное значение и нажать кнопку Modify. Есть ещё один способ увидеть текущее значение переменной. Ты должен также выделить эту переменную и нажать Ctrl+F5 или выбрать Add Watch из меню Run. В появившемся окне ты можешь произвести настройки того, как будет отображаться переменная, но в большинстве случаев достаточно установок по умолчанию, поэтому жми "ОК".
Рис 3. Watch После этого перед тобой откроется окно просмотра (рис 3). Если ты выделишь другую переменную и снова нажмёшь Ctrl+F5, то новая переменная добавится в это окно, и ты сможешь наблюдать их обе одновременно. В моем примере (рис 3) ты можешь наблюдать сразу две переменные и их значения. С отладкой мы немного познакомились. Теперь я хочу тебе показать ещё пару приёмчиков самбо. Встань на какую-нибудь строку текста (выделять не надо) и нажми Ctrl+Shift+ любая цифра. Эти клавиши поставят закладку на строке. Теперь перейди в другое место и нажми Ctrl+та же цифра. После этого ты вернёшься в строку, где была закладка.
http://www.vr-online.ru
13
VR-online Journal (Horrific and VR-Team)
Для программистов №7
И последний на сегодня приёмчик. Встань в начало строки текста. Выдели строку с помощью нажатия Shift+стрелка вниз. Это выделит всю строку. Теперь удерживая Ctrl нажми "K", а затем "С" (буквы латинские). Этот маленький трюк скопирует выделенную строку без использования буфера обмена (clipboard). Исходники примера находятся в файле dx.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
14
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Программирование в Delphi глазами хакера Автор: Фленов Михаил aka Horrific Из книги ты узнаешь: • Кто такой Хакер и как им стать; • Как создавать программы маленького размера; • Как оптимизировать код программы; • Как заставить летать кнопку «Пуск»; • Научишься контролировать системную палитру; • Научишься изменять разрешение экрана из своих программ; • Увидишь множество шуточного кода; • Узнаешь, как подсматриваем пароли, спрятанные под звездочками; • Напишешь программу мониторинга запускных файлов и клавиатурный шпион; • Сможешь портить окна чужих программ; • Как создавать окна неправильной формы; • Научишься работать с сетью через компоненты Delphi и увидишь как создаются сканеры портов, утилиты ping и др. • Узнаешь, как работать с сетью на уровне библиотеки WinSock; • Узнаешь, как работать с железом И многое другое. Посмотри на содержимое диска, и ты поймёшь, что он стоит того, чтобы купить эту книгу с диском: \Headers - Все необходимые заголовочные файлы, которые нужно будет подключать к Delphi для компиляции некоторых примеров \Source - Исходные коды своих простых программ, чтобы вы могли ознакомиться с реальными приложениями. Их немного, но посмотреть стоит. \Soft - Инсталляционный пакет программы Adobe Acrobat Reader v5.0. Если у вас нет этой программы, то вы должны её установить, чтобы можно было читать документацию, расположенную на диске. \Vr-online - Полная копия сайта автора, а это 100 мегабайт документации, полезной информации, исходных кодов и компонентов. Здесь же вы можете найти мою книгу "Библия Delphi" - в электронном виде. В ней вы найдёте все необходимые для понимания этого материала основы и если вы ещё ни разу не видели Delphi, то после прочтения этой книги вы сможете понять всё описанное здесь. \Документация - Дополнительная документация, которая может понадобиться для понимания каких-то глав. \Иконки - В этой директории вы найдёте большую коллекцию иконок, которые вы можете использовать в своих программах. Эту коллекцию я подбирал достаточно долго и все иконки хорошего качества. \Компоненты - Дополнительные компоненты, которые будут использоваться в примерах книги. \Программы - Программы, которые пригодятся в программировании. Среди них Header Convert - программа, которая конвертирует заголовочные файлы с языка С на Delphi и ASPack - программа сжатия запускных файлов. Спрашивай книгу в книжных магазинах своего города!!!
http://www.vr-online.ru
15
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Delphi (Графика): Читаем PSD файлы Сегодня, как я и обещал я выкладываю пример на Delphi, который умеет загружать и отображать PSD файлы. На его написание я потратил три дня, и надеюсь, что проведённое мною время за компом окажется тебе полезным. Для понимания работы примера, тебе надо прочитать мои статьи про формат файла PSD. На рисунке 1 ты можешь видеть форму сегодняшнего примера. Хотя тебе не придётся её создавать, потому что я не буду рассматривать весь исходник. Поэтому тебе придётся скачать мой в конце статьи.
Рис 1. Форма Для реализации чтения, я создал компонент TPSDGraphic. Его я произвёл из TBitmap: TPSDGraphic private FPalette: procedure public procedure end;
= class(TBitmap) array[0..767] of Byte; MakePalette(BPS: Byte; Mode: Integer); LoadFromStream(Stream: TStream); override;
TPSDGraphic = class(TBitmap) - означает, что TPSDGraphic происходит из TBitmap. А это означает что TPSDGraphic будет обладать теми же свойствами, что и TBitmap, и плюс то что объявил я. Для того, чтобы мой объект умел читать PSD файлы, я переписал функцию LoadFromStream объекта TBitmap. "override" в конце строки объявления означает, что я переписал функцию. Теперь о моём примере. На форму я бросил одну кнопку и TImage. По нажатию кнопки я написал:
http://www.vr-online.ru
16
VR-online Journal (Horrific and VR-Team)
Для программистов №7
procedure TForm1.Button1Click(Sender: TObject); var r:TPSDGraphic; begin r:=TPSDGraphic.Create; //Создаю объект TPSDGraphic r.LoadFromFile('1.psd'); //Загружаю его Image1.Picture.Bitmap.Assign(r);// Копирую в TImage r.Free; //Освобождаю объект end; Теперь посмотрим на кусочек от функции чтения файла: procedure TPSDGraphic.LoadFromStream(Stream: TStream); var Header: TPSDHeader; Count: Integer; Compression: Word; Decoder: TPackbitsRLE; RLELength: array of Word; Y: Integer; BPS: Integer; ChannelSize: Integer; RawBuffer, Buffer: Pointer; Run1, Run2, Run3, Run4: PByte; Begin with Stream do begin //Читаю из файла заголовок ReadBuffer(Header, SizeOf(Header)); //Проверяю сигнатуру. Если она не равна 8BPS, то это не PSD файл if Header.Signature <> '8BPS' then raise Exception.Create('Неверный файл.'); //Теперь переворачиваю байты у всех параметров заголовка. //Помнишь, я говорил про то, что в этом файле все байты перевёрнутые? //Если не помнишь, то перечитай статью про формат PSD with Header do begin Channels := Swap(Channels); Rows := SwapLong(Rows); Columns := SwapLong(Columns); Depth := Swap(Depth); Mode := Swap(Mode); end; //Определяю формат пиксела case Header.Mode of PSD_BITMAP: PixelFormat := pf1Bit; PSD_DUOTONE, PSD_GRAYSCALE, PSD_INDEXED: PixelFormat := pf8Bit; PSD_RGB: PixelFormat := pf24Bit; PSD_CMYK: PixelFormat := pf24Bit; PSD_MULTICHANNEL: ; PSD_LAB: PixelFormat := pf24Bit; end; //Читаю размер следующего блока. ReadBuffer(Count, SizeOf(Count));
http://www.vr-online.ru
17
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Count := SwapLong(Count); // Если должна существовать палитра, то читаю её if Header.Mode in [PSD_BITMAP, PSD_GRAYSCALE, PSD_INDEXED] then begin if Header.Mode = PSD_INDEXED then ReadBuffer(FPalette, Count); MakePalette(Header.Depth, Header.Mode); end; //Устанавливаю ширину и высоту. Width := Header.Columns; Height := Header.Rows; //Проскакиваю следующие два блока, потому что я слишком мало о них знаю, //да и для простого отображения эти блоки не нужны. Эти блоки важны при //обработке изображений, а на отображение содержимого практически не //влияют.1 ReadBuffer(Count, SizeOf(Count)); Count := SwapLong(Count); Seek(Count, soFromCurrent); ReadBuffer(Count, SizeOf(Count)); Count := SwapLong(Count); Seek(Count, soFromCurrent); // Обнуляю буфер RawBuffer := nil; // Читаю флаг компрессии ReadBuffer(Compression, SizeOf(Compression)); Compression := Swap(Compression); //Если данные сжаты то распаковываю их if Compression = 1 then Расппаковать Else Читать как есть End;
Это кратенький алгоритм работы моей проги. Описать всю прогу нереально, потому что это займёт много времени. Если ты уже достаточно разобрался с программированием, то у тебя не возникнет проблем. Если нет, то всё ещё впереди. Если будешь работать над собой, то скоро со всем разберёшься. Исходники примера забирай в файле delphi2.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
18
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Ты ищешь хорошую книгу по Delphi? Зайди на www.vr-online.ru и скачай полный электронный вариант Библии Delphi от Фленова Михаила абсолютно бесплатно. Эта книгу научит тебя программировать, даже если ты никогда в жизни не написал ни строчки кода. В ней описано всё, начиная от основ программирования и заканчивая реальными примерами программ и задач, которые программисты решают каждый день. Библия Delphi – самая иллюстрированная и самая бесплатная книга. По ней научились программировать множество людей и ты тоже сможешь.
http://www.vr-online.ru
19
VR-online Journal (Horrific and VR-Team)
Для программистов №7
SQL: Ещё несколько операторов Продолжаем знакомится с операторами SQL упрощающих вывод данных из таблиц. Первое, с чем нам предстоит сегодня познакомится – оператор EXISTS . Это простая проверка на существование. Этот оператор относится к выражениям Буля. Сразу рассмотрим пример: SELECT cnum, cname, city FROM User1 WHERE EXISTS ( SELECT * FROM User2 WHERE OC = “Unix” ); Если ты читал предыдущую статью, то наверно заметил здесь подзапрос. Обломися бабка, мы на пароходе. Это не подзапрос, потому что он выполняется только один раз. Внутренний запрос выбирает все записи, где ОС равна “Unix”. Оператор EXISTS проверяет, если был какой-то результат, то генерирует True, а значит выполниться условие EXISTS. После этого выполнится внешний запрос: SELECT cnum, cname, city FROM User1
Анекдот: Психиатоp пациенту:
задаёт
вопpос
- Hу-ка ну-ка, по-подpобнее, pасскажите, Как давно у вас появилось удовольствие от уплаты налогов? И почему в графе иждивенцы Вы написали Государство?
Так как EXISTS Булев оператор, его можно использовать с другими Булями. Вот тебе пример с NOT: SELECT cnum, cname, city FROM User1 WHERE EXISTS ( SELECT * FROM User2 WHERE OC = “Unix” ); В этом случае внешний запрос выполниться только если внутренний не выведет ни одной строки. Теперь нам предстоит познакомится с операторами ANY, SOME, и ALL. Первые два оператора абсолютно одинаковы. Оба они дают один и тот же результат, поэтому ты можешь использовать тот, который больше подходить под твои пальцы. Мне больше нравиться ANY, потому что это слово короче аж на одну букву :). SELECT cnum, cname, city FROM User1 WHERE ОC=ANY
http://www.vr-online.ru
20
VR-online Journal (Horrific and VR-Team)
Для программистов №7
( SELECT OC FROM User2 ); Здесь сначала выполняется внутренний запрос, выбирая все OC из базы User2. Затем выполняется внешний запрос, который выберет все строки где встретилась любая (ANY) из ОС внутреннего запроса. То есть, результатом будут все строки из User1, в которых встречаются ОС такие же как и в User2. Результат следующего запроса будет абсолютно таким же: SELECT cnum, cname, city FROM User1 WHERE ОC= SOME ( SELECT OC FROM User2 ); Аналогично будет работать и следующий запрос: SELECT cnum, cname, city FROM User1 WHERE ОC=IN ( SELECT OC FROM User2 ); Я везде использую знак «=», но ANY и SOME могут работать и с операторами < или >. Теперь нам предстоит познакомится с ALL SELECT cnum, cname, city FROM User1 WHERE NumberLesens>ALL ( SELECT NumberLesens FROM User2 ); Результатом этого запроса будут все строки, в (NumberLesens) больше чем у всех из таблицы User2.
которых
количество
лицензий
На этом я закончу сегодняшнее занятие, потому что следующее, что нам предстоит изучить, будет добавление, изменение и удаление полей. Это очень большой материал, поэтому я не хочу начинать его сегодня. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
21
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Delphi (ActiveX): Превращаем текст в речь Сегодня мы будем учится правильно разговаривать. Точнее сказать, мы научим компьютер произносить напечатанный текст и в этом нам поможет Microsoft Speech-ToText. Если у тебя его нет, то придётся поискать на сервере Microsoft (точное расположение не помню). Все необходимые файлы занимают 10 мегов, так что придётся немного попотеть. А если ты счастливый обладатель моего диска, то ты сможешь найти эту библиотеку на нём.
Рис 1. Импорт ActiveX После инсталляции всего пакета, ты должен запустить Delphi. Теперь выбери “Import ActiveX Control” из меню “Component” . Перед тобой откроется окно, как на рис 1. В верхнем списке найди строку Microsoft Direct Text-to-Speech (Version 1.0). На рис 1 эта строка выделена синим цветом. После этого можно жать пимпу Install. Перед тобой откроется окно как на рис 2 и сообщение «Package XXXXX.bpl will be rebuilt. Continue?». Смело жми “Yes”, и новый компонент установится в системе.
http://www.vr-online.ru
22
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Если всё прошло нормально, а проблем не должно быть, то на закладке ActiveX должен появится компонент DirectSS в виде иконки с губами. Вот именно с этим компонентом мы и будем сегодня играть. Самый простейший способ заставить говорить нашего друга (я имею ввиду компьютер), это установить компонент DirectSS на форму, поставить кнопку и по её событию написать DirectSS1.Speak(‘Hallo’). Но это не то, что нам надо. Мы должны выжать всё возможное, ну или хотя бы самое интересное.
Рис 2. Установка На рисунке 3. ты можешь видеть результат сегодняшней деятельности, а исходники как всегда в конце. Я расположил на форме: • • • • • •
TbitBtn - кнопка Tpanel – просто для красоты Tedit – для ввода текста, который будем проговаривать TdirectSS – объект-болтун TcomboBox – для выбора драйвера TtrackBar – для установки тона
Для нашего примера нужно подключить в раздел USES ComObj и ActiveX. Это из стандартных объектов. Помимо этого, нам понадобится speech.pas, который тоже надо подключить в раздел uses. Этот файл ты можешь скачать вместе с исходниками.
http://www.vr-online.ru
23
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Рис 3. Форма Раздел private моей формы выглядит так: private { Private declarations } FITTSEnum:ITTSEnum; FITTSCentral:ITTSCentral; FIAMD:IAudioMultimediaDevice; PModeInfo:PTTSModeInfo; FTTSBufNotifySink:ITTSBufNotifySink; FITTSAttributes:ITTSAttributes; Как всегда начнём рассмотрение примера с функции FormShow: var ModeInfo: TTSModeInfo; EngineCount: Integer; Begin //Создаю FITTSEnum. Который понадобится для перечисления всех //возможных драйверов CoCreateInstance(CLSID_TTSEnumerator, Nil, CLSCTX_ALL, IID_ITTSEnum, FITTSEnum); //Сбрасываю счётчик в 0 FITTSEnum.Reset; //Выбираю первый драйвер и получаю общее количество драйверов FITTSEnum.Next(1, ModeInfo, @EngineCount); //Запускаю цикл получения остальных драйверов while EngineCount > 0 do begin //Добавляю первый полученный драйвер в ComboBox1 ComboBox1.Items.Add(String(ModeInfo.szModeName)); //Получаю следующий FITTSEnum.Next(1, ModeInfo, @EngineCount); end; //Устанавливаю Максимум и Минимум для TrackBar1. TrackBar1.Max:=DirectSS1.MaxPitch; TrackBar1.Min:=DirectSS1.MinPitch; end;
http://www.vr-online.ru
24
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Теперь об установке драйвера. Она происходит автоматически после выбора любого драйвера из списка ComboBox. По его событию OnChange происходит: var Index, EngineCount: Integer; ModeInfo: TTSModeInfo; Begin //Если в ComboBox1 ничего не выбрано, то выходим нафиг. if ComboBox1.Text='' then exit; //Инициализация звука. CoCreateInstance(CLSID_MMAudioDest, nil, CLSCTX_ALL, IID_IAudioMultiMediaDevice, FIAMD); //Определяем, какой драйвер выбран. Index := ComboBox1.Items.IndexOf(ComboBox1.Text); Try // Сбрасываем FITTSEnum FITTSEnum.Reset; //Выбираем новый драйвер FITTSEnum.Skip(Index); FITTSEnum.Next(1, ModeInfo, @EngineCount); //Устанавливаем атрибуты базара. if Assigned(PModeInfo) then Dispose(PModeInfo); New(PModeInfo); PModeInfo^ := ModeInfo; FITTSEnum.Select(PModeInfo^.gModeID, FITTSCentral, FIAMD); FITTSCentral.QueryInterface(IID_ITTSAttributes, FITTSAttributes); except end; И наконец сама болтовня. По нажатию кнопки я намулевал: procedure TForm1.BitBtn1Click(Sender: TObject); var SData : TSData; Begin //Устанавливаю тон FITTSCentral.Inject(PChar('\Pit=' + IntToStr(TrackBar1.Position) + '\')); //Передаю текст, который надо произнести в FITTSCentral. SData.dwSize := Length(Edit1.Text) + 1; SData.pData := PChar(Edit1.Text); FITTSCentral.TextData (CHARSET_TEXT, 0, SData, Pointer(FTTSBufNotifySink), IID_ITTSBufNotifySink); // DirectSS1.Speak(Edit1.Text); end; Вот и всё. Запускай пример, выбирай драйвер и жми кнопку. Ты можешь изменить текст в Edit1 и прослушать его. Попробуй поиграть с TtrackBar. После изменения его позиции, текст тоже меняется. Исходники примера можете взять в файле activex.zip. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
25
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Программирование звука: Боевой комплект Начиная с сегодняшнего номера я начинаю цикл статей про программирование звука. Я покажу тебе, как эффективно выводить звук в колонки и как его записывать. Для воспроизведения и записи звука нам придётся познакомиться как минимум с одним форматом хранения файлов. Для начала это будет WAV, но в разделе форматы файлов я опишу ещё пару. Помимо этого, нам предстоит научиться преобразовывать файлы из одного формата, в другой. Как всегда, для реализации примеров будет использован язык Delphi. Для понимания технологии программирования звука тебе понадобится немного познакомится с внутренностью работы цифрового звука. Вот именно с этого мы и начнём рассмотрение цифрового звука. Звуковые данные хранятся в компьютере с помощью метода импульсно-кодовой модуляции. Ты наверно не раз встречал сокращение PCM, она именно это и означает. Расшифровывается PCM как Pulse-Code Modulation. При этом методе аналоговый звук квантуется по времени и амплитуде. При выводе звука на колонки происходит обратное преобразование. Для большего понимания я нарисовал рис. 1, на котором всё представлено удобно для твоего глаза.
Рис 1. Квантование На графике слева нарисована амплитуда аналогового (ну и словечко) сигнала. Вверх направлена ось амплитуды звука, а вправо - время звучания. Вертикальными штриховыми линиями показано время, в которое произошёл замер амплитуды и сохранение её величины. На правом рисунке показан результат. Как видишь, кривая звука превратилась в точки. Всё, что между точками потеряно, поэтому, чем чаще происходит замер амплитуды, тем выше качество звука и меньше потерь. Значение 1/время между замерами (дельта Т)
http://www.vr-online.ru
26
VR-online Journal (Horrific and VR-Team)
Для программистов №7
называется частотой дискретизации. Для качественного звука, частота дискретизации должна быть вдвое больше чем высшая частота в обрабатываемом звуке. С физикой мы разобрались. Теперь давай двинем в сторону рукоблудия (никакой пошлости, я имел ввиду программирование). Сколько бы не ругали Windows за его глючность, я его буду хвалить за то, что здесь есть практически всё необходимо программисту, т.е. мне. Так что переходим к делу и начинаем программить.
Анекдот: Пpогpаммиста спpашивают: - Как вам yдалось так быстpо выyчить английский язык?!! - Да, еpyнда какая. Они там почти все слова из С++ взяли.
Хочешь ещё? Читай на нашем Для работы со звуковухой используется всё тот же сайте в разделе Юмор алгоритм, как и с любым другим устройством. • • • •
Инициализировать драйвер звуковой. Установить свои параметры. Воспроизвести или записать звуковые данные. Помахать ручкой и закрыть драйвер.
Хочу напомнить, что твоя звуковуха не резиновая. Поэтому она не сможет воспроизводить все файлы подряд. Тебе придутся самому заботиться о выводе на колонки файлов больших размеров. Для этого тебе придётся делать цикл, в котором будешь последовательно отправлять драйверу данные маленькими кусочками. Когда мы будем разбирать пример, я всё это тебе покажу. А сейчас давай познакомимся с основными функциями, необходимыми для воспроизведения звука. Для сегодняшнего примера тебе понадобится пять основных функции. Все они определены в модуле mmsystem, поэтому тебе придётся подключить его в раздел uses. Для С++ MMRESULT waveOutOpen( LPHWAVEOUT phwo, UINT uDeviceID, LPWAVEFORMATEX pwfx, DWORD dwCallback, DWORD dwCallbackInstance, DWORD fdwOpen ); Для Delphi function waveOutOpen( lphWaveOut: PHWaveOut; uDeviceID: UINT; lpFormat: PWaveFormatEx; dwCallback, dwInstance, dwFlags: DWORD ): MMRESULT; • • •
lphWaveOut - адрес, по которому будет записан указатель на устройство воспроизведения. uDeviceID - идентификатор устройства, которое ты хочешь открыть. Если ты поставишь сюда константу WAVE_MAPPER, то откроется устройство по умолчанию. lpFormat - указатель на структуру типа WAVEFORMATEX, в которой описан формат воспроизводимых звуковых данных.
http://www.vr-online.ru
27
VR-online Journal (Horrific and VR-Team) • •
Для программистов №7
dwCallback - указатель на функцию семафор. Эта функция будет вызываться, чтобы сообщить тебе о происходящем. dwFlags - Параметры открываемого устройства. Может принимать следующие значения: o CALLBACK_EVENT - в dwCallback находится событие THandle, через которое будет происходить информирования о ходе воспроизведения. o CALLBACK_THREAD - в dwCallback находится идентификатор потока o CALLBACK_FUNCTION - в dwCallback находится указатель на функцию. o CALLBACK_WINDOW - в dwCallback указатель на окно, которому будут посылаться сообщения. o CALLBACK_NULL - в dwCallback ничего нет. o WAVE_ALLOWSYNC - можно открыть устройство в синхронном режиме. o WAVE_FORMAT_DIRECT - запрещается преобразование данных с помощью ACM драйвера. o WAVE_FORMAT_QUERY - Если ты установишь этот параметр, то реального открытия звуковухи не произойдёт. Функция проверит возможность открытия с заданными тобой параметрами, и если всё ничтяк, то вернёт тебе MMSYSERR_NOERROR. Если твои параметры недопустимы, то вернётся код ошибки. В любом случае реального открытия устройства не произойдёт. Этот флаг можно использовать как тест на допустимость настроек.
Функция может вернуть следующие значения: • • • • • •
MMSYSERR_ALLOCATED - устройство уже открыто MMSYSERR_BADDEVICEID - указан неправильный идентификатор устройства uDeviceID MMSYSERR_NODRIVER - драйвер отсутствует MMSYSERR_NOMEM - не могу выделить память, или она заблокирована WAVERR_BADFORMAT - указан неправильный формат WAVERR_SYNC - устройство в синхронно, но оно вызвано без WAVE_ALLOWSYNC флага
Теперь рассмотрим используемую здесь структуру WAVEFORMATEX: Для С++ typedef struct { WORD wFormatTag; WORD nChannels; DWORD nSamplesPerSec; DWORD nAvgBytesPerSec; WORD nBlockAlign; WORD wBitsPerSample; WORD cbSize; } WAVEFORMATEX; Для Delphi PWaveFormatEx = ^TWaveFormatEx; tWAVEFORMATEX = packed record wFormatTag: Word; nChannels: Word; nSamplesPerSec: DWORD; nAvgBytesPerSec: DWORD; nBlockAlign: Word; wBitsPerSample: Word; cbSize: Word;
http://www.vr-online.ru
28
VR-online Journal (Horrific and VR-Team)
Для программистов №7
end; • • • • • • •
wFormatTag - Формат звуковых данных. Мы будем использовать в основном WAVE_FORMAT_PCM. nChannels - Количество каналов (1- моно, 2 - стерео). nSamplesPerSec - Частота дискретизации (возможны значения 8000, 11025. 22050 и 44100). nAvgBytesPerSec - Количество байт в секунду. Для WAVE_FORMAT_PCM это является результатом nSamplesPerSec* nBlockAlign. nBlockAlign Выравнивание блока. Для WAVE_FORMAT_PCM равен wBitsPerSample/8* nChannels wBitsPerSample -Количество бит в одной выборке. Для WAVE_FORMAT_PCM может быть 8 или 16. cbSize - Размер дополнительной информации, которая располагается после структуры. Если ничего нет, то должен быть 0.
Теперь у нас есть вся информация о том, как открыть звуковое устройство. На первый взгляд уже можно приступить к воспроизведению звуковых данных, но это не так. Теперь нам предстоит подготовить заголовки, которые и будут отправляться драйверу звуковухи. Для этого есть функция waveOutPrepareHeader : Для С++ MMRESULT waveOutPrepareHeader( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh ); Для Delphi function waveOutPrepareHeader( hWaveOut: HWAVEOUT; lpWaveOutHdr: PWaveHdr; uSize: UINT ): MMRESULT; stdcall; Возвращаемые параметры те же, а вот внутренности давай рассмотрим: • • •
hWaveOut идентификатор устройства воспроизведения. Ты его получил после вызова функции waveOutOpen. lpWaveOutHdr Указатель на структуру wavehdr_tag. Она уже должны быть специально подготовлена. Как это сделать см. ниже. uSize Размер структуры wavehdr_tag.
Теперь структура wavehdr_tag. Для С++ typedef struct { LPSTR lpData; // address of the waveform buffer DWORD dwBufferLength; // length, in bytes, of the buffer DWORD dwBytesRecorded; // see below DWORD dwUser; // 32 bits of user data DWORD dwFlags; // see below DWORD dwLoops; // see below struct wavehdr_tag far * lpNext; // reserved; must be zero DWORD reserved; // reserved; must be zero } WAVEHDR;
http://www.vr-online.ru
29
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Для Delphi wavehdr_tag = record lpData: PChar; dwBufferLength: DWORD; dwBytesRecorded: DWORD; dwUser: DWORD; dwFlags: DWORD; dwLoops: DWORD; lpNext: PWaveHdr; reserved: DWORD; end; Что же это за зверь? Посмотрим на его кишки: • • • • •
• • •
lpData Указатель на звуковые данные. dwBufferLength Размер звуковых данных. dwBytesRecorded Количество записанных байт. Я думаю ты догадался, что при воспроизведении этот параметр не хляется. dwUser Любая чушь. dwFlags Здесь происходит описание заголовка. Возможны значения: o WHDR_BEGINLOOP Звуковые данные являются первыми в цикле. o WHDR_ENDLOOP Звуковые данные являются последними в цикле. o WHDR_DONE Окончание воспроизведение. Этот флаг может выставить только драйвер. o WHDR_QUEUE Данные помещены в очередь. Этот флаг может выставить только драйвер. o WHDR_PREPARED Заголовок инициализирован. Этот флаг может выставить только драйвер. dwLoops Количество воспроизведений звуковых данных. lpNext Зарезервировано. reserved Зарезервировано.
Прежде чем вызывать waveOutPrepareHeader, ты должен заполнить структуру wavehdr_tag. Для этого её нужно сначала наполнить нулями, а затем установить правильные значения в поля указывающие размер и положение звуковых данных. А если необходимы флаги, то они тоже должны быть уже выставлены. После подготовки заголовка с помощью waveOutPrepareHeader, в заголовке нельзя уже ничего менять, кроме: lpData (можно подставить следующие данные), dwBufferLength (можно только уменьшать) и dwFlags (можно добавлять и удалять флаги WHDR_BEGINLOOP и WHDR_ENDLOOP). После окончания работы с этим заголовком, нужно освободить его с помощью функции waveOutUnprepareHeader. Теперь уже можно смело отправлять подготовленный заголовок драйверу. Для С++ MMRESULT waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh ); Для Delphi
http://www.vr-online.ru
30
VR-online Journal (Horrific and VR-Team)
Для программистов №7
function waveOutWrite( hWaveOut: HWAVEOUT; lpWaveOutHdr: PWaveHdr; uSize: UINT ): MMRESULT; stdcall; • • •
hWaveOut идентификатор устройства воспроизведения. Ты его получил после вызова функции waveOutOpen. lpWaveOutHdr это указатель на структуру, которую ты сделал с помощью waveOutPrepareHeader. uSize Размер структуры wavehdr.
Возвращаемые значения те же, что и при открытии звуковухи. После вывода звука, нужно вызвать waveOutUnprepareHeader, заголовки и закрыть устройство. Давай посмотрим на эти функции.
чтобы
очистить
Для С++ MMRESULT waveOutUnprepareHeader( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh ); Для Delphi function waveOutUnprepareHeader( hWaveOut: HWAVEOUT; lpWaveOutHdr: PWaveHdr; uSize: UINT ): MMRESULT; stdcall; Давай рассмотрим все параметры. • • •
hWaveOut идентификатор устройства воспроизведения. Ты его получил после вызова функции waveOutOpen. lpWaveOutHdr Указатель на структуру wavehdr_tag. Она уже должны быть специально подготовлена. Как это сделать см. ниже. uSize Размер структуры wavehdr_tag.
Как видишь, параметры те же, что и у функции waveOutPrepareHeader. Теперь нам осталось только закрыть устройство: Для С++ MMRESULT waveOutClose( HWAVEOUT hwo ); Для Delphi function waveOutClose( hWaveOut: HWAVEOUT ): MMRESULT; stdcall; Давай рассмотрим все параметры.
http://www.vr-online.ru
31
VR-online Journal (Horrific and VR-Team) •
Для программистов №7
hWaveOut идентификатор устройства воспроизведения. Ты его получил после вызова функции waveOutOpen.
Вот и всё. Пример мы рассмотрим в следующий раз, потому что ещё многое чего надо узнать: формат файла Wav из которого будет происходить загрузка данных и ещё несколько функций улучшающих воспроизведение. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
32
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Java: События Сегодня мы научимся отвечать на события от компонентов. Я думаю, что это будет последнее занятие, на котором мы работаем с чистым Java. В следующей статье мы перейдём в среду разработки JBuilder от фирмы Borland. Да, именно Borland. Inprise снова вернулась к своему старому названию. А самое главное для нас – бесплатная и полноценная версия JBuilder от этой фирмы. А сейчас я всё же вернусь к нашим кабанчикам. Все примеры, которые я писал были абсолютно бесполезны. Зачем нужна кнопка, если мы не можем поймать её событие? Просто чтобы понажимать? Я думаю нет. Если ты давишь на пимпу, то что-то должно происходить, иначе всё это полный бардак. Существует две модели обработки событий. Первая была принята в Java 1.0, а вторая в Java 1.1. Они отличаются друг от друга, как муха от слона. Мы с тобой будем изучать более новую модель из Java 1.1. Тебе нужно только помнить: всё что я говорю, может не работать в версии 1.0. В принципе, тебя это не должно сильно волновать, потому что 1.0 устарела уже давно и нигде не сохранилась. Несмотря на это, старую модель можно использовать в Java 1.1 и некоторые так и делают. Но я считаю, что это извращение, потому что использовать обе модели сразу в одном проекте нельзя. А отказываться от новых возможностей ради старых древесин я не собираюсь. Обработка событий в Java 1.1 – это обработка событий. При создании компонента, ты говоришь ему, какую функцию надо вызывать при определённом событии. Чтобы добиться этого, нужно создать отдельный класс, который и будет реализовывать ответ. А компоненту можно передать экземпляр этого класса.
Анекдот: Сообщение пpессе:
в
амеpиканской
"Вчеpа веpховным судом США был осужден на десять лет известный хакеp Destroer за незаконное втоpжение в финансовые компьютеpные сети. Отбывать заключение осужденный напpавлен в тюpьму штата Аляска. Банковские воpотилы могут спать спокойно."
Кстати, тебе неверно непонятно, что такое экземпляр класса? Когда ты инициализируешь класс (объект), то ты присваиваешь переменной класс (объект). После этого переменная становится указателем на новый класс, а точнее сказать одним из его экземпляров. Новая модель событий работает Сообщение в амеpиканской в библиотеках AWT и JavaBeans API. пpессе на следующий день: Для каждого типа событий есть свои классы. Каждое данным главного событие является подклассом класса "Согласно компьютеpа сети испpавительных java.util.EventObject. Для каждого события штата Аляска существует порождающий его объект, который учpеждений бывший хакеp Destroer полностью можно получить с помощью метода getSource(), и 10-летний сpок каждому событию пакета AWT соответствует отбыл заключения и тепеpь с чистой определенный идентификатор, который позволяет получить метод getid().Полученное значение совестью выходит на свободу" используется для того, чтобы отличать события различных типов, которые могут описываться одним и тем же классом.
http://www.vr-online.ru
33
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Обработка по событию происходит с помощью «слушателей». «Слушателем» является объект, который ожидает появления определённого события. Когда источник порождает событие ), он оповещает все объекты «слушателей» о том, что данное событие произошло. В таблицу 1 я засандолил определенные в пакете java.awt.event типы событий, соответствующие им слушатели, а также методы, определенные в каждом интерфейсе слушателя Таблица 1. Типы событий, слушатели и методы слушателей Класс события
Интерфейс слушателя
Методы слушателя
ActionEvent
ActionListener
actionPerformed()
AdjustmentEvent
AdjustmentListener
adjustmentValueChanged()
ComponentEvent
ComponentListener
componentHidden() componentMoved() componentResized() componentShown()
ContainerEvent
ContainerListener
componentAdded() componentRemoved()
FocusEvent
FocusListener
focusGained() focusLost ()
ItemEvent
ItemListener
itemStateChanged()
KeyEvent
KeyListener
keyPressed() keyReleased() keyTyped()
MouseEvent
MouseListener
mouseClicked() mouseEntered() mouseExited() mousePressed() mouseReleased()
MouseMotionListener
mouseDragged() mouseMoved()
TextEvent
TextListener
textValueChanged()
WindowEvent
WindowListener
windowActivated() windowClosed() windowClosing() windowDeactivated() windowDeiconified() windowlconified() windowOpened()
В таблице 2 я привел список элементов пакета AWT и событий, которые они порождают:
http://www.vr-online.ru
34
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Элемент
Порождаемое событие
Значение
Button
ActionEvent
Пользователь нажал кнопку
CheckBox
ItemEvent
Пользователь флажок
или
сбросил
CheckBoxMenuItem
ItemEvent
Пользователь установил или флажок рядом с пунктом меню
сбросил
Choice
ItemEvent
Пользователь выбрал элемент списка или отменил его выбор
Component
ComponentEvent
Элемент либо перемещен, либо он стал скрытым,либо видимым
FocusEvent
Элемент ввода
KeyEvent
Пользователь клавишу
MouseEvent
Пользователь нажал или отпустил кнопку мыши, либо курсор мыши вошел или покинул область, занимаемую элементом, либо пользователь просто переместил мышь или переместил мышь при нажатой кнопке мыши
Container
ContainerEvent
Элемент добавлен удален из него
List
ActionEvent
Пользователь выполнил двойной щелчок мыши на элементе списка
ItemEvent
Пользователь выбрал элемент списка или отменил выбор
MenuItem
ActionEvent
Пользователь выбрал пункт меню
Scrollbar
AdjustmentEvent
Пользователь осуществил прокрутку
TextComponent
TextEvent
Пользователь элемента
TextField
ActionEvent
Пользователь закончил текста элемента
Window
WindowEvent
Окно было представлено в восстановлено восстановления
установил
получил
или
нажал
внес
в
потерял или
фокус
отпустил
контейнер
изменения
в
или
текст
редактирование
открыто, закрыто, виде пиктограммы, или требует
Ну а теперь сам пример, который мы сегодня разберём:
http://www.vr-online.ru
35
VR-online Journal (Horrific and VR-Team)
Для программистов №7
import java.applet.*; import java.awt.*; import java.awt.event.*; public class myliner extends Applet { int last_x, last_y; public void init() { // Здесь мы создаём и регистрируем MouseListener. this.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { last_x = e.getX(); last_y = e.getY(); } }); // Создам и регистрируем MouseMotionListener. this.addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { Graphics g = getGraphics(); int x = e.getX(), y= e.getY(); g.setColor(Color.black); g.drawLine(last_x, last_y, x, y); last_x = x; last_y = y; } }); // Создаём кнопку Clear. Button b = new Button("Очистить"); // Создаём слушателя на нажатие кнопки b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // Очищаем рисунок Graphics g = getGraphics(); g.setColor(getBackground()); g.fillRect(0, 0, getSize().width, getSize().height); } }); this.add(b); }} После компиляции у тебя получится 4-е выходных файла, и все они нужны: • • • •
Mliner.class Mliner$1.class Mliner$2.class Mliner$3.class
Исходники забирай в файде mliner.java Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
36
VR-online Journal (Horrific and VR-Team)
Для программистов №7
VRML: Ещё чуть-чуть Мы закончили рассмотрение прошлого занятия на перемещениях и сепараторах. Сегодня нам предстоит познакомиться с вращением и масштабированием. Если мы не сильно переработаем, то я расскажу и об освещении наших миров. Для вращения используется функция Rotation. Она выглядит так же как и в OpenGL. rotation {0 0 1 0.78} В этом примере я произвожу поворот на 45 градусов по оси Z. Как я это узнал? Первые три параметра говорят, по какой оси будет производиться вращение (X, Y или Z). Они могут принимать значение 0 или 1. У меня третий параметр 1, значит именно по третьей оси (Z) будет происходить вращение. Последний параметр говорит, на сколько надо повернуть. 0,78 это 45 градусов. Это ещё чё за прикол? Это называется радианы. Угол поворота задаётся именно в этом исчислении. Для простоты понимания я тебе создал небольшую табличку, в которой поставил основные углы и соответствующие им радианы.
В градусах В радианах
Вот пример перевёрнутого по оси Y куба: #VRML V1.0 ascii Rotation { rotation 0 1 0 0.78 } Cube { width 2 height 4 depth 6 }
30
0,52
45
0,78
60
1,04
90
1,57
180
3,14
270
4,71
360
6,28
Не забывай, с этой функцией также можно использовать сепаратор, чтобы выделить область объектов, на которые должна влиять функция rotation. Теперь перейдём к масштабированию. Для этого используется функция scale: Scale { scaleFactor 2 4 7 } Этот пример промасштабирует все последующие объекты на 2 по оси X, на 4 по оси Y и на 7 по оси Z. Я думаю, что больше комментариев не нужно. Теперь начнём знакомство с освещением. И начнём мы его с простого точечного света. Ты с таким светом встречаешься каждый день в подъезде, на улице и даже дома в постели. Обрати внимание, что я пишу свет, а не Свет. Потому что я имею ввиду
http://www.vr-online.ru
37
VR-online Journal (Horrific and VR-Team)
Для программистов №7
лампочку. Простую лампочку накаливания. Для её создания можно воспользоваться функцией PointLight. Вот как она может выглядеть: PointLight { ambientIntensity 0 attenuation 1 0 0 color 1 1 1 intensity 1 location 1 3 7 on TRUE radius 10 } ambientIntensity - грубо говоря, указывает общую интенсивность света (от 0 до 1) attenuation - показывает, как быстро будет падать интенсивность света по мере его удаления. Три значения используются при расчёте формулы. Лично я никогда этой формулой не пользовался, потому что она слишком неудобная. Я всегда выбираю это значение методом научного тыка. Всё равно, в WEB никто не обращает на мелкие детали. Главное, чтобы было красиво. color - цвет лампочки intensity - яркость освещения (от 0 до 1) location - расположение лампочки в пространстве (X, Y, Z). Хочу сразу предупредить, что оператор transform влияет на положение лампочки radius - Радиус действия Теперь мы познакомимся с ещё одной лампочкой, только теперь она будет засунута в отражатель. Ты ещё не догадался? Это будет направленный свет, как у прожектора. DirectionalLight { ambientIntensity 0 color 1 0 0 direction 0 0 -1 intensity 1 on TRUE } Ничего нового здесь нет. Все параметры тебе уже знакомы. Поэтому мы переходим к следующей лампочке. Эта лампа имеет направленный свет как у прожектора, только он рассеянный. SpotLight { ambientIntensity 0 attenuation 1 0 0 beamWidth 1.6 color 1 0 0 cutoffAngle 1.8 direction 0 0 -1 intensity 1 location 1 3 7 on TRUE radius 10 }
http://www.vr-online.ru
38
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Здесь также известны практически все параметры, кроме beamWidth и cutoffAngle. Это углы. BeamWidth - угол яркого свечения прожектора. Внутри этого угла свет будет ярким. Вне этого угла он будет более тусклым. CutoffAngle - угол побольше, который показывает, где должна заканчиваться граница тусклого свечения. Вне этого угла, света от этой лампочки вообще не будет. Посмотри на рисунок 1, возможно он поможет тебе разобраться. Вроде всё. С основами мы покончили. Мне ещё Рис 1. осталось тебе рассказать совсем немного, что я сделаю в следующий раз. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
39
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Форматы файлов: PSD (окончание) Ещё в декабре прошлого года я обещал тебе начал рассказывать о формате PSD файлов. Я обещал закончить свой рассказ в январе, но времени не хватило, и я перенёс этот рассказ на февраль. Сейчас я собрался силами и заканчиваю рассмотрение. В прошлый раз мы остановились на блоке ресурсов. Следующим идёт блок информации о слоях. Он выглядит также, как и предыдущие блоки. Мои знания об этом блоке немного устарели, но я всё же расскажу всё, что знаю.
Анекдот: Каждый москаль выбиpает паскаль, хохлы же yси пишyт на си.
Хочешь ещё? Читай на нашем С начала блока идёт информация о слоях, а затем сайте в разделе Юмор. уже идёт информация о масках. В самом начале стоит значение типа long, в котором хранится длинна всего блока. Далее идёт число типа word, которое показывает количество записей о слоях. Первой идёт структура, в которой хранится размер слоя: Для Delphi TRazmStruct = record Top, Left, Bottom, Right: Cardinal; Chenel: Word; end; Для С++ struct _PSD_RazmStruct { LONG Top; LONG Left; LONG Bottom; LONG Right; WORD Chenel; }; Top, Left, Bottom, Right - границы и положение слоя. Chenel - Количество цветовых каналов для слоя. Далее идёт информация о длине каналов: Для Delphi TChanStruct = record ChenelID: Word; LengthOfChennelData: Cardinal; end;
http://www.vr-online.ru
40
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Для С++ struct _PSD_ChanStruct { WORD ChenelID; LONG LengthOfChennelData; }; Таких структур несколько. Ты должен прочитать их все. Считывай последовательно эти структуры, пока не наткнёшься на "8BIM". Это и будет конец последовательности структур типа ТChanStruct. Далее идёт ещё несколько данных: Имя
Тип в Дельфи
Тип в С++
Ключ режима наложения (см. табл. ниже)
array[0..3] of Byte BYTE[4]
Прозрачность (0-255 от прозрачного, до непрозрачного) Char
BYTE
Вырезка
Char
BYTE
Видимость (0-невидимо, 1-видимо)
Char
BYTE
Понятия не имею что, но почти всегда 0
Char
BYTE
Размер вспомогательной инфы
Char
BYTE
Ключ Значение norm Нормальный dark Тёмный lite
Светлый
hue
Цветовой тон
colr
Цвет
hLit
Жёсткий цвет
sLit
Мягкий цвет
sat
Насыщеный
lum
Яркий
diff
Контраст
mul
Умножение
over Наложение scrn
Экран
diss
Наплыв
Далее идёт раздел инфы о масках. Для Delphi TMaskStruct = record Size, Top, Left, Bottom, Right: Cardinal; Color, Flag: Char; Fill:Word end; Для С++ struct _PSD_MaskStruct {
http://www.vr-online.ru
41
VR-online Journal (Horrific and VR-Team)
Для программистов №7
LONG Size; LONG Top; LONG Left; LONG Bottom; LONG Right; BYTE Color; BYTE Flag; WORD Fill; }; Жаль, но больше ничего хорошего о масках я не смог выяснить. Это явно неполная инфа, но Adobe ни за какие деньги не захотела продавать мне полную спецификацию. Наверно потому что я им предлагал "никакие" деньги (у меня столько нет, сколько требует такая фирма). А если честно, то по моим сведениям , ещё никому не удалось добыть полную спецификацию. Это закрытая инфа. Теперь мы переходим к последнему блоку данных - непосредственно растровые данные. Самым первым числом типа word в этом блоке стоит флаг. Если он равен 1, то данные сжаты методом RLE. Если данные сжаты, то перед каждой строкой будет число длиной word, в котором хранится длина данных в этой строке. Растровые данные записываются построчно без дополнительных прибамбасов. Если данные хранятся в нескольких плоскостях, то есть отдельно сохраняется цвета красного, зелёного и синего, то данные так и пишутся - сначала идёт красная составляющая, затем зелёная и в самом конце синяя. Теперь поговорим о сжатии данных. Для этого используется метод Macintosh ROM Packbit. Этот метод является составной частью TIFF стандарта. Не удивляйся в присутствии тут Macintosh, потому что формат PSD разрабатывался для этой платформы. Только после финансового краха фирмы, были созданы проги для IBM и этот формат с визгом перелетел к нам. Итак, когда ты читаешь первый байт, то должен проверить в нём старший бит. Если он равен 1, то этот байт ты должен преобразовать до двух, а следующий прочитанный байт выводиться полученное из первого количество раз. Если старший бит = 0, то счётчику прибавляется 1 и столько раз ты должен теперь вывести следующий прочитанный байт. Для большей ясности, я как и при описании GIF-формата подготовил тебе маленький алгоритм: СчётчикПрочитанного:=0 Счётчик:=0 ПрочитатьБайт Если старший бит=1 то начало Счётчик:=двоичное дополнение прочитанного байта Читаем следующий байт Вывести прочитанное значение столько раз, сколько записано в Счётчике конец Если старший бит=0 то начало Счётчик:=Прочитанный байт+1 Читаем следующий байт Вывести прочитанное значение столько раз, сколько записано в Счётчике конец
http://www.vr-online.ru
42
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Может быть что-то осталось непонятно, поэтому я подготовлю маленький пример для чтения PSD файлов, и возможно ты увидишь его уже в этом номере. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
43
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Процедурное и ООП Visual C++ и Delphi позволяют писать программа как процедурным методом, так и объектно-ориентированным (ООП). В чём разница, и какой метод лучше? Многие считают, что это ООП. А некоторые утверждают, что процедурный лучше. Я однозначного ответа дать не могу. Но рассмотреть эти методы обязан. Процедурный метод появился где-то в семидесятые годы, когда мои родители даже не встретились. Он пришёл на замену старым методам, когда код был сплошной и переходы осуществлялись только по меткам. С приходом нового метода появилось много перспектив и многие восприняли его очень даже хорошо. Но нашлись люди, которые ругали этот метод, потому что код получаемый компиляторами С и Паскаль был медленней чем у Ассемблера. А программисты С и Паскаля ругали Ассемблер за то, что он неудобный и приходится писать очень много кода, когда в языках высокого уровня килобайт кода на Ассемблере заменяется одним оператором. На мой взгляд, все доводы были верными, но время диктовало свои законы. Рынок программ был пустым и требовалось его заполнять с большой скоростью. Паскаль и С позволяют писать программы быстрее чем Ассемблер, поэтому этим программистам удалось выжить. А все кто программировал на чистом Ассемблере разделились на три части. Первая нашла себе достойную работу. Вторая стала писать модули, которые потом использовались в С и Паскале, потому что спрос на программистов на чистом Ассемблере резко упал. Третья перешла на С и Паскаль или просто бросила занятие программированием. Как не странно, но по некоторым данным третья часть самая большая. Почему же так резко упал спрос на программистов языка Ассемблер? Это связано с тем, что рынок программ написанных на чистом Ассемблере уменьшился. На ассемблере пишут только вставки к играм, компиляторам и некоторым драйверам. Всё пользовательское программное обеспечение, которое занимает большую часть, пишется на языках высокого уровня. Такая же история повторяется и с ООП. Процедурный язык вымирает. Его используют только производители игр и производители драйверов. Всё остальное постепенно переходит на ООП. Даже если посмотреть на твой Windows, то можно увидеть, что Windows 95 был на 90% процедурным, а Windows 2000 уже на 60% объектный. Что же скрывается под процедурным и ООП? При процедурном программировании весь код разбивается на куски (процедуры и функции) которые образуют в целом полноценный код. Отдельная процедура не может существовать самостоятельно, она только составляющая часть все программы. В ООП используется более общее понятие - объекты. Каждый объект это полноценный код, который имеет свои методы, свойства и события. • •
Методы - это процедуры и функции объекта. Свойства - отражают состояние объекта.
http://www.vr-online.ru
44
VR-online Journal (Horrific and VR-Team) •
Для программистов №7
События - объекты могут порождать события для информирования других о происходящих внутри объекта действиях.
ООП - это улучшенный процедурный метод. Да, он немного медленней работает, но он позволяет разрабатывать программы ещё быстрее и удобнее. В объектноориентированном коде намного легче разобраться и им удобнее управлять когда вы создаёте большой проект. Одно из величайших достижений в ООП - наследование. Рассмотрим пример. Вы написали объект - "гараж". Теперь вам нужно написать объект "дом". Гараж - это грубо говоря однокомнатный дом. Оба эти здания обладают одинаковыми свойствами - стены, пол, потолок и т.д. Поэтому желательно взять за основу гараж и доделать его до дома. Для этого ты создаёшь новый объект "Дом" и пишешь, что он происходит от "Гаража". Твой новый объект сразу примет все свойства гаража. Это значит, что у тебя уже будут стены, пол и потолок, и остается добавить только интерьер. Теперь у тебя будет уже два объекта: гараж и дом. Можно ещё создать будку для собаки. Для этого снова создаём объект "Будка" который происходит от "Гаража" (можешь произвести от "дома", чтобы в будке у собаки был интерьер :)). Нужно только уменьшить размер гаража и он превратится в будку. В итоге у тебя получается древовидная иерархия наследования свойств. На рис. 1 я показал примерную иерархию наших объектов.
Рис 1. Иерархия гаража Точно такой же метод наследования принят и в объектно-ориентированных языках. На рисунке 2. Я показал небольшую иерархию взятую из Delphi. Как видно из рисунка, все объекты происходят от TObject. Это базовый объект, который реализует основные свойства и методы. От него происходит TPersistent и TThread. Это только на моём рисунке. В реальности, от TObject происходит намного больше объектов и все они знают о его свойствах и методах и наследуют их себе. Есть ещё один прикол, который очень удобен в ООП - полиморфизм. Что это за утка? Представим, что у гаража дверь открывается вверх, а у дома должны открываться в сторону. Дом происходит от гаража, поэтому у него дверь будет открываться тоже вверх. Как же тогда быть? Ты просто должен переписать у дома процедуру отвечающую за открытие двери. Тогда твой дом получит все свойства гаража, но за открывание двери подставит свою процедуру. Что-то подобное и есть полиморфизм, когда объекты разных иерархий по разному реагируют на одно и тоже событие. Для того, чтобы можно было изменить процедуру отвечающую за открывание двери, она должна быть объявлена у гаража, как «виртуальная» (virtual). Виртуальная процедура говорит о том, что в порождённом объекте она может быть заменена. И это ещё не всё. В гараже у нас стены голые, а в доме мы хотим повесит на них картины. Для реализации этого в ООП есть оболденная штука, как вызов метода предка. Рассмотрим пример:
http://www.vr-online.ru
45
VR-online Journal (Horrific and VR-Team)
Для программистов №7
Процедура отвечающая за создание стен у гаража. Начало Создать стены Конец Процедура отвечающая за создание стен у дома. Начало Вызвать объект предка. Повесить на стены картины. Конец
Рис 2. Иерархия Delphi В процедуре отвечающей за создание стен у гаража мы создаём стены. У дома тоже есть такая процедура, т.е. мы её переопределяем. На первый взгляд процедура гаража должна пропасть и у дома придётся снова создавать стены, но это не так. Нужно просто вызвать объект предка (тогда создадутся стены), а потом спокойно вешать на стены картины. В Delphi для вызова процедуры предка используется ключевое слово inherited. Если нужно передать параметры, то нужно писать inherited Имя процедуры(параметр1, параметр2 …). Пример: constructor TScrollBox.Create(AOwner: TComponent); begin inherited Create(AOwner); ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents, csSetCaption, csDoubleClicks]; Width := 185; Height := 41;
http://www.vr-online.ru
46
VR-online Journal (Horrific and VR-Team)
Для программистов №7
FBorderStyle := bsSingle; end; Или вот ещё один: procedure TCustomFrame.CreateParams(var Params: TCreateParams); begin inherited; if Parent = nil then Params.WndParent := Application.Handle; end; Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
47