動作確認
C++ Builder XE4
RAD Studio 10.2 Tokyo Update 2 (追記: 2017/12/28)
処理の進捗を表すプログレスバー。キャンセルボタン付きのダイアログで使われる。
PDFExport時のビルトインプログレス表示にてキャンセルボタンの認識ができそうにないので、自作することにした。
関連 http://qiita.com/7of9/items/5a12f5c482bfba552383
code v0.1
ProgressUnit
以下のコンポーネントを持つ。
- L_message
- ProgressBar1
- B_cancel
APIはProgressUnit.hを参照。
コードは以下。
ProgressUnit.dfm
object FormProgress: TFormProgress
Left = 0
Top = 0
BorderStyle = bsDialog
Caption = 'Progress'
ClientHeight = 180
ClientWidth = 378
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object L_message: TLabel
Left = 16
Top = 16
Width = 345
Height = 41
AutoSize = False
Caption = 'L_message'
end
object ProgressBar1: TProgressBar
Left = 16
Top = 80
Width = 345
Height = 25
TabOrder = 0
end
object B_cancel: TButton
Left = 152
Top = 128
Width = 75
Height = 25
Caption = 'Cancel'
TabOrder = 1
OnClick = B_cancelClick
end
end
ProgressUnit.h
//---------------------------------------------------------------------------
#ifndef ProgressUnitH
#define ProgressUnitH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ComCtrls.hpp>
//---------------------------------------------------------------------------
class TFormProgress : public TForm
{
__published: // IDE で管理されるコンポーネント
TLabel *L_message;
TProgressBar *ProgressBar1;
TButton *B_cancel;
void __fastcall B_cancelClick(TObject *Sender);
void __fastcall FormShow(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
void __fastcall SetMessage(String message);
void __fastcall SetMaxPosition(int value);
void __fastcall SetCurrentPosition(int value);
void __fastcall SetCaption(String caption);
__fastcall TFormProgress(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TFormProgress *FormProgress;
//---------------------------------------------------------------------------
#endif
ProgressUnit.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "ProgressUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFormProgress *FormProgress;
//---------------------------------------------------------------------------
/*
v0.1 2016/08/28
- SetCaption()追加
- B_cancelClick()追加
- FormShow()追加
- SetCurrentPosition()追加
- SetMaxPosition()追加
- SetMessage()追加
*/
__fastcall TFormProgress::TFormProgress(TComponent* Owner)
: TForm(Owner)
{
ProgressBar1->Min = 0;
ProgressBar1->MarqueeInterval = 1;
}
//---------------------------------------------------------------------------
void __fastcall TFormProgress::SetMessage(String message)
{
L_message->Caption = message;
}
void __fastcall TFormProgress::SetMaxPosition(int value)
{
ProgressBar1->Max = value;
}
void __fastcall TFormProgress::SetCurrentPosition(int value)
{
ProgressBar1->Position = value;
}
void __fastcall TFormProgress::SetCaption(String caption)
{
this->Caption = caption;
}
//---------------------------------------------------------------------------
void __fastcall TFormProgress::FormShow(TObject *Sender)
{
this->ModalResult = mrNone;
}
void __fastcall TFormProgress::B_cancelClick(TObject *Sender)
{
this->ModalResult = mrCancel;
Close();
}
Unit1
ProgressUnitを使う例。
以下のコンポーネントを持つ。
- TTimer型のTimerProgress
- Button1
Unit1.dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 300
ClientWidth = 404
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 168
Top = 208
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object TimerProgress: TTimer
Enabled = False
OnTimer = TimerProgressTimer
Left = 336
Top = 24
end
end
Unit1.h
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE で管理されるコンポーネント
TButton *Button1;
TTimer *TimerProgress;
void __fastcall TimerProgressTimer(TObject *Sender);
void __fastcall Button1Click(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "ProgressUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
static const int kMaxPos = 200;
static const int kDivision = 10;
static int s_loop = 0;
void __fastcall TForm1::TimerProgressTimer(TObject *Sender)
{
if (s_loop == kDivision) {
TimerProgress->Enabled = false;
FormProgress->Close();
return;
}
if (FormProgress->ModalResult == mrCancel) {
TimerProgress->Enabled = false;
s_loop = 0;
ShowMessage(L"Canceled");
return;
}
int pos = (s_loop + 1) * kMaxPos / kDivision;
FormProgress->SetCurrentPosition(pos);
s_loop++;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
FormProgress->SetMessage(L"Exporting to PDF files...");
FormProgress->SetMaxPosition(kMaxPos);
FormProgress->SetCurrentPosition(0);
FormProgress->SetCaption(L"PDF Export");
FormProgress->Show();
TimerProgress->Enabled = true;
}
//---------------------------------------------------------------------------
実行例
if (FormProgress->ModalResult == mrCancel) {
にてキャンセルされたか判断する。
検討事項
100%のクローズ
プログレスが100%になった時にProgressUnitをクローズするのは以下の2つの方法がある。
- 使用者(Unit1)がクローズする
- ProgressUnit自身がクローズする
2の方法を取る場合、以下のような検討事項が発生する。
- SetCurrentPosition()でクローズ処理を含めるのか
- クローズ前の100%の表示時間の保持
- クローズ時のdelay処理の挿入
- AutoCloseフラグの検討: trueの時はProgressUnit自身がクローズする、など。
Progressを使用するもの
Timerを使ったが、本来はTThreadで使おうとしている。
Timer実装はもっと良い実装がありそう。
最初はButtonClick()時のイベントでループをまわしながらSetCurrentPosition()を繰り返す処理を試した。こちらは、プログレスバーの表示に関してはうまく表示されなかった。
Application->ProcessMessage(); Sleep(100);を入れても、変な場所のプログレスが表示される症状があった。
ボタンクリック時に長時間の処理をすること自体があやまりなので、この変な振る舞いについては追求しない。