Главная » Delphi » Полируем почти до блеска

0

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

Если бы мы использовали компонент Gauge (см. главу 13), то проценты у нас уже были бы готовыми. Но и тут вычислить их очень просто: объявим в процедуре SearchFile переменную рх типа integer (дополнительно к уже имеющейся переменной i) и вставим в середину цикла, где у нас стоит вызов процедуры Forml.ProgressBarl.Steplt, следующие строки:

px:=round(nall*100/ncount);

Application.Title:= ‘Trace ‘+IntToStr!px)+’%';

Forml.Caption;=’Поиск файлов ‘+IntToStr(px)+ ‘% выполнено';

Application.ProcessMessages; {чтобы все прокрутилось I

По окончании цикла добавим следующую строку:

Application.Title:= ‘Trace: 100% выполнено';

При изменении заголовка формы мы номер версии в заголовке "1.10" потеряем, но это не страшно: так даже красивее. Вот вопрос: когда именно следует восстанавливать "испорченный" заголовок приложения, который у нас в начале есть просто "Trace"? Неплохо бы сделать так, чтобы пока программа свернута, в заголовке не убиралась надпись "100% выполнено" после окончания поиска. А когда же надо ее убирать? Проще всего осуществить это по событию onPaint, которое обязательно возникнет при разворачивании окна — если программа закончила поиск, то заголовок вернется к нормальному виду, если нет— при сворачивании в первом же цикле нарисуется с процентами заново:

prooedure ТForml.FormPaint(Sender: TObject); begin

Application.Title := ‘Trace'; end;

Вторая проблема — с повторным звпуском. Отметим, что для предотвращения повторного запуска старый способ простого сравнения строк заголовков в данном случае уже не годится — придется анализировать строку на наличие в ней записи "Trace". Я специально отложил решение вопроса до этого момента: предотвращение повторного запуска тут важная вещь, т. к. мы работаем с временным файлом определенного названия, могут быть всякие коллизии (чтобы их не было, достаточно присваивать ftemphtm случайное имя, но лучше все же предотвратить повторный запуск). Вот как может выглядеть эта процедура (при попытке повторного запуска будем пробовать распахнуть запущенное приложение):

dWin:= GetWindow(Application.Handle, GW_HWNDFIRST);

while dWin <> 0 do

begin

if (dWin <> Application.Handle) and

{собственное окно игнорируем) (GetWindow(dWin, GW_OWNER) = 0) and {дочерние окна игнорируем) (GetWindowText(dWin, pbuff, sizeof(pbuff)) <> 0) {без названия игнорируем)

then begin

GetWindowText(dWin, pbuff, sizeof(pbuff));

{получаем текст названия приложения) st:=string(pbuff); {переводим его в строку) if pos(‘Trace’,st)<>0 then begin

ShowWindow(dWin,SW_SHOMNORMAL); BringWindowToTop(dWin);

{выводим первое приложение вперед) exit; {прерываем программу I end; end;

dWin:= GetWindow(dWin, GW_HWNDNEXT);

(ищем следующее приложение из списка}

end;

Разумеется, нужно объявить соответствующие переменные (см. проект SlideShow в главе 4) и внести модуль Windows в перечень модулей. Кстати, тут именно перебор файлов (а не просто вызов Findwindow с нужным заголовком) полностью оправдан — мы не знаем заранее, как будет выглядеть заголовок.

Третья задача вот какая: особенностью нашей программы является то, что здесь мы не задаем маску имени файла, ограничившись общим поиском, что. вообще говоря, неправильно. Мы отфильтровываем ненужные файлы, а нельзя ли, наоборот, еще и задавать тип файла, как это положено во всех таких поисковых программах? На самом деле нам ничего не стоит сделать это, вводя маску файла прямо в строке задания начальной папки. Чтобы сделать это удобным для пользователя, надо предусмотреть возможность задания как- просто пути к папке, так и пути с именем файла, а также постараться сохранить однажды введенную маску файла в неприкосновенности, чтобы не приходилось при смене папки каждый раз ее вводить заново. Дальше нам остается только проводить анализ строки — если имя файла пусто, то мы ищем все файлы, как и раньше. Введем специальную переменную stMask:string и добавим такой текст в начало поиска после строки if stpath=” then exit:

stMask:=ExtractFileName(stpath); if (stMasko”) and (pos{‘*’,stMask)<>0) then (имя файла может быть только со звездочкой) begin stMask:=’\’+stMask; stpath:=ExtractFilePath(stpath); end

else stMask:=’\*';

To есть мы разделяем имя файла и путь к папке, причем знак "обратный слеш" приписываем к имени (в следующем операторе он у нас удалится, см. код в предыдущей главе). Здесь имя файла может быть только со звездочкой, т. е. представлять собой маску — более подробное пояснение этого ограничения см. далее. Теперь осталось во всех вызовах FindFirst ввести именно это имя. Удаляем в цикле строку stsearch: = ‘\*';, а следующую строку записываем так:

stsearch:=stpath+stMask; {строка для поиска файлов)

Теперь перейдем к процедуре GetTreeDirs и вместо выражения

FindFirst(Path+’Y”,faAnyFile, sr)

запишем FindFirst(Path+stMask,faAnyFile,sr),

Наконец, в процедуре подсчета файлов Calculatefiles также заменим * \** на stMask, а процедуру вызова диалога установки папки перепишем так:

procedure TForrol.ButtonlClick(Sender: TObject); begin

stMask:=ExtractFileName(Editl.Text); stpath:=Edi 11.Text;

if (stMasko") and (pos (‘*’, stMask) <>0)

then stMask:=’\’+stMask else stMask:=";

[имя файла злесь может быть только со звездочкой!

ChDir(ExtractFilePath(Editl.Text)); {устанавливаем текущею

директорию и вызываем диалог установки директорш-!:} if SelectDirectory(stpath,[],0) then begin

if stpath[length(stpath)] = ‘\’ then delete{stpath,length(stpath),1);

Editl,Text:=stpath+stMask; end;

Edi.t2.SetFocus; {фокус в Edit2! end;

Заметки на полях

Имя файла здесь должно обязательно содержать звездочку (т. е. представлять собой маску) потому, что мы имеем дело не с именами настоящих (существующих на диске) файлов, а асего лишь со строками. А в строке отличить, например, имя файла "vasya.txt" от имени папки "vasya.txt" невозможно — папки могут иметь также имена с расширением. И поэтому функция ExtractFileName просто тупо читает все, что после последнего "обратного слеша", будучи примененной к строке "C:\DOK", она еернет имя несуществующего файла "DOK". Если бы мы имели дело с существующим дисковым файлом или папкой, то отличить их можно было бы по атрибутам — как при чтении, твк и при записи — то, что содержится в параметре Name структуры TSearchFile, никаких разночтений не допускает. Но есть и еще одно обстоятельство: если функция ExtractFilePath всегда возвращает путь со знаком "обратный слеш" на конце (ее можно даже использовать вместо действия "+’V"), то диалог открытия файла почему-то делает это только для имени диска ("С:\" — видимо, чтобы выглядело красивее), а папки всегда аозвращаются без концевого "слеша". Из-за всей этой путаницы и пришлось городить сложные условия.

После всех этих установок у нас имя папки и в Labels всегда (кроме первого запуска с установками по умолчанию) будет демонстрироваться без "обратного слеша" иа конце, а, например, для "голого" диска С: это действительно некрасиво, потому мы добавим его туда искусственно:

Forml.Label2.Caption:=stpath+’\’.

Кстати, теперь в программе есть одна недоработка, которую я оставляю вам в качестве домашнего задания. После того как мы введем в строке "Искать где" маску файлов, легко сообразить, что произойдет с ProgressBar. Предположим, в папке 1000 файлов, но только 10 из них с заданным нами расширением. Программа просмотрит только эти 10 файлов и поиск будет окончен, однако ползунок при этом вообще с места не успеет сдвинуться. Подумайте, как можно устранить этот недостаток?

Наконец, четвертая проблема, которую нам нужно решить— как помочь пользователю перейти к найденному файлу? Если ему придется запоминать результаты в уме, то вся наша работа обессмысливается. Для решения этой проблемы мы сделаем две вещи: во-первых, мы позволим сохранять полученный HTML-файл отдельно. Это просто: поставим на форму диалог сохранения SaveDialcgl, установим у него свойство Filter В Файлы HTMLI*.htir.; * .html и напишем отдельную процедуру:

procedure Savehtmlfile; var htmlname:string;

fhtm:TextFile; begin (запоминаем результаты поиска) with Forml do begin

SaveDialogl.InitialDir:=ExtractFileDir(Application.ExeName); htmlname:=Edit2.Text+’.htm'; {имя = равно строке поиска) SaveDialogl.FileName:=htmlname; if SaveDialogl.Execute then begin

assignfile(fhtm,ftempname); {имя временного файлаI Rename(fhtm,SaveDialogl.FileName); {создаем копию} end; end; end;

Вставим ее вызов в конец основной процедуры searchFile:

if nfile>0 then {если наши больше одного) if Application.MessageBox(‘Сохранить результаты поиска?’,

",mb_OKCANCEL) = idOK then Savehtmlfile;

Заметки на полях

В данном случае мы переименовываем наш временный файл ftempname, потому что в начале поиске мы все равно его создадим заново. Если в подобных случаях возникает задача скопировать файл под новым именем, то удобнее всего воспользоваться API-функцией копирования по такому шаблону:

CopyFi1е(pcha г(fname),pcha г(fnewname), T rue).

Если последний параметр рваен True, как а этом примере, то функция аозара- тит ошибку при встрече уже существующего файла, иначе она его перепишет заноао.

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

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

По теме:

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