Short int Yhead; // координата низа головы




switch (num){

case 0:

Yhead=Ypos-H-Hmen;

Image1->Canvas->MoveTo(Xpos-H,Ypos);

Image1->Canvas->LineTo(Xpos, Ypos-H); // нога

Image1->Canvas->LineTo(Xpos+H, Ypos); // другая нога

Image1->Canvas->MoveTo(Xpos, Ypos-H);

Image1->Canvas->LineTo(Xpos, Yhead); // туловище

Image1->Canvas->MoveTo(Xpos+revers*H, Yhead-H);

Image1->Canvas->LineTo(Xpos,Yhead+4); // рука

Image1->Canvas->Ellipse(Xpos+revers*H-Rhead2,Yhead-H-Rhead2,

Xpos+revers*H+Rhead2, Yhead-H+Rhead2);

Image1->Canvas->LineTo(Xpos+revers*H, Yhead+H); // другая рука

Image1->Canvas->Ellipse(Xpos+revers*H-Rhead2, Yhead+H-Rhead2,

Xpos+revers*H+Rhead2, Yhead+H+Rhead2);

Image1->Canvas->Ellipse(Xpos-Rhead, Yhead,Xpos+Rhead,

Yhead-2*Rhead);

Image1->Canvas->Rectangle(Xpos-Rhead, Yhead-2*Rhead-1,

Xpos+Rhead, Yhead-2*Rhead-4); // шляпа

Image1->Canvas->Rectangle(Xpos-Rhead+2, Yhead-2*Rhead-4,

Xpos+Rhead-2, Yhead-2*Rhead-10); // верх шляпы

break;

case 1:

Yhead=Ypos-L-Hmen;

Image1->Canvas->MoveTo(Xpos, Ypos);

Image1->Canvas->LineTo(Xpos, Yhead);

Image1->Canvas->MoveTo(Xpos, Yhead+4);

Image1->Canvas->LineTo (Xpos+revers*L, Yhead+4);

Image1->Canvas->Ellipse (Xpos+revers*L-Rhead2, Yhead+4-Rhead2,

Xpos+revers*L+Rhead2, Yhead+4+Rhead2);

Image1->Canvas->Ellipse (Xpos-Rhead, Yhead, Xpos+Rhead,

Yhead-2*Rhead);

Image1->Canvas->Rectangle(Xpos-H/2, Yhead-2*Rhead-1,

Xpos+H / 2,Yhead-2*Rhead-4);

Image1->Canvas->Rectangle(Xpos-H/2+2, Yhead-2*Rhead-4,

Xpos+H/2-2,Yhead-2*Rhead-10);

}

}

//------------------------------------------------------

Обработчик события OnTimer выглядит так:

void __fastcall TForml::Timer1Timer(TObject *Sender){

Draw();

if ((Xpos>=Image1->Picture->Width-H) || (Xpos<=H))

revers=-revers;

Xpos=Xpos+revers*H;

num=1-num;

Draw();

}

//---------------------------------------------------------

Обработчик события кнопки OnClick содержит всего лишь один оператор, который меняет сотояние таймера на обратное

void __fastcall TForm1::Button1Click(TObject *Sender)

{

Timer1->Enabled=!Timer1->Enabled;

}//--------------------------------------------------------

В обработчик события формы включите FormCreate следующий код:

void __fastcal1 TForm1::FormCreate(TObject *Sender){

Image1->Canvas->MoveTo(0,Ypos+3);

Image1->Canvas->Pen->Width = 4;

Image1->Canvas->LineTo(Image1->ClientWidth,Ypos+3); // земля

Image1->Canvas->Pen->Width=1;

Image1->Canvas->Pen->Mode=pmNotXor; /* инверсный pmMerge

pmMerge - логическое or между цветом пера и фоном - возвращает 1, если оба бита не равны 0. Приводит к осветлению.

*/

Draw();

}

 

Начнем анализ этого кода с конца — с последней процедуры FormCreate, которая является обработчиком события OnCreate формы. В этой процедуре рисуется линия, отображающая «землю», по которой будет ходить наш человечек. Затем устанавливается режим пера pmNotXor, который говоит о том, что если пиксел принадлежит рисунку и фону, то все биты цвета пиксела меняются на противоположные. И в заключение, вызывается функция Draw, которая рисует исходное положение человечка.

Функция OnClick является обработчиком события кнопки. Каждый щелчок на кнопке включает или выключает таймер, в результате чего человек идет или останавливается. Функция Timer1Timer является обработчиком события OnTimer таймера. Это событие означает, что надо стереть прежний кадр и нарисовать новый. Сначала вывается функция Draw. Поскольку позиция человечка с момента показа предыдущего кадра не изменилась, то этот вызов рисует на том же самом месте, котором рисовался предыдущий кадр. Следовательно, предыдущий рисунок стирается. Затем анализируется позиция человечка Xpos. Если эта позиция отстоит от какого-либо конца холста Imagel на величину, меньшую шага Н, то изменяет на обратный знак переменной revers, характеризующей направление движения. Если revers =1, человечек шагает вправо; если revers =-1, человечек шагает влево. Затем позиция Xpos изменяется на величину revers*Н, т.е. на шаг вправо или влево. Изменяется переменная num, которая указывает номер высвечиваемого кадра: 0 или 1. В заключение вызывается процедура Draw, которая рисует указанный кадр в указанной позиции.

Последняя функция, которую мы рассмотрим - функция Draw, рисующая кадр. Она достаточно длинная, но в ней нет ничего сложного. В зависимости от значеиия num рисуется один или другой кадр, причем в рисунке учитывается позициция Xpos и направление движения revers.

Сохраните ваше приложение и выполните его. Щелкнув на кнопке, вы можете заставить вашего человечка перемещаться. Достигнув края формы, он будет поворачиваться, и шагать в обратном направлении. При вторичном щелчке на кнопн он будет останавливаться.

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

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

Image1->Canvas->Brush->Color=0;

Image1->Canvas->Rectangle(90,0,200,100);

Image1->Canvas->Brush->Color=clWhite;

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

В рассмотренном примере это можно сделать так: Откройте свой предыдущий проект мультипликации и сохраните его под новым именем командой File|Save Project As. Затем сохраните под новым именем файл модуля командой File|Save As. Теперь вы можете вводить в модуль изменения, не опасаясь испортить свой предыдущий проект.

Введите глобальную переменную типа TBitmap:

Graphics::TBitmap *BitMap;

Это будет объект, в котором вы будете сохранять фрагменты фона, испорченные очередным кадром. Переделайте процедуру FormCreate следующим образом:

void fastcall Tform1::FormCreate(TObject *Sender)

{

BitMap=new Graphics::TBitmap;

BitMap->Width=2*(L + Rhead);

BitMap->Height=L+Hmen+2 * Rhead+6;

Image1->Canvas->MoveTo (0, Ypos+3);

Image1->Canvas->Pen->Width=4;

Image1->Canvas->LineTo(Image1->ClientWidth, Ypos+3); // земля

Image1->Canvas->Pen->Width=2;

BitMap->Canvas->CopyRect(Rect(0,0,BitMap->Width,BitMap->Height),

Imagel->Canvas,Rect(Xpos-L-Rhead,Ypos-(L+Hmen+2*Rhead+5), Xpos+L+Rhead,Ypos+1));

Draw(); }

Первыми операторами этой процедуры вы создаете объект BitMap и задаете его размеры равными максимальным размерам изображения человечка. В конце процедуры, перед вызовом Draw в компонент BitMap методом CopyRect копируется фрагмент изображения, внутри которого будет расположен рисунок человечка.

После этого процедурой Draw рисуется соответствующий кадр. Обратите внимание на то, что в данном приложении отсутствует оператор, задававший ранее режим pmNotXor. Так что по умолчанию рисунок будет делаться обычным образом.

Поскольку приложение создало объект Bitmap, надо не забыть добавить в него обработчик события OnDestroy формы, в который вставить оператор

Bitmap->Free();

Процедуру Timer1Timer измените следующим образом:

void __fastcall Tform1::Timer1Timer(TObject *Sender){

Image1->Canvas->Draw(Xpos-L-Rhead,

Ypos-L-Hmen-2*Rhead-5, BitMap);

if ((Xpos >=Image1->Picture->Width-H) | (Xpos <= H))

revers=-revers;

Xpos=Xpos+revers*H;

num=1-num;

BitMap->Canvas->CopyRect(Rect(0, 0, BitMap->Width,

BitMap->Height),Imagel->Canvas,

Rect(Xpos-L-Rhead,

Ypos-(L+Hmen+2*Rhead+5),

Xpos+L+Rhead,Ypos+1));

Draw ();

}

Если вы сравните с тем, что было в предыдущем приложении, то увидите, чтовместо первого вызова процедуры Draw, который стирал предыдущий кадр, вводится оператор Image1->Canvas->Draw, который выполняет ту же функцию, но путем восстановления запомненного ранее фрагмента фона под рисунком. Вторым отличием является наличие оператора BitMap—>Canvas—>CopyRect, который перед вызовом Draw запоминает новый фрагмент фона.

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

Наше изображение было очень простым и рисовалось быстро. Но при сложных Изображениях время рисования может быть заметным и приводить к мерцанию картинки и другим неприятным зрительным эффектам. В этих случаях используем буферизацию изображения. Это напоминает то, что вы только что делали с фоном, но относится не к фону, а к рисунку. Рисование очередного кадра производится не сразу на холсте, который видит пользователь, а на канве невидимого компонента, типа того Bitmap, с которым вы только что работали. А после того, как рисунок сделан, он переносится на видимый холст методами Draw или CopyRect. Эти методы работают очень быстро, и никаких неприятных мерцаний не происходит.

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

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

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

#define Pi 3.1415926535897932385

short int cadr =0; // номер кадра

short int H=30; // длина ноги и руки

short int Xpos = 3 * H;, // координата опорной ноги

short int Ypos =120; // "земля"

short int Hmen =3 0; // высота тела

short int Rhead =10; // радиус головы

short int revers =1; // направление движения

short int L = H * 1.41; // длина ноги

short int Ncadr=16; // число кадров на шаг

//-------------------------------------------------

void fastcall Tform1::Draw(){

float Angl=Pi/4*(1+(2.*cadr)/(Ncadr-1));

short int Yb=Ypos-H*sin(Angl);

short int Yt=Yb-Hmen;

short int X=Xpos-revers*H*cos(Angl);

Image1->Canvas->MoveTo(X-(Xpos-X),Ypos);

Image1->Canvas->LineTo(X,Yb); // нога

if(cadr!=Ncadr/2-1)

Image1->Canvas->LineTo(Xpos,Ypos); // другая нога

Image1->Canvas->MoveTo(X,Yb);

Image1->Canvas->LineTo(X,Yt);.. // туловище

short int XI = X - revers * O(b-Ypos);

Image1->Canvas->MoveTo(XI,Yt+5-(Xpos-X));

Image1->Canvas->Ellipse(Xl-Rhead/2,

Yt+5-(Xpos-X)-Rhead/2, Xl+Rhead/2, Yt+5-(Xpos-X)+Rhead/2);

Image1->Canyas->LineTo(X,Yt+5); // рука

if (cadr!=Ncadr/2-1) {Imagel->Canvas->Ellipse(Xl-Rhead/2,

Yt+5+(Xpos-X)-Rhead/2, Xl+Rhead/2,

Yt+5+(Xpos-X)+Rhead/2);"

Image1->Canvas->LineTo(XI,Yt+5+(Xpos-X)); // другая рука }

Image1->Canvas->Ellipse(X-Rhead,Yt-2*Rhead, X+Rhead, Yt);

Image1->Canvas->Rectangle(X-Rhead,Yt-2*Rhead-4,

X+Rhead,Yt-2*Rhead-l); //шляпа

}

 

void __fastcall TForml::BRunClick(TObject *Sender){

Timer1->Enabled=!Timerl->Enabled;

}

//------------------------------------------------

void __fastcall Tform1::Timer1Timer(TObject *Sender){

Draw();

 

cadr=(cadr+l)%Ncadr;

if((cadr==0)

if((Xpos<=Image1->(Xpos->Picture->Width-revers*3*H)&&(Xpos + =revers*H* 1.41;

else revers = -revers;

Draw();

}

//------------------------------------------------I

void __fastcall Tform1::FormCreate(TObject *Sender){

Image1->Canvas->MoveTo(0,Ypos+3);

Image1->Canvas->Pen->Width = 4;

Image1->Canvas->LineTo(Image1->ClientWidth, Ypos + 3); // земля

Image1->Canvas->Pen->Width = 1;

Image1>Canvas->Pen->Mode = pmNotXor;

Image1->Interval = 600/Ncadr;

 

В этом коде использованы математические функции sin и cos. Поэтому в код необходимо добавить директиву препроцессора

#include <math.h>

Рассмотрим коротко приведенный код.

Процедура FormCreate отличается от той, что была в первом приложении только оператором, задающим выдержку таймера (свойство Interval) путем деления 600 на константу Ncadr, которая задает число кадров на один цикл движе на один шаг человечка. Как видно, длительность одного шага выбрана рав 600 миллисекунд. Вы, конечно, можете изменить это значение, как и значение Ncadr, выбранное равным 16.

Процедура BRunClick не отличается от той, что была в первом приложении Процедура Timer1Timer, как и в первом приложении начинается и кончается вызовами Draw, первый из которых стирает изображение предыдущего кадра, а второй рисует новый кадр. После первого вызова Draw рассчитывается значени переменной cadr оператором

сadr=(cadr+l)%Ncadr;

Поскольку тут используется операция вычисления остатка от деления cadr+1 на Ncadr, то значение cadr последовательно получает значения 0, 1, 2,..., Ncadr-1. Шаг начинается с cadr = 0. При этом проверяется, не приблизился ли человечек к краю формы, и если приблизился — изменяется направление движения (знак переменной revers).

Наиболее серьезно изменилась процедура Draw. Она начинается с определения угла наклона ног и рук Angl, исходя из номера кадра:

float Angl=Pi/4"(1+(2.*cadr)/(Ncadr-1));

Обратите внимание на то, что константа 2 в этом выражении записана с точкой и произведение 2.*cadr заключено в скобки. Это принципиально, так как в этом случае константа воспринимается как значение с плавающей запятой и все мыражение (2.*cadr)/(Ncadr-1) вычисляется как значение с плавающей точкой. Если не поставить точку после 2, то будут применяться целочисленные вычисления и пока 2 * cadr меньше, чем Ncadr-1, значения этого выражения буду равны 0 и угол изменяться не будет.

После вычисления угла на его основе строится изображение, подобное тому которое было в первом приложении. Обратите внимание на то, что вторая нога и вторая рука человечка рисуются только, если выполняется условие cadr!=Ncadr /2-1. Это связано с тем, что, если число кадров Ncadr четное, то в этот момент одна нога накладывается на другую и руки также накладываются друг надруга. Поскольку рисование идет в режиме pmNotXor, то это наложение приведет к тому, что у человечка вообще исчезнут в этом кадре руки и ноги. Правда всего на один кадр, но все равно неприятно.

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

 

 

Воспроизведение немых видео клипов — компонент Animate

Теперь рассмотрим способ воспроизведения в приложении C++Builder стандартных мультипликаций Windows и файлов .avi - клипов без звукового сопровождения. Это позволяет сделать компонент Animate, расположенный на закладке Win32 библиотеки.

Компонент Animate позволяет воспроизводить на форме стандартные видео клипы Windows (типа копирования файлов, поиска файлов и т.п.) и немые видеофайлы .avi — Audio Video Interleaved. Эти файлы представляют собой последовательность кадров битовых матриц. Они могут содержать и звуковую дорожку, компонент Animate воспроизводит только немые клипы АVI. Он работает только с неуплотненными файлами AVI или с клипами AVI, уплотненными с использона нием RLE — run-length encoding.

TAnimate может воспроизводить клипы AVI из ресурсов, из файлов или библиотеки Shell32.dll, если приложение работает с Windows 95 или NT.

Откройте новое приложение, перенесите на форму компонент Animate. Он расположен на вкладке Win32.

После переноса компонента на форму его свойства появятся в инспекторе объектов. Воспроизводимое им изображение задается одним из двух свойств: FileName или CommonAVI.

Число повторений
Выбор типового клипа из ресурсов Windows

 

Первое из этих свойств, как ясно из его названия, позволяет в процессе проектирования или программно задать имя воспроизводимого файла, а свойство CommonAVI позволяет воспроизводить стандартные мультипликации Windows. Это свойство типа TCommonAVI, объявленного следующим образом:

enum TCommonAVI {aviNone, aviFindFolder, aviFindFile,

aviFindComputer, aviCopyFiles, aviCopyFile,

aviRecycleFile, aviEmptyReсycle, aviDeleteFile};

 

Тип TCommonAVI определяет множество предопределенных в Windows мультипликаций типа копирования файлов, поиска файлов, удаления файлов и т.д. Что означает каждое значение вы увидите из тестового приложения, которое построите чуть позже.

А пока установите значение CommonAVI, например, равным aviCopyFile. Это соответствует стандартному изображению копирования файла. Соответствуюющий начальный рисунок немедленно появится на вашей форме.

 

Свойство Repetition компонента Animate задает число повторений воспроизведения клипа. Если оно равно 0 (значение по умолчанию), то воспроизведение повторяется вновь до тех пор, пока не будет выполнен метод Stop. При выполнении этого метода генерируется событие OnStop, которое можно использовать, например, чтобы стереть изображение — сделать его невидимым.

Если же свойство Repetitions задать большим нуля, оно определит число повторений клипа. Задайте его, например, равным 3. А теперь установите свойство Active компонента Animate в true. Вы увидите, что еще в процессе проектирования ваше приложение заработает. Изображение оживет, и клип будет щ вторен 3 раза.

Вы можете посмотреть воспроизводимое изображение по кадрам. Для этого щелкните на компоненте правой кнопкой мыши и из всплывшего меню выберите разделы Next Frame (следующий кадр) или Previous Frame (предыдущий кадр). Это позволит вам выбрать фрагмент клипа, если вы не хотите воспроизводить клип полностью. Воспроизвести фрагмент клипа можно, установив соответствующие значения свойств StartFrame - начальный кадр воспроизведения, и StopFrame - последний кадр воспроизведения.

Воспроизводить фрагмент клипа можно и методом Play, который определен следующим образом:

void __fastcall Play(Word FromFrame, Word ToFrame, int Count);

Метод воспроизводит заданную последовательность кадров клипа от FromFrame до ToFrame включительно, и воспроизведение повторяется Count раз. Если FromFrame =1, то воспроизведение начинается с первого кадра. Значение ToFrame должно быть не меньше FromFrame и не больше значения, определяемого свойством FrameCount (свойство только для чтения), указывающим полное число кадров в клипе. Если Count = 0, то воспроизведение повторяется до тех пор, пока не будет выполнен метод Stop.

Выполнение Play идентично заданию StartFrame равным FromFrame, StopFrame равным ToFrame, Repetitions равным Count и последующей установке live в true.

В компоненте Animate предусмотрены события OnClose, OnOpen, OnStart и OnStop, генерируемые соответственно в моменты закрытия и открытия компонента начала и окончания воспроизведения.

Давайте теперь построим тестовое приложение, показывающее возможности компонента Animate. Установите в том приложении, которое вы уже начали, свойство Visible компонента Animate в false. Это надо для того, чтобы изображение возникало только тогда, когда произойдет соответствующее событие: копирование файлов, поиск файлов и т.п. В тестовом приложении мы будем имитировать начало и окончание события, которое должно сопровождаться мультипликацией, нажатиями кнопок запуска и останова воспроизведения. Поэтому верните значение свойства Repetitions в 0, чтобы воспроизведение длилось до окончания события. Свойство Active установите в false. Полезно также установить свойство AutoSize в false, а свойство Center в true, чтобы изображение всегда появлялось в центре экрана.

Добавьте в приложение 3 кнопки. Первая из них (назовите Start) будет начинать процесс воспроизведения поочередно всех стандартных клипов Windows. Вторая кнопка (назовите ее Stop) пусть завершает воспроизведения очередного клипа. А третью кнопку (назовите ее File) введем для того, чтобы показать, что компонент может воспроизводить изображения из заданного файла .avi. Чтобы пользователь мог выбрать файл изображения, добавьте на форму компонент OpenDialog и задайте его фильтр (свойство Filter) равным «видео *.avi|*.avi».

Теперь все приготовления закончены и осталось только написать обработчик событий. Код обработчиков может иметь вид:

 

int i;

//-----------кнопка Start-------------------

void __fastcall TForm1:: Button1Click (TObject *Sender)

{

Animate1->Visible=true;

i=1;

Animate1->CommonAVI = aviFindFolder;

Animate1->Active=true;

}

//------------кнопка Stop------------------------

void fastcal1 TForm1:: Button2Click (TObject *Sender)

{

Animate1->Stop();

}

//------------------------------------------------

void __fastcal1 TForm1::Animate1Stop(TObject *Sender)

{

i++;

switch (i) {

case 2: Animate1->CommonAVI=aviFindFile;

break;

case 3: Animate1->CommonAVI=aviFindComputer;

break;

case 4: Animate1->CommonAVI=aviCopyFiles;

break;

case 5: Animate1->CommonAVI=aviCopyFile;

break;

case 6: Animate1->CommonAVI=aviRecycleFile;

break;

case 7: Animate1->CommonAVI = aviEmptyRecycle;

break;

case 8: Animate1->CommonAVI=aviDeleteFile;

}

if (i<9) Animate1->Active=true;

else Animate1->Visible=false;

}

//-----------------------------------------------------

void __fastcal1 TForm1:: Button3Click(TObject *Sender)

{

if (OpenDialog1->Execute())

{

i=9;

Animate1->FileName=OpenDialog1->FileName;

Animate1->Visible=true;

Animate1->Active=true;

}

}

Обработчик события OnClick кнопки Start задает начальное значение свойства CommonAVI, сбрасывает счетчик на 1, делает компонент Animatel видимым и активизирует его.

Обработчик события OnClick кнопки Stop останавливает воспроизведение методом Stop.

Обработчик события OnStop компонента Animatel увеличивает счетчик на 1, в зависимости от значения счетчика загружает в компонент соответствующий клип Windows, и активизирует компонент. Если все клипы уже воспроизведены, компонент делается невидимым.

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

В качестве видео файла можно использовать файл...\Exampies\Mfc\General\Cmnctrls\dillo.avi, поставляемый с примерами C++Builder, или любой другой более интересный видеофайл.

 

Проигрыватель MediaPlayer

Компонент MediaPlayer обеспечивает воспроизведение звуковых файлов различных форматов (WAV, MID, RMI, МРЗ), полноценной, сопровождаемой звуком анимации и видеороликов (AVI) и музыкальных компакт-дисков. Значок компонента MediaPlayer находится на вкладке Systemбиблиотеки компонентов.

Компонент можно использовать для работы с помощью предоставляемых для этого кнопок:

или сделать кнопки невидимыми и управлять воспроизведением с помощью методов компонента.

Кнопка Обозначение Действие
Воспроизведение btPlay Воспроизведение звука или видео
Пауза btPause Приостановка воспроизведения
Стоп btStop Остановка воспроизведения
Следующий btNext Переход к следующему кадру
Предыдущий btPrev Переход к предыдущему кадру
Шаг btStep Переход к следующему звуковому фрагменту, например, к следующей песне на CD
Назад btBack Переход к предыдущему звуковому фрагменту, например, к предыдущей песне на CD
Запись btRecord Активизирует процесс записи
Открыть btEject Открывает CD-дисковод компьютера

 

Каждой кнопке медиа-плеера соответствует метод, осуществляющий по умолчанию требуемую операцию: Play, Pause, Stop, Next, Previous, Step, Bad StartRecording, Eject.

Тип устройства мультимедиа, с которым работает медиа-плеер, определяется его свойством DeviceType. Если устройство мультимедиа хранит объект воспроизведения в файле, то имя файла задается свойством FileName. По умолчанию свойство DeviceType имеет значение dtAutoSelect. Это означает, что медиа-плеер пытается определить тип устройства исходя их расширения имени файла FileName.

Еще одно свойство MediaPlayer - AutoOpen. Если оно установлено в true, медиа-плеер пытается открыть устройство, указанное свойством DeviceType, автоматически во время своего создания в процессе выполнения приложения.

Воспроизведение видео информации по умолчанию производится в окно, которое создает само открытое устройство мультимедиа. Однако это можно изменить, если в свойстве Display указать оконный элемент, в котором должно отображаться изображение. Это может быть, например, форма или панель. Можно также задать в свойстве DisplayRect типа TRect (свойство только времени выполнения) прямоугольную область этого окна, в которую должно выводиться изображение. Для задания свойства DisplayRect можно использовать функцию Rect. Однако в данном свойстве использование этого типа не совеет обычно. Первые две координаты, как и обычно, задают положение левого верхнего угла изображения. А два следующих числа задают ширину и высоту изображения, а не координаты правого нижнего угла. Например, оператор

MediaPlayerl->DisplayRect = Rect(10,10,200,200);

задает для вывода область с координатами левого верхнего угла (10, 10), длиной; шириной, равными 200.

В компоненте MediaPlayer определены события OnClick и OnNotify. Первое из них происходит при выборе пользователем одной из кнопок медиа-плеера и определено как

enum TMPBtnType fbtPlay, btPause, btstop, btNext,

btPrev, btStep, btBack, btRecord, btEject);

void fastcall Click (TObject *Sender, TMPBtnType Button, bool SDoDefault)

Параметр Button указывает выбранную кнопку. Параметр DoDefauIt, передаваемый по ссылке, определяет выполнение (при значении true по умолчанию) или отказ от выполнения стандартного метода, соответствующего выбранной кнопке.

Событие OnNotify происходит после возвращения очередного метода, если свойство медиа-плеера Notify было установлено в true. Способ возврата любого метода медиа-плеера определяется свойством Wait. Если установить Wait равным false, то возвращение управления в приложение происходит сразу после вызова метода, не дожидаясь завершения его выполнения. Таким образом, задав Notify равным true и Wait равным false, можно обеспечить немедленный возврат в приложение и отображения пользователю текущего состояния объекта мультимедиа.

Свойства Notify и Wait действуют только на один очередной метод. Поэтому их значения надо каждый раз восстанавливать в обработчиках событий OnClick или OnNotify.

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

Вот, собственно, в конспективном виде основная информация о компоненте MediaPIayer. А теперь попробуйте все это на практике. Простое и в тоже время мощное приложение можно сделать очень просто. Начните новый проект и перенесите на форму компоненты MediaPlayer (вкладка System), MainMenu (вкладка Standart) и OpenDialog (вкладка Dialods). В фильтре компонента OpenDialog можно, например, задать:

 

аудио и видео (*.wav,*.mid,*.avi) *.wav; *.mid; *.avi
аудио (*.wav,*.mid) *.wav;*.mid
видео (*.avi) *.avi
все файлы *.*

В меню достаточно задать одну команду: Файл | Открыть. Обработчик события OnClick этой команды может содержать оператор

if (OpenDialog1->Execute()) {

MediaPlayer1->FileName=OpenDialog1->FileName;

MediaPlayer1->Open();

}

который открывает устройство мультимедиа, соответствующее выбранному пользователем файлу. При этом надо проследить, чтобы в компоненте MediaPlayer свойство DeviceType равнялось dtAutoSelect. Это обеспечит автоматический выбор соответствующего устройства мультимедиа исходя из расширения выбранного файла.

В компоненте MediaPIayer при желании можно указатьими файла FileName, открываемого в момент начала выполнения приложения. Тогда надо установитьсвойство AutoOpen в true. Впрочем, это, конечно, не обязательно.

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

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

 

В первой из них (Label1) укажем надпись «Файл:» (свойство Caption). Во второй (Label2) «Состояние» - состояние проигрывателя, в третьей (Label3) «Операция» - последнюю вызванную операцию. Четвертую метку (Label4) расположим рядом с меткой Label1 так, чтобы она служила ее продолжением. В ней будем отображать имя загруженного файла, но в сокращенном виде с многоточиями, если имя файла не помещается в отведенном для него месте.

Код, обеспечивающий подобную обратную связь в приложении, может следующим.

#include "filectrl.hpp"

AnsiString ModeStr[7]={"He готово", "Остановлено", "Воспроизведение", "Запись", "Поиск", "Пауза", "Открыто"};

AnsiString ButtonStr[9]={"Воспроизведение", "Пауза", "Стоп", "Следующий", "Предыдущий", "Вперед", "Назад", "Запись", "Конец"};

//----------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)

{

Label4->Caption = MinimizeName(MediaPlayer1->FileName,

Label4->Canvas,200);

Label2->Caption = "Состояние: " + ModeStr[MediaPlayer1->Mode];

MediaPlayer1->Notify=true;

}

//-------------------------------------------

void __fastcall TForm1::N2Click (TObject *Sender)

{

if (OpenDialog1->Execute ())

{

MediaPlayer1->FileName=OpenDialog1->FileName;

Label4->Caption=MinimizeName(MediaPlayer1->FileName,

Label4->Canvas,200);

MediaPlayer1->Open();

MediaPlayer1->Notify=true;

}

}

//----------------------------------------

void __fastcall TForm1::MediaPlayer1Notifу(TObject *Sender)

{

Label2->Caption="Состояние:"+ModeStr[MediaPlayer1->Mode];

// Переустановка Notify,

// чтобы событие произошло в следующий раз

MediaPlayer1->Notify=true;

}

//--------------------------------------------

void __fastcall TForm1::MediaPiayer1Click(TObject *Sender,

TMPBtnType Button, bool &DoDefault)

{

Label3->Caption="Операция:"+ButtonStr[Button];

// Переустановка Notify, чтобы произошло событие OnNotify

MediaPlayer1->Notify=true;

}

В свойстве FileName компонента MediaPlayer1 задано имя файла, загружаемого в момент создания MediaPlayer1, т.е. в момент создания приложения. Соответственно в обработчике FormCrcate события OnCreate формы записаны операторы, задающие имя файла и текущее состояние проигрывателя в метке Label4 и Label2 соответственно. Для записи имени файла использованафункция MinimizeName, которая обеспечивает сокращенное отображение пути К файлу (см. рис.) в случае если путь не помещается в отведенном месте (в операторе указана максимальная длина 200 пикселей). Чтобы эта функция работала, в модуль добавлена директива компилятора

#include "filectrl.hpp"

В дальнейшем отображение соответствующей информации предусмотрено в процедурах, соответствующих открытию файла (OpenClick), нажатии пользователемкакой-нибудь кнопки проигрывателя (MediaPlayerlClick), возвращении любого метода проигрывателя (MediaPlayerlNotify). После каждого события выполняется оператор

MediaiaPlayer1->Notify=true;

обеспечивающий наличие события OnNotify после возвращения следующего метода проигрывателя.

Запустите приложение и проверьте его в работе. С его помощью можно слушать музыку, смотреть немые и звуковые клипы.

 

 

 

 

Процессы, потоки, распределенные приложения

 

Процесс — это то, что в обиходе обычно называется выполняющейся прикладной программой или приложением. Простая самодостаточная программа, не вызывающая других программ, — это один процесс. Он характеризуется своим адресным простраством, памятью, исполняемым кодом и данными. Рассмотренные ранее примеры — это, как правило, именно такие программы, состоящие одного процесса.

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

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

 

Запуск внешней программы функцией execlp

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

Функция execlp определена в библиотечном файле process.h. Его нужно включать в приложение соответствующей директивой #include<process.h>

Сама функция имеет прототип:

int execlp(char *path, char *arg0,*arg1,..., char *argn, NULL)

 

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

Если же расширение файла не задано, то сначала ищется такой файл,который задан. Если он не находится, к имени добавляется распширение .exe ипоиск повторяется. Если файл опять не находится, к имени добавляется расширение .com и поиск повторяется. Если в path не задан путь, то сначала поиск файла производится в текущем каталоге. Если в нем требуемый файл не найдем, то поиск продолжается в каталогах, указанных в переменной окружения РАTН.

Аргументы функции arg0 — argn являются параметрами, передаваемыми в запускаемую на выполнение программу через командную строку. Функция должна передать в запускаемое приложение хотя бы один аргумент arg0. По соглашению аргумент arg0 это копия path. Впрочем, передача другого значения не является ошибкой. Остальные аргументы, если они требуются,



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: