Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4433.html
 

Индикатор загрузки/ожидания

Опубликовано 10.08.2010 г. 22:38

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

Итак поставим такую задачу: приложения при событии (нажатие на кнопку) проводит копирование заранее указанного файла в заданное место. Во время копирования пользователь видит подобный вращающийся кружок, и надпись с просьбой подождать. Для реализации тестового приложения создадим новый проект, с двумя формами. Первая форма будет главная, на которой будет размещаться волшебная кнопка запуска, вторая форма будет появляться, во время копирования, и отображать индикатор. Работу организуем следующим образом: при нажатии на кнопку, создается экземпляр формы индикатора ожидания, которая потом показывается в модальном виде. Работа по копированию файла, будет осуществляться в отдельном потоке, по окончанию копирования поток будет отправлять сообщение форме, уведомляя ее, что процесс копирования завершен, и форму следует закрыть. Класс потока опишем весьма просто: нам понадобится как и всегда переопределить метод execute(), а также сохранить дескриптор окна-индикатора.
    TWorkThread = class(TThread)
      public
        loadDialog : HWND;
        procedure Execute(); override;
    end;
Далее воспользовавшись волшебной кнопкой главной формы, мы создаем рабочий поток и запускаем форму-индикатор (TLoadDialog).
procedure TMainForm.Button1Click(Sender: TObject);
var loadDialog : TLoadDialog;
    workThread : TWorkThread;
begin
    loadDialog := TLoadDialog.create(nil);

    workThread := TWorkThread.Create(true);
    workThread.loadDialog := loadDialog.Handle;
    workThread.Start();

    loadDialog.ShowModal();
    loadDialog.Free;
end;
Теперь перейдем к работе потока. Все действия по копированию мы внесем в блок try, с тем чтобы при неудаче копирования закрыть форму в блоке finally. Копирование файла будем осуществлять с помощью объекта IFileOperation, который уже упоминался в одной из предыдущих статей статей
procedure TWorkThread.Execute;
const SOURCE_FILE_NAME = 'E:\_video2\тренер Картер(2005).avi';
      DEST_FILE_NAME = '1.avi';
var ifo : IFileOperation;
    s,d : IShellItem;
begin
    try
        CoInitialize(nil);

        ifo := createComObject(CLSID_FileOperation) as IFileOperation;

        shCreateItemFromParsingName(SOURCE_FILE_NAME, nil, IID_IShellItem, s);
        SHCreateItemFromParsingName(pChar('d:\'),     nil, IID_IShellItem, d);

        ifo.SetOperationFlags(FOF_SILENT);
        ifo.CopyItem(s, d, pChar(DEST_FILE_NAME), nil);
        ifo.PerformOperations;

        ifo := nil;

        CoUninitialize;
    finally
        SendMessage(loadDialog, WM_CLOSE, 0, 0);
    end;
end;
Имена файлов обозначены константами, из которых нам потребуется получить объекты ISHellItem. Далее нам потребуется установить флаг FOF_SILENT объекту IFileOperation чтобы не отображать диалог копирования, задать операцию с помощью вызова процедуры CopyItems и вызвать метод PerfomOperations для запуска копирования. Отмечу, что при работе с COM объектами в потоке вам потребуется вызывать методы CoInitialize & CoUnitialize. По окончании копирования, нашему диалогу-индикатору мы с помощью метода SendMessage отправим сообщение WM_CLOSE. Теперь о главном (: как нарисовать окно-индикатор? Для начала найдем подходящую нам картинку с нужным нам кружочком и добавим ее в ресурсы нашего приложения, например с именем (loadBrush). В моем случае картинка была такая (65x65):
 
Заменим канву формы на TDirec2DCanvas, добавим в описание формы растровую кисть, и целочисленную переменную - угол поворота. Так же на форму добавим таймер, с интервалом 100 мс. Границу окна установим в bsNone. При создании формы создадим нашу канву, загрузим картинку из ресурсов, и создадим на ее основе кисть ID2D1BitmapBrush. Как это делать подробнее я уже писал ранее. Однако для целостности картины исходный код добавлю:
procedure TLoadDialog.FormCreate(Sender: TObject);
var bm : TBitmap;
    bitmap : ID2D1Bitmap;
    imgBrushProperties : TD2D1BitmapBrushProperties;
begin
    FCanvas := TDirect2DCanvas.Create(handle);
    with FCanvas do begin
        RenderTarget.SetDpi(96,96);
        Brush.Color := clBlack;
        Font.Color := clWhite;
        Font.Size := 14;
    end;

    bm := TBitmap.Create;
    bm.LoadFromResourceName(HInstance,'LoadBrush');
    bitmap := canvas.CreateBitmap(bm);
    bm.free;

    imgBrushProperties.extendModeX := D2D1_EXTEND_MODE_CLAMP;
    imgBrushProperties.extendModeY := D2D1_EXTEND_MODE_CLAMP;
    imgBrushProperties.interpolationMode := 0;
    canvas.renderTarget.CreateBitmapBrush(Bitmap, @imgBrushProperties, nil, loadBrush);
end;
Таймер мы добавили с той целью, чтобы перерисовывать картинку, т.е поворачивать наш кружок (: Поэтому событие таймера будет такое:
procedure TLoadDialog.PaintTimerTimer(Sender: TObject);
begin
    inc(angle,15);
    RePaint();
end;
почему именно 15 градусов? потому что четверть кружка разбита на 6 частей. Так что чтобы при повороте риски на круге занимали место предыдущей, потребуется поворот на 15 градусов (90/6). Далее осталось реализовать метод рисования. Обработчик события onPaint будет выглядеть следующим образом:
procedure TLoadDialog.FormPaint(Sender: TObject);
var m : TD2D1Matrix3x2F;
begin
    with canvas do begin
        BeginDraw;

        fillRect(GetClientRect());

        m := TD2DMatrix3x2F.Rotation(angle mod 360, Point(33, 33)) *  TD2DMatrix3x2F.Translation(10,10);
        renderTarget.SetTransform(m);
        renderTarget.FillRectangle(rect(0,0,65,65), loadBrush);


        renderTarget.SetTransform(TD2DMatrix3x2F.Identity());
        TextOut(90,30, 'Wait...');

        EndDraw;
    end;
end;
начало рисования будет проводить от точки (10,10). Также нам потребуется провести поворот нашего кружка на заданный угол, вокруг точки (33,33) - центр кружка. Чтобы этого добиться нам надо получить произведение матриц преобразования - поворота и переноса. После чего полученную матрицу m мы передаем как параметр метода SetTransform поверхности. После этого мы можем нарисовать наш кружок с помощью кисти LoadBrush. Восстановив преобразования координат в исходное состояние с помощью Identity матрицы можем вывести сопроводительный текст а-ля "Подождите..". И как итог форма выглядит следующим образом:

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

Комментарии

utmost
04.12.2010 в 21:21
Под XP работать не будет. Лучше использовать GIF или APNG
ter
09.12.2010 в 18:53
естественно не будет, д2д коли используется.
XeleX
23.12.2014 в 21:37
Здравствуйте, а вы можете выложить рабочий проект. Есть несколько вопросов, клык проще рассмотреть на примете. Спасибо
Александр
25.02.2015 в 13:46
Добрый день!!
Не могли бы Вы показать описание - TLoadDialog ( переменные и секция uses) ?
спасибо!
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно