Главная » Delphi » Составление списка вложенных папок

0

Перенесем, как всегда, проект из папки Glava 13\2 в новую папку (Glava 14\1). Список вложенных папок мы будем хранить в переменной типа TStringList— назовем ее CatDir и добавим в список глобальных переменных. Так как мы все равно будем перебирать все папки, то нам ничего не стоит заодно и пересчитывать файлы в них1. Расчет оставшегося времени мы, как и говорили, отменим, но количество файлов нам все равно понадобится. Для удобства мы введем специальную переменную ncount типа integer — обращаться каждый раз к Forml.ProgressBarl.Max — больно громоздкое занятие. Затем создадим такую процедуру:

prooedure GetTreeDirs(Root: string; OutCat: TStringList); lсоставление списка всех вложенных папокI var

i: integer; stx: string;

procedure InsDirs(stc: string; ind: Integer; Path: string; OutList:

TStringList) ; var (Создает список вложенных папокI

sr: TSearchRec; begin

if DirectoryExists(Path) then

if FindFirst(Path+’\*",faAnyFile,sr)=0 then begin repeat

if ((sr.Attr and faDirectory)<>0)

and (sr.Name[Length(sr.Name)]<>’.’)

then Iесли папка, но не вышележащая} begin

if ((sr.Attr and faKidden)=0) then Outlast.Insert(ind,sUc+sr.Name) (и не скрытая, то включаем) end else if (sr. Name [Length (sr.Name) jo’.’) then ncount:=ncount+l; /иначе дополняем количество файлов) until (FindNext(s г)<>0); FindClose(sr); end

end; (конец InsDirs) begin

nccunt:=0;

if not DirectoryExists(Root) then exit;

Forml.ProgressBarl.Max:=0;

(Создаем список каталогов первой вложенности/

InsDirs(Root+’V, OutCat.Count, Root, OutCat);

i: =0 ;

if OutCat.CountoO then (теперь для каждой вложенной папки)

repeat

stx := OutCat [ ij; (в stx ползаем путь к уже внесенной в список) InsDirs(stx+’,i+l.OutCat[ij, OutCat);

(вставляем нашейную сразу за данной директорией в списке) inc(i) ;

until (i=0utCat.Count);

OutCat.Insert(0,Root); (вносим корневуп папку)

Forml.ProgressBar1.Max:=ncount; end; (конец GetTreeDirs)

Остановимся на условиях, при которых мы здесь относим найденные имена к папкам и файлам. Требование, чтобы найденное имя файла или папки (sr.Name) не содержало в конце символа точки, означает, что мы исключаем из рассмотрения вышележащие папки (в MS-совместимых файловых системах это "." и ".."). Сложнее понять, откуда взялось условие (sr.Attr and faDirectory) <>0 — почему бы не поставить просто sr.Attr-faDirectory? Если мы сделаем по второму варианту, то мы упустим, например, папку Program Files — она имеет еще и атрибут "только для чтения", т. е. ее атрибут равен числу $11 (faDirectory + faReadOnly), а не $10 (просто faDirectory). А ВОТ скрытые папки ("корзину" и т. п.) нам желательно опустить, поэтому условие усложняется дальнейшими "наворотами". Зато скрытые файлы в общее количество войдут— хотя здесь это и не принципиально, если очень захочется, мы можем отсеять их уже при просмотре.

Чтобы обеспечить просмотр файлов во всех папках, которые содержатся в списке, придется заключить наш цикл просмотра в текущей папке внутрь еще одного цикла, для чего в процедуре Button2ciick. создадим локальную переменную i: integer. Не забудем также до начала цикла создавать экземпляр списка (catDiг.create), а после сразу его уничтожать (Catnir.Free). При следующем поиске список будет создаваться заново. Наконец, неплохо бы для большей информированности пользователя выводить конкретную папку, в которой в данный момент ведется просмотр, как это обычно делается в таких программах. Для этого у нас есть неиспользуемый теперь Label я — будем надеяться, что длины оставшейся части формы хватит для большинства реально встречающихся имен папок. Все, что относится к выводу времени в цикле, мы удалим, а переменную ttold используем для того, чтобы отображать прошедшее время в конце поиска.

Теперь надо еще обеспечить выбор — искать только в заданной папке, как и раньше, или по всему списку вложенных папок. Для этого установим два компонента RadioButton ниже полоски редактора Editi. Для того чтобы они были связаны только друг с другом, их следует установить на отдельную маленькую панель (Рапе12), на панель Panell в дальнейшем нам придется устанавливать еще такие же кнопки. Чтобы сделать панель Рапе12 невидимой на фоне Panell, следует у нее установить свойство ParentColor в True, а свойство BevelOuter в hvNone (оно вместе с Bevelinner определяет наличие и форму окантовки). В заголовке RadioButtonl запишем только в каталоге и сделаем ее отмеченной по умолчанию, а в заголовке RadioButton2 — включая подкаталоги. Шрифт для обеих кнопок сделаем "полужирный" Arial 8-го кегля. Если кому не терпится взглянуть на то, как это должно выглядеть, то окончательный внешний вид заголовка формы после всех изменений показан на рис. 14.1.

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

procedure Calculatefiles;

begin

ncount:=0;

if FindFirst(stpath+’\*’,S23,sf)=0 then

i $23 = не просматриваем системные файлы, тома и каталоги/ repeat ncourtt: =ncount+l;

until rindNext(sf)<>0; (пока файлы не закончатся} FindClose(sf); (конец поиска} Forml.ProgressBarl.Max:=ncount;

CatDir.Insert(0,stpath); (вносим единственный элемент в список} end;.

После всех этих изменений процедура поиска (Button2Ciick) будет выглядеть так (неизмененные места я пропускаю для сокращения записи):

..  (все, как было}

Forml.Label1.Сар^оп: = ‘…'; (заголовок Labell}

Forml.Label2.Caption:=”; (очисткли заголовок Label2) Application.ProcessMessages; (чтобы сразу сработало} CatDir:=TStringList.Create; (создаем список папок} if RadioButton2.Checked then (поиск во всех папках)

GetTreeDirs(stpath,CatDir) (пересчитываем папки и файлы) else Calculatefiles; (иначе только файлы, как раньше) nfile:=Q; nall:=Q; ttold:=Time;

ProgressBarl.Position:=0; (установили в 0} for i:=0 to CatDir.Count-x do begin stpath:=CatDir[i] ; stsearch: = ‘\*’ ;

stsearch:=stpath+stsearch; (строка для поиска файлов} if FindFirst(stsearch,$23, sf)=0 then

( $23 = не просматриваем системные файлы, тома и каталоги) repeat

Forml.Labell.Caption:=’ Всего файлов ‘ +IntToStr(ncount)+’ Просмотрено: ‘+IntToStr(nail)+ ‘ Найдено: ‘+IntToStr(nfile); Forml.Label2.Cap t i on:=s tpat h;

……….  (все, как было}

st: =1 ‘ UntToStr(sf.Size)+ ‘ bytes ‘ +

DateToStr(FileDateToDateTirae(sf.Time))+ ‘ кодировка:’+st;

……….  (все, как было)

until FindNext(sf)<>Q; (пока файлы не закончатся) end;

FindClose(sf); (конец поиска} CatDir.Free;

Forml.Labell.Caption:= ‘ Всего файлов ‘+IntToStr(ncount) +’Просмотрено: ‘-UntToStr (nail) +’ Найдено: 1+IntToStr(nfile);

Forml.Label2.Caption:=’Время поиска ‘ +FormatDateTime (‘ hh: пп: ss’, Time-ttold);      /асе, как бьшо)

Для большей информативности я здесь заодно добавил вывод размера и даты создания найденного файла (через строку st). Заметьте также, что для вывода времени мы использовали функцию FormatDateTime.

Если мы зададим теперь поиск в какой-то достаточно большой по объему папке, то увидим, что происходит он очень некрасиво — т. к. WebBrowser каждый заново перезагружает страницу, то он все время "моргает" линейками прокрутки и все это к тому же сильно тормозит процедуру— попробуйте исключить вызов процедуры Navigate из цикла вообще, и вы увидите, что она стала выполняться в несколько раз быстрее. Но не сидеть же перед пустым экраном в ожидании, пока поиск не закончится? Давайте ограничим вывод во времени. Для этого поместим на форму компонент Timer, зададим ему интервал, например, 3000, а свойство Enable установим в False. Строку webBrowserl.Navigate(Pchar(st)) перенесем из цикла в обработчик события onTimer, добавим к ней для надежности Application.ProcessMessages. Непосредственно перед началом цикла вставим строку Timerl. Enabled :=True, а сразу же по его окончании— Timerl.Enabled:=Faise. В самом цикле Application. ProcessMessages перенесем повыше, сразу после строки Forml. ProgressBarl. StepXt (иначе крутящиеся В Процедуре ReadFileFormat циклы будут тормозить событие таймера). Теперь у нас обновление содержимого браузера будет происходить раз в три секунды (примерно, потому что циклы будут тормозить так или иначе), а если вдруг все выполнится быстрее, то таймер не сработает ни разу, и содержимое браузера обновится единственный раз при вызове процедуры Navigate уже после окончания цикла (см. полный листинг в главе 13).

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

По теме:

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