Главная » Delphi » Демонстрация "превьюшек"

0

Для демонстрации "превьюшек" мы создадим новую форму (когда;), и назовем модуль preview.pas. Вопрос состоит в том, куда именно выводить "пре- выошки"? Если вы ожидаете, что я воспользуюсь чем-нибудь вроде DrawGrid (что было бы самым логичным), то ошибаетесь. У таблицы DrawGrid, как и у stringGrid (которую также можно использовать) слишком много, на мой вкус, "фичей", которые придется мужественно преодолевать без какой-либо в них надобности. Все, что нам требуется — расположить уменьшенные картинки рядами, вписавшись в ширину формы. И наиболее просто это сделать, используя канву и уже знакомый нам по главе 10 метод stretchDraw. Единственная сложность, которая подстерегает нас на этом пути — вычислить номер картинки при щелчке на изображении ее "превыошки", чтобы отобразить ее в основном окне. По это, как увидите, совсем нетрудно— в сравнении с тем, какое количество параметров нам пришлось бы настраивать при использовании компонентов-таблиц и сколько при этом было бы возни с формированием уменьшенных изображений.

Итак, поставим на форму Form4 компонент ScroilBox (ScrollBoxl), растянем его и установим внутрь наш любимый image (imagel). Вообще-то называть одинаково компоненты на двух формах одного приложения — плохой стиль, слишком легко что-то перепутать, но мы постараемся избежать ошибок — imagel выглядит привычно, а использовать мы его будем в основном в своих процедурах, где Delphi без уточнений, что чему принадлежит, все равно не обойдется. Ниже установим кнопку Butuonl и запишем у нее в заголовке Остановить.

У ScrollBoxl установим ДЛЯ свойства HorScrollBox пункт Visible В False, а свойство color в cisiiver— пусть фон для "превьюшек" всегда будет серый. Чтобы imagel имел тот же цвет, сразу создадим обработчик oncreate для формы Fonn’i:

procedure TForm4.FormCreate(Sender: TObject); begin

Imagel.Canvas.Brush.Color:=Fonm4.ScrolIBoxl.Color;

{цвет фона картинок clSilver)

end;

А для самой формы установим Position В poDefault, FormStyle в fsStayOnTcp, BorderStyle в bsDialog, а в Caption запишем просто siideshow. После всех этих действий мы получим заготовку формы, показанную на рис. 15.2.

Рис. 15.2. Форма Preview программы SlideShow

В основном модуле (slide.pas) мы объявим дополнительные переменные: счетчик ni: .integer и FiagE:byte=o (последний будет отвечать за режим остановки загрузки "превыошек" по ходу процесса). Кроме этого туда же добавим еще один флаг— MinFlag:booiean=True. Чтобы из модуля preview.pas также были доступны переменные главного модуля, мы вставим ссылку на последний в главное предложение uses (а не после implementation, как это делает Delphi автоматически). Туда же добавим ссылку на модуль JPEG. А в slide.pas еще вынссем в интерфейсную секцию строку procedure Loadfile (рядом с createMyicon), она нам понадобится в preview.pas.

Теперь научимся запускать окно предпросмотра. Чтобы не думать о запутанной логике действий при минимизации в Tray, мы попросту будем запрещать все нестандартные разновидности минимизации, пока окно предпросмотра виднеется на экране. Именно для этого нам понадобился флаг MinFiag. Внесем такие изменения в тексте основного модуля: в процедуре FormlCreate внутри операторных скобок ПО условию if SectionExists {‘Sets’) добавим строку:

MinFiag:=CheckBox3.Checked; /запоминаем состояние митмизации)

То же самое допишем в процедуре окончания установок Button2ciick в самом конце ее. А теперь заменим В процедурах OnMinimizeProc И FormDeactivate условие if CheckBox3.Checked then на if MinFiag then. И, наконец, создадим обработчик щелчка на кнопке Butt on р:

procedure TForml.ButtonPClick(Sender: TObject); begin /предпросмотр /

Panel2.Visible:=False; (закрываем панель настроекI if Filelist.Count.=0 then exit; (если нет картинок – выходим) if Timerl.Enabled=True then

(если демонстрация идет – останавливаем) begin

Run.Capt ion: =’Запуск';

(меняем название пункта меню обратно на ЗапускI Timerl.Enabled:=False; (таймер остановлен) PlaySound(Pchar (frnusic) ,0, SND_PURGE); (выключили музыку) FlagMusic:=0; /в следующей раз опять запустим) end;

ButtonP.Enabled:=False; (чтобы в другой раз не нажать)

MinFiag:=False; (запрещаем минимизацию в Tray)

mayClose:=True; (запрещаем закрытие в Tray)

Form4.Top:=Forml.Тор+64;

Form4.Left:=Forml.Left+14;

Form4.Show;

end;

Согласно последним операторам, у нас новая форма откроется как бы внутри главного окна. Если кому это не понравится, то изменить ее положение легко. Причем главное окно и окно предпросмотра никак не будут связаны между собой — их легко можно передвигать по экрану, за исключением того, что окно предпросмотра будет стремиться "вылезти" вперед— например, если вы распахнете главное окно клавишей <Esc> (см. выше), то окно предпросмотра скроется, но тут же вновь появится, если отменить полнооконный режим. А вот создавать его в MDI-стиле (как отдельные окна документов в Word) нецелесообразно — это приведет только к потере полезной площади. Заметим попутно, что MDI-окна вообще сейчас не в моде — гораздо удобнее делать многооконные приложения в виде одного окна с закладками, просто тут у нас всего одно "лишнее" окно и закладки нам ни к чему.

Теперь возьмемся вплотную за модуль preview.pas и в первую очередь создадим основную процедуру вывода "превьюшек" в компонент imagel формы Form4. Перебор списка происходит согласно счетчику ni, который мы установим позднее. В модуле preview.pas объявим следующие переменные:

var

Form-J: TForm4; Arect: TRect;

nframe,nlast:integer; {счетчик и признак выхода из загрузки) ratio:double; {соотношение сторон картинки) iWidth,iHeight,iTop,iLeft,iLShift,iTShift:integer;

Тогда основная процедура будет выглядеть так:

procedure imagepreview;

(основная процедура загрузки "превьюшек" на страницу) begin

Form4.Buttonl.Caption: =’Остановить';

with Forml do

begin

Bmplm:= TBitMap.Create; (создаем экземпляр BitMap) Jpglm:= TJpeglmage.Create; (создаем экземпляр JPEG) end; ni:=0;

repeat (основной цикл загрузки) if FlagEoO then begin

Form4.Buttonl.Caption: =’Продолжить 1; exit; (если признак выхода не 0 – выходим) end;

with Forml do begin try

BmpIm.LoadFromFile (Filelist [ni]) ;

Iзагружаем BMP-картинку с дискаI except /это не BMP) Jpglm. LoadFromFile(Filelist [ni]);

(загружаем JPEG-картинку с лиска) Bmplm.Assign(Forml.Jpglm); {ассоциируем BitMap с JPEG) end;

ratio:=BmpIm.Height/Bmplm.Width;

{определяем соотношение сторон)

end;

iLShift:=0; {сдвиг картинки слева =0} iTShift:=0; {сдвиг картинки сверху =0) iWidth:=66; {ширина поля для картинки » 66 пикселов) iHeight:=round(66*ratio);

{высота поля для картинки с сохранением соотношения сторон) if iHeight>66 then

{если высота вылезла за пределы квадрата 66×66)

begin

iHeight:=66; {тогда наоборот, высота-бб) iWidth:=round(66/ratio);

(а ширину поля определяем по соотношению сторон} iLShift:=(66-iWidth) div 2;

{сдвиг поля слева от стороны квадрата ббхбб) iLeft :=iLeft+iLShift; {косршната левого края картинки) end else (если высота не выходит за рамки) begin

iTShi f t: = (66-iHeight) div 2;

(сдвиг поля сверху от стороны квадрата 66×66) iTop:=iTop+iTShift; (координата верхнего края картинкиI end;

if iLeft+70>=Form4.Imagel.Width then {если правый край поля картинки

вылезает за правый край компонента)

begin

iTop:=iTop+70; {то организуем следующий ряд картинок -

сверху прибавляем 70 пк/ iLeft:=iLShift; {слева первая картинка в новом ряду только на величину сдвига)

end;

with FornvS do begin

if (iTop-iTShift+70-490*nframe)>490 then

{если достигнут нижний край окна)

begin

Forml.Imagel.Height:^Imagel.Height+490;

(увеличиваем высоту Image) n f rame:=n fcame•1;

Image.I.. Pictrure.Bitmap.Height :=Imagel. Picture.Height (490;

/увеличиваем высоту картинки) ScrollBoxl.VertScroilBox.Position:^

Scroll Boxl .Verr.ScrollBox. Position (-490; /сдвигаем ползунок прокрутки вниз! applicat ion.ProcessMessages;

/заставляем Windows отреагировать)

end; end;

Arect:=Rect (it,eft, tTop, iLeft+iWidth, iTop+iHeight) ;

/залаем размеры прямоугольника для картинки) iLeft :-iI,ef t-iLShift;

/залаем начальные координаты для следующего раза) i.Top: = iTop-i.T.Shi f.t; iLe?t:=iLeft+70;

Form4.Imagel.Canvas.StretchDraw(aRect, Forml.Bmplm);

(загружаем картинку в заданный прямоугольник из BitMap) application.ProcessMessages;/заставляем Windows отреагировать) ni:=ni+l;

until ni=Forml.Filelist.Count; /и так до конца списка) Forml .Bmplm.Destroy; {уничтожаем объект BiU-iap) Forml.Jpglm.Destroy; (уничтожаем объект JPEG) Form4.Buttonl.Caption^’Закрыть'; end;

Здесь мы отвели рисунку поле (так сказать, одно "рисункоместо") 70×70 пикселов, но само изображение вписываем в квадрат 66×66 — по два пиксела со всех сторон остается для разделения соседних рисунков. Отметим, что корректно работать этот алгоритм будет именно при той высоте image: (490 пикселов) н Scrol lBoxl (>510 пикселов), которые установлены на этапе конструирования в данном конкретном случае, что, конечно, концептуально неправильно. Но, с другой стороны, такие процедуры требуются нечасто, а в данном случае форму мы сделали с неизменяемыми размерами, так что вес будет работать как надо. Более универсальный алгоритм, который бы годился для любых форм и любого размера "превьюшек", потребовал бы от нас на порядок больше времени на отладку.

Саму процедуру мы будем выполнять по событию onPaint формы, чтобы все происходило на глазах пользователя. Л для того чтобы процесс выполнялся не каждый раз при прорисовке формы, а только один раз— когда в imagel еще ничего нет, мы установим условие, что прорисовка выполняется, если счетчик ni равен 0:

prooedure TForm4.FormPaint(Sender: TObject); begin

if ni<>0 then exit; {уже нарисовали)

Form4.ScrollBoxl.VertScroll3ar.Position:=0;

{начальная позиция ползунка линейки прокрутки – самый верх)

iTop:=0; {расстояние первой картинки от верхнего края

компонента Imagel =0) iLeft:=0; {и от левого края тоже =01 nframe:=0; {нулевой кадрI imagepreview; {загружаем картинки) end;

Так когда же мы будем счетчик ni устанавливать в начальное нулевое значение? Естественно, каждый раз, когда меняется содержание списка Filelist. Именно поэтому нам и потребовалась глобальная переменная ni, которую мы включили в главный модуль (могли бы, впрочем, и оставить в preview.pas, если перенести ссылку на него в модуле slide.pas также в общее предложение uses). Включим в процедуру PictureList в модуле slide.pas такие строки (сразу после первой строки с проверкой наличия картинок по вызову NumFiies):

if Forml.Timerl.Enabled=True then (если демонстрации? идет – останавливаем)

begin

Forml.Run.Caption:=’Запуск'; {меняем название пункта меню

обратно на Запуск/ Forml.Timerl.Enabled:=False; {таймер остановлен/ PlaySound(Pchar(fmusic),0, SND_PURGE); {выключили музыку) FlagMusic :=0; {в следукше-ш раз опять запустим) end;

Form4.Close; {закрываем форму Preview/

ni:=0; {обнуляем счетчик/

FlagE:=0; {загружать Preview с начала/

Fom4.Imagel.Canvas.FillRect (Form4 .Imagel.Canvas.CIipRect) ; {чистим Preview от старых картинок)

Заодно мы здесь также выключаем автоматическую демонстрацию, если она была запущена, и удаляем с глаз долой окно предпросмотра со старым содержимым. А как быть с кнопкой Button 1 (с заголовком Остановить) на форме нредпросмотра? Для ее обработчика нам и понадобился флаг FiagE:

procedure TForm4.ButtonlClick(Sender: TObject); begin

if Buttonl.Caption=’Остановить" then begin

FlagE:=l; (если нажали на кнопку при загрузке – остановим/ exit; end;

if Buttonl.Caption^’Продолжить’ then begin

FlagE:=0; (продолжим) imagepreview; (загружаем картинки) exit; end;

if Buttonl.Caption=1 Закрыть’ then Form4.Close; [закрываем форму Preview) end;

Но это еще не все — минимизацию мы запретили, а кто будет восстанавливать? Для этого создадим такой обработчик события onciose формы:

procedure TForrrH .FormClose (Sender: TObject; var Action: TCloseAction) ; begin

with Forml do begin

MinFlag:=CheckBox3.Checked;

{восстанавливаем состояние мннимизашт) mayClose:= not CheckBox2.Checked;

(восстанавливаем состояние при закрытии) ButtonP.Enabled:=True; (восстанавливаем кнопку Предпросмотр] end; end;

Результат загрузки в окно предпросмотра папки с фотографиями из домашнего альбома автора показан на рис. 15.3.

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

Рис. 15.3. Результат вызова окна предпросмотра программы SlideShow

Осталось два заключительных штриха — во-первых, обещанный переход от "превьюшки" к изображению. Для этого создадим обработчик события onMouseDown ДЛЯ компонента Imagel формы Form4:

prooedure TForml.ImagelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin (вычисляем номер "кликнутой" картинки} if not FlagDblClick than exit; (если это простой щелчок – на выход; FlagDblClick:=False;

If ButtonombLeft then exit; (если кнопка не левая – на выход) n:=»(x Div 70)+ (Imagel.Width div 70)* (у div 70); (так вычисляется номер "кликнутой" картинки

по координатам в рамках Imagel) if n>Forml.Filelist.Count-1 then exit;

{если щелкнули за последней картинкой} if Buttonl.Capti0n=’0cTaH0BHTb’ then

FlagE:=l; (если нажали на кнопку при загрузке — остановим ее)

Loadfile;

Form’l .Close; /закрываем форму Preview) end;

Так как одинарным щелчком пользоваться для перехода неудобно, то мы ввели здесь флаг FiagDbici ick, который объявлен в секции var этой же формы следующим образом:

FlagDblClick:boolean=False;

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

procedure TForm4.ImagelDblClick(Sender: TObject); begin Iудостоверяем, что DblClick произошло)

FlagDblClick:=T rue; end;

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

А второй "последний" штрих — обеспечение поддержки колесика мыши при прокрутке окна с "превыошками", как мы говорили в главе 13. компонент ScrollBox поддерживает колесико через пень-колоду (если вообще поддерживает). Тут, по счастью, у нас все готово: добавим в секции private модуля preview.pas процедуру:

procedure WndProc(var Msg: TMsg; var Handled:boolean);

В обработчике события onCreate формы Form4 добавим строку.

Application.OnKessage:=WndProc; {перехват оконной процедуры)

И, наконец, скопируем один к одному саму процедуру из главы 13, заменив только Forml на Form4 и удалив то, что относится к горизонтальной прокрутке:

procedure TForm4.WndProc(var Msg: TMsg; var Handled:boolean); begin

if Msg.message=WM_MOUSEWHEEL then begin

if (Msg.wParam and MK_CONTROL) = 0 then begin

if Msg.wParam>0 then lкрутим вниз/ ScrollBoxl.VertScro HBar.Position: =

ScrollBoxl.VertScrollBar.Position+8

else /крутим вверх}

ScrollBoxl. VertScrollBar. Positions

ScrollBoxl.VertScrollBar.Position-8;

end; end; end;

В следующей главе мы сделаем справку, и на этом, как и обещали, с данной программой закончим. Хотя в принципе ее еще можно совершенствовать и совершенствовать— чего бы, например, не сделать но щелчку правой кнопкой мыши в окне предпросмотра всплывающее меню с информацией о картинке? Или не ввести возможность масштабировать картинки в основном окне, чтобы разглядеть детали? Или не создавать файлы предпросмотра для ускорения загрузки — как это делают все продвинутые программы подобного рода3? Или не обеспечить различные эффекты при переходе от одного слайда к другому? Или, наконец, читатель наверняка недоумевает, почему я тут не запрограммировал характерную для подобных приложений панель инструментов в виде кнопок? Из всего сказанного вы можете сделать вывод, почему программы обычно не делают сразу "как надо", а выпускают все новые и новые версии — кушать-то хочется все время, а дорабатывать программу можно до бесконечности. Главное гут— вовремя остановиться и не навязывать пользователю ненужную ему функциональность. И, конечно, обязательно в новой версии исправлять старые ошибки и недоработки так, чтобы количество новых ошибок не превысило разумного предела.

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии