2018.10.02

Lazarusでの日本語文字コード処理その2

先日の記事について追加情報です。
先日の記事ではCharEncStreamが一番のような表現をしていましたが、この関数とCP932ToUTF8には重大な欠陥があることが判りました。
 
実はこれらの関数では「半角カタカナ」を処理出来ないため、UTF-8からShift-JISコードに変換する時点で半角カタカナ文字が欠落してしまいます。
 
ここからテスト用の文字列です。
 
CharEncStreamとCP932ToUTF8では半角カタカナ文字コードが変換されずに欠落してしまう。
アイウエオカキクケコサシスセソ アイウエオカキクケコサシスセソ
 
CP932ToUTF8・・・半角カタカナが欠落したり文字化けしたりしています
Cp932toutf82
 
 
CharEncStream・・・同じです。
Charencstream2
 
 
WinCPToUTF8
Wincptoutf82
 
 
このように半角カタカナを含むShift-JISコードのテキストを変換する場合、半角カタカナも含めて正しく文字コード変換出来るのはWinCPToUTF8関数だけのようです。
 
 

| | コメント (0) | トラックバック (0)

2018.09.28

Lazarus用のレポートコンポーネントについて

過去にDelphiで作成した計測データから判定値とグラフをレポートにするアプリケーションを、今後のメンテナンスのためにLazarusに移植したのですが、レポートコンポーネントでちょっと苦労したもののうまく解決出来たので情報を共有しておきます(実は前の2つのLazarusの記事も、この作業の中で出てきた課題を解決した結果を書いています)。
 
もともとDelphi(7)ではレポート作成にLightReportコンポーネントを使用しており、その後Delphi XE2でDEKOさんのところのLightReport2コンポーネントに置換して使用していました。
一方、Lazarus1.8でのレポート作成用コンポーネントは「パッケージをインストールもしくはアンインストール」で表示されるLazReportがあるのですが、これが使いづらくて(と言いますか、使い方がよくわからない)、LightReport2からの置換が出来ませんでした。そこで何か良いレポート作成コンポーネントがないかと探したところ、Fortes Reportコンポーネントを見つけました。
このコンポーネントはLazarusだけではなくDelphiにも対応しています。配布アーカイブを解凍したフォルダ内のpackagesフォルダ内にあるfrce.lpkがLazarus用、frce.dpkがDelphi用です(Delphiでの動作確認は未実施)。
 
このFortes Reportの各コンポーネントを用いることで、今回の場合に置いてはLightReport2の必要な全てのコンポーネントを置換することが出来ました。また、レポートのデザインもLightReport2と同じ感覚で行うことが出来ますので、LazarusでLightReport2と同じようなことを実現したい方には良いのではと思います。
 
レポートデザインの様子
Designwd
 
動作状態
Sample
 
 
プロジェクトサンプル
「fortesreport.zip」をダウンロード
 
 

| | コメント (0) | トラックバック (0)

2018.09.26

Lazarusでの日本語文字コード処理について

Delphiでは文字列の入力の際に文字コードの違いを自動的に吸収(変換)してくれるのですが、Lazarusでは文字列はUTF-8前提での処理を行うため、日本語を扱う際には多少の工夫が必要になってきます。
例えば、Windows上で蓄積されてきたテキストファイルはまだまだShift-JISコードで保存されたのものが多いと思いますが、残念ながら文字コードがUTF-8前提となっているLazarusでこれらのファイルを普通に読み込むと当然のことながら文字化けを起こします。
私も実際に保存された測定データをLazarusで読み込む処理を書いたのですが、ちょっとハマってしまったので整理してみます。
 
ネット上にはShift-JISコードの文字列は、AnsiToUTF8またはSysToUTF8でUTF-8コードに変換するとの情報があるのですが、実際に試してみると現在のLazarusではShift-JISコードをUTF-8コードに変換してくれません。
 
AnsiToUTF8
Ansitoutf8
 
SysToUTF8
Systoutf8
 
そこで更に情報を探したところ2つの方法を見つけました。ひとつめはLazUTF8ユニットにあるWinCPToUTF8、もうひとつはLConvEncodingユニットにあるCP932ToUTF8です。どちらを使用しても正常にShift-JISからUTF-8にコード変換してくれます。
 
WinCPToUTF8
Wincptoutf8
 
CP932ToUTF8
Cp932toutf8
 
更に、https://torum.jp/2018/01/27/lazarus_unicode-loading-saving-utf-16-files/でCharEncStreamユニットの情報を見つけました。
このユニットを使用すると、読み込んだファイルの文字コードを自動的にUTF-8変換してくれます。Shift-JIS, Unicode(UTF-16) Big Endian/Little Endianは文字コードを自動変換してくれますし、もちろんUTF-8/UTF-8Nも読み込めます。残念ながらEUC-JPやJIS78/83コードは認識してくれませんが、現状これらの文字コードにお目にかかることはほとんどないと思いますので、機能としては十分かと思います。

CharEncStream
Charencstream
 
 

unit Encunit;
 
{$mode objfpc}{$H+}
 
interface
 
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
StdCtrls, LConvEncoding, LazUTF8, CharEncStreams;
 
type
 
{ TForm1 }

TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
OpenDialog1: TOpenDialog;
Panel1: TPanel;
ConvertBy: TRadioGroup;
procedure Button1Click(Sender: TObject);
private
 
public
 
end;
 
var
Form1: TForm1;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
procedure TForm1.Button1Click(Sender: TObject);
var
line: TStringList;
enc: TCharEncStream;
begin
with OpenDialog1 do
if Execute then
begin
if ConvertBy.ItemIndex = 4 then
begin
// CharEncStreamを使用する
enc := TCharEncStream.Create; // CharEncStreams
try
enc.LoadFromFile(FileName);
Memo1.Lines.Text := enc.UTF8Text;
finally
enc.Free;
end;
end else begin
line := TStringList.Create;
line.LoadFromFile(FileName);
try
case ConvertBy.ItemIndex of
0: Memo1.Lines.Text := AnsiToUTF8(line.Text); // System
1: Memo1.Lines.Text := SysToUTF8(line.Text); // LazUTF8
2: Memo1.Lines.Text := WinCPToUTF8(line.Text); // LazUTF8
3: Memo1.Lines.Text := CP932ToUTF8(line.Text); // LConvEncoding
end;
finally
line.Free;
end;
end;
end;
end;
 
end.


 
 
「lazcode.zip」をダウンロード
 
 


| | コメント (0) | トラックバック (0)

2018.09.25

Lazarusでの多言語化

Lazarusでの多言語化ですが、ネット上にであまり詳しい情報が見つけられませんでしたので、私が確認した結果を簡単にまとめで見ます。
 
結論から言うと、一部注意しなければならないところはあるものの、意外と簡単に多言語化を実装することが出来ます。
 
多言語化するプロジェクトのオプションダイアログの国際化にある「国際化を有効にする」をチェックし、「PO出力ディレクトリ」に言語翻訳定義ファイルであるPOファイルを保存するためのフォルダを指定します(ここではLanguageを指定)。
Projoption
 
あとは「.lfmを保存する際に~」をチェックしておけば、フォームを保存するタイミングでPOファイルがPO出力ディレクトリ内に自動的に作成されます。
ここで作成されるPOファイル名はプロジェクト名.POとなります。
 
次に、作成されたPOファイルをPOEdit等の編集アプリケーションを使用して他の言語に翻訳します(編集の仕方はネット上を検索してみて下さい)。
尚、準備したPOフォルダファイルはファイル名を「プロジェクト名.PO」から「プロジェクト名.言語名.PO」に変更する必要があります。
例えば、元の名前がProject1.POで翻訳言語が英語の場合には、Project1.en.POとします。
Poediten
  
プロジェクト多言語化のための翻訳ファイルの準備が整ったら、言語を切り替えるための処理をプログラムソース内に記述していきます。
 
手順は簡単で、
・uses節にLCLTranslator世ニットを追加する。
・言語切替イベント処理を追加する。
・必要に応じて、終了時に現在の言語情報を保存する処理や、起動時に言語情報を読み込んで切替える処理を追加する。
となります。
 
言語の切替はSetDefaultLang(言語(, POファイルフォルダ名(, 強制的に書き換えるか)))を使用します。尚、2つ目以降の引数は省略可能です。
 
実際に言語を切替える処理ですが、言語定義ファイルが英語でサブフォルダLanguageにある場合は、
SetDefaultLang('en', ExtractFilePath(Application.ExeName) + 'Language');
の一行で表示言語が英語に切り替わります。
 
以下は注意が必要な点です
 
多言語化したアプリケーションを一旦日本語以外の言語に切り替えた後に日本語に戻そうとして、
SetDefaultLang('ja', ExtractFilePath(Application.ExeName) + 'Language');
としたり、
SetDefaultLang('');
としても日本語には戻りません。

他の言語に切り替えた後に日本語に戻す場合には、アプリケーションを終了して再起動する必要があります。
または、どうしてもリアルタイムで日本語に戻したい場合は、日本語の言語定義ファイル(Project1.ja.PO)を準備して、
SetDefaultLang('ja', ExtractFilePath(Application.ExeName) + 'Language');
を実行するようにします。
POファイルから日本語の定義を作成するのは面倒ですが、POEditを使用する場合は翻訳元の項目を選択してCtrl+Bを押すと、ソーステキストが翻訳後にそのままコピーされるので効率よく作業できます。
とはいっても、翻訳元の情報量が多いと大変なので、その場合には日本語に切替えた際にはダイアログボックスで「日本語表示は再起動後に有効になります」などとメーッセージを表示するだけにしたほうが楽かもしれません。
Poeditja
 
  
プログラムソース中に埋め込まれた文字列はPOファイルに反映されず、言語切替の対象にも出来ません。
対応ですが、ソース中に埋め込む文字列はresourcestring節を追加して、

resourcestring
 Str001 = 'ほにゃらら';
 Str002 = 'ほげほげ';

などと宣言することで作成されるPOファイル内にこれらの定義が保存されますので、後はPOEdit等で翻訳・保存すれば言語切替の対象にすることが出来ます。
尚、これらの宣言はプログラムの中で
 
 Label1.Caption := Str001:
 ShowMessage(Str002);
 
などと通常の変数のように使用できます。
 
 
 
フォームに配置するコンポーネントでTStringsで格納されている文字列はPOファイルに反映されず、言語切替の対象にも出来ません。
対応ですが、上記と同様にコンポーネントに格納するTStringsをコンマテキスト形式でresourcestring内で宣言して、
 
FormCreate等のイベント内でコンポーネントに動的に追加することで、言語切替の対象にすることが出来ます。
 
resourcestring
 RadioGroup1Str = '一番,二番,三番';
 
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
 // 言語切替の初期化
 RadioGroup1.Items.CommaText := RadioGroup1Str;
end;
 
 
DelphiもCommunityEditionが公開されて、無償でほぼフル機能を使用することが出来るようになり、GnuGetTextの出番も無くなるかもしれませんが、Lazarusでも多少注意するところはあるものの、その辺りを克服すると手軽に多言語化が出来るということがわかりました。
自動的に作成されるPOファイルも、既に翻訳済みの部分を保持したまま変更点だけを更新してくれますので、メンテナンス性も高くて好感が持てます。
 
Lazarusも日本語の扱いがこなれつつあるとは言うものの、まだ文字コード処理には癖があるようですので、この辺りももう少し何とかなるといいなと思う次第です。

unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Menus,
  ExtCtrls, StdCtrls,
  LCLTranslator; // 言語切替ユニット
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Edit1: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    MainMenu1: TMainMenu;
    MenuItem1: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem4: TMenuItem;
    RadioGroup1: TRadioGroup;
    RadioGroup2: TRadioGroup;
    ToggleBox1: TToggleBox;
    ToggleBox2: TToggleBox;
    procedure FormCreate(Sender: TObject);
    procedure MenuItem4Click(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
    procedure ToggleBox1Change(Sender: TObject);
    procedure ToggleBox2Change(Sender: TObject);
  private
 
  public
 
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
// 文字列をそのままプログラムソース内に埋め込むとPOファイルに反映されないので
// resourcestringで文字列を定義して、プログラムソース内で使用する
resourcestring
  Str1 = 'これはプログラムソース中の文字列です。';
  // コンポーネント内にTStringsで格納されている文字列もPOファイルに反映されない
  // のでresourcestringで定義しておいて、コンポーネントに動的に反映させる
  // RadioGroup1はアイテムを静的に設定しているのでPOファイルに反映されず、言語
  // 切替で変更できない
  Str2 = '日本語(&J),英語(&E)';
 
 
procedure TForm1.MenuItem4Click(Sender: TObject);
begin
  Close;
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  // RadioGroup内のアイテムを動的に追加する
  RadioGroup2.Items.CommaText := Str2;
end;
 
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  inherited;
 
  case RadioGroup1.ItemIndex of           // サブフォルダLanguageを指定
    0: SetDefaultLang('ja', ExtractFilePath(Application.ExeName) + 'Language');
    1: SetDefaultLang('en', ExtractFilePath(Application.ExeName) + 'Language');
  end;
  RadioGroup2.Items.CommaText := Str2;
  RadioGroup2.ItemIndex := RadioGroup1.ItemIndex;
end;
 
procedure TForm1.ToggleBox1Change(Sender: TObject);
begin
  Edit1.Text := Str1;
end;
 
procedure TForm1.ToggleBox2Change(Sender: TObject);
begin
  ShowMessage(Str1);
end;
 
end.

「project1.zip」をダウンロード
 
 
 


| | コメント (0) | トラックバック (0)

2017.12.10

Lazarus 1.8がリリースされています

Lazarus 1.8正式版がリリースされています。

1.8RC版からのアップデート時にRC版での環境設定をそのまま引き継ごうとすると、追加でインストールしてあるパッケージで不具合が起こる可能性があるため、追加パッケージをアンインストールしてからアップデートするか、RC版を完全にアンインストールしてからインストールしたほうが良いかも知れません。

私の環境では、AncorDockingDsgn 0.5/sparta_DockedFormEditor 0.0が継承されているにも関わらずフォームがドッキングスタイルにならない問題と、indylaz 10.2.0.3がらみのコンポーネントパスが見つからない不具合が発生しました。

| | コメント (0) | トラックバック (0)

2017.09.27

Lazarus1.8RC4クロスコンパイルでの実行結果その2

Windows上からのX86_64-Linux版クロスコンパイルにてリンクの途中でエラー(Error while Linking)になる問題ですが、その後の調査でどうもリンク時にライブラリが不足しているために発生しているようで、プロジェクトソースに以下の指示を追加することでエラーを回避出来ることがわかりました(根本原因がはっきりしないので、すっきりはしないのですが)。

program Project1;
 
// ここから
{$ifdef Linux}
  {$ifdef FPC_CROSSCOMPILING}
    {$ifdef CPUARM}
      //if GUI, then uncomment
      //{$linklib GLESv2}
    {$endif}
    {$linklib libc_nonshared.a}
  {$endif}
{$endif}
// ここまでを追加する
 
uses
  Forms, tachartlazaruspkg, Interfaces,
  Unit1 in 'Unit1.pas' {Form1};

で、作成したx86_64-Linux版の実行ファイルをVirtualBox上のUbuntu64bit版で実行した結果です。
Ubuntu64
おーーーー!ちゃんと動きました。

残った課題はArm-Androidですが、VectPressさんのLazarusでAndroid開発環境を構築してみましたといった情報もありますので、無理して自力でやることもないのかも・・・

| | コメント (0) | トラックバック (0)

2017.09.23

Lazarus1.8RC4クロスコンパイルでの実行結果

クロスコンパイルに成功したi386-Linuxとarm-Linux用の実行ファイルが動作するかどうか確認しました。

尚、テスト用のプロジェクトは前回作成した正規乱数発生テスト用のものを用いいました(ソースコードは修正無しでコンパイル出来ました)。

i386-Linux・・・VirtualBox上のUbuntu32bit版で確認した結果、
Randtest
おーーーー!正常に動作するようです。

arm-Linux・・・Raspberry Pi3+Raspbian Pixelで確認した結果、
Randtestrasp
おーーーーーーー!こちらも正常に動作しました。

とりあえず、あとはx86_64-Linuxとarm-Androidがなんとかなるのかどうかです。今後の課題として、暇な時に取り組んでみたいと思います。

| | コメント (0) | トラックバック (0)

2017.09.20

Lazarus1.8RC4とクロスコンパイル環境の構築

Lazarus1.8のRC4がリリースされていたのでインストール。

Windows32/64両方に対応出来るよう、Lazarus自体は32bit版を選択して、追加インストールファイルであるlazarus-1.8.0RC4-fpc-3.0.4rc1-cross-x86_64-win64-win32.exeも合わせてダウンロードし、最初に本体をインストール後Win64クロス環境をインストール。

で、起動すると各フォームが独立したクラシックスタイルで表示されます。Lazarus1.7で折角モダンなスタイルになっていたので1.8も同じようなスタイルにしてみました。

インストール直後の状態
Lazarus18

Lazarusのパッケージメニューからパッケージのインストールもしくはアンインストールを選択して、表示されたダイアログの右側のリストからAnchorDockingDsgn 0.5とsparta_DockedFormEditor 0.0をダブルクリックしてインストール対象に登録し、保存してIDEを再構築ボタンを押してしばし待つと、Lazarusが再起動してモダンスタイルに変更されます。この時、sparta_DockedFormEditor 0.0も一緒に登録しないと、フォームデザイナだけ独立フォームとなって面倒なことになりますので(と言うかなった)、必ず同時に登録しましょう。

Lazarus18mdn


Lazarus1.7(trunk)ではi386-Linuxのクロスコンパイル環境が標準で含まれていましたが、1.8RC4では(といいますか、通常のWindows版では)含まれていませんので、クロスコンパイル環境の構築にも挑戦してみました。

ということで、Web上の情報を色々と探したのですが、Windows上でMinGWやCygwin環境を構築してコマンドラインでごにょごにょするとか敷居の高い方法やfpcupコマンドを使用する方法が見つかりましたが、中々うまく行かず悩んでいたところで、GUIで操作できるfpcupdeluxeというものを発見。このfpcupdeluxeも中々うまくいかなかったのですが、やっとなんとかなる方法が見つかりましたのでまとめておきます。

まずfpcupdeluxeのソースコードをgithubからダウンロードします。

適当なフォルダに解凍して、Lazarusでプロジェクトファイルfpcupdeluxe.lpiを開いて実行ファイルを作成します。
次にfpcupdeluxe用の作業フォルダを作成して(ここの例ではC:\freepascal-master)、その中にサブフォルダfpcsrcを作成します。
続いてLazarus1.8RC4で使用しているFreePascal3.0.4のソースコードを取得ためにSubversion(SVN)クライアントを準備するのですが、ここではちょっと調べたところで使いやすそうだったTortoiseSVNをインストールして使用します。
https://ja.osdn.net/projects/tortoisesvn/から自分の環境にあったものをダウンロードしてインストールするとエクスプローラのコンテキストメニューからSVNの操作が出来るようになります。

エクスプローラで先程準備したC:\freepascal-master\fpcsrcを選択して右クリックし、コンテキストメニューからSVN Checkout...を実行すると以下のダイアログが表示されます。
Svncheckput

Tortoisesvn

ここでfpcupdeluxeをインストールしたフォルダ内のサブフォルダdeluxebinにあるfpcup.iniを開き、[ALIASfpcURL]タグ内にある3.0.4=の右側のアドレスをコピーして、このダイアログのURL of repository:に貼り付ける。あとはOKボタンを押せば、3.0.4のソースコードが全てフォルダ内にダウンロード出来ます。

ダウンロードが完了したら、fcpupdeluxe-xxx-xxx.exeを実行します(xxx-xxxはWindows32か64かで変わる)。
fpcupdeluxeが起動したら、まずは画面中央下の方にあるSetup+ボタンを押して設定ダイアログを開き、Include LCL with cross compiler (default=no)をONにして閉じます。
Fpcupsetting

次に左側のFPC Versionから3.0.4を選択、Lazarus versionからfixes1.8を選択します。
後は、作成したいクロスコンパイル環境に応じてCPUとOSを選択して、Install cross-compilerボタンを押せば必要なファイルの作成が始まるので、あとは終了のを待ちます・・・
Fpcupdelux

作成が終了すると、C:\freepascla-master\cross\binフォルダ内にcpu-os名のフォルダ(i386-linux等が出来ていますので、その中のファイル全てをLazarusをインストールしたフォルダ内のFreePascalフォルダ(例えばC:\Lazarusにインストールした場合は、C:\Lazarus\fpc\3.0.4\bin\i386-win32フォルダにコピーします。
尚、arm-linuxの場合にはファイル名を全てarm-linux-gnueabihf-xxxx.exeからgnueabihfを削除してarm-linux-xxxx.exeに変更する必要があります。
尚、一回の作業では必要なunitが作成されない場合がある(C:\freepascal-master\fpc\unit内にcpu-os名のフォルダとファイルがなければ作成されていない)ので、その場合はfpcupdeluxeで再度Install cross-compilerを実行すれば作成されると思います。
続いて、C:\freepascal-master\fpc\bin\i386-win32フォルダ内に作成されたppcrossXXX.exeも同様に上記フォルダにコピーします。
続いて、C:\freepascal-master\fpc\fpmkinstフォルダとC:\freepascal-master\fpc\unit内に作成されたcpu-osフォルダをそれぞれC:\lazarus\fpc\3.0.4\fpmkinstフォルダとC:\lazarus\fpc\3.0.4\unitsにコピーすればクロスコンパイル環境が出来上がります。

あとはLazarusのプロジェクトオプションの設定と対象で対象とするOSと対象とするCPUファミリーを選択すればそれらターゲット環境用の実行ファイルを作成出来るようになります。


この方法で試してみたのは以下の通りです。

i386-linux・・・環境作成OK.コンパイル・リンクは正常終了で多分OK
x86_64-linux・・・環境作成OK.リンクの途中でエラーとなるが原因は不明
arm-linux・・・環境作成OK.コンパイル・リンクは正常終了で多分OK
arm-android・・・環境作成の途中でエラーとなり、fpmkinstやunitの作成が出来ずに停止

多分OKというのは、実際にそれらの環境で実行出来るか確認出来ていないからです。そのうち暇があれば確認しようと思います。

| | コメント (0) | トラックバック (0)

2017.09.09

Delphi/Lazarusでの正規分布乱数の発生

正規分布乱数を発生させるためにはどのような方法がベストなのかDelphiで試してみました。
比較したアルゴリズムはボックスミュラー法と中心極限法理による正規分布乱数生成にDelphi標準関数のRandGを加えた3種類で、生成元の一様乱数はDelphi標準のRandomとメルセンヌ・ツイスターアルゴリズムを用いたDelphi用MT19937ライブラリを用いて見ました。

テスト方法はそれぞれ任意の正規分布乱数をグラフに表示させての”見た感じ”比較と一応定量的な評価として±1σ,±2σ,±3σそれぞれの確立分布と実際の分布数を比較して、その差をパーセント表示させて誤差の大きさを見ることにしました。
とここまでは良かったのですが、何とDelphi 10.1/10.2 StarterエディションにはTeeChartが付属していないためグラグ表示が出来ないことが判明。仕方がないので、一度はDelphi XE2 Pro版でテストプログラムを作成したものの、Starterしか持ってないから確認出来ない!という方がいるかも知れないと思い、あらためてLazarusで作成し直しました(LazarusにはTAChartというTeeChartに似たコンポーネントがあります)。

で、その結果ですが・・・どれもあまり代わり映えがしないようで、生成数が10,000個になるとどれもほとんど差がありません。
まぁ、よほど正確性が必要な用途に用いるのではない限り、Delphi/Lazarus標準のRandGが最強ではないかと思った次第です。
#それにしてもTeeChartにしてもTAChartにしても、機能が豊富すぎて使うのがものすごく面倒くさいです。

参考にLazarusプロジェクトソースと実行ファイルをアップしておきます

// ボックスミュラー法による乱数生成
procedure TForm1.BoxMullerTransform(RandType: integer; var x, y: Double);
var
  r1, r2: double;
begin
  case RandType of
    TYPEMT19937:
      begin
        r1 := mt19937_double(mtctx);
        r2 := mt19937_double(mtctx);
      end;
    else begin
      r1 := Random;
      r2 := Random;
    end;
  end;
  x := Sqrt(-2.0 * Ln(r1)) * Cos(2.0 * Pi * r2);
  y := Sqrt(-2.0 * Ln(r1)) * Sin(2.0 * Pi * r2);
end;
 
// 中心極限定理による乱数生成
function TForm1.CentralLimitTheorem(RandType: integer): double;
var
  i: integer;
  r: double;
begin
  r := 0.0;
  for i := 1 to 12 do
    case RandType of
      TYPEMT19937:
        r := r + mt19937_double(mtctx);
      else
        r := r + Random;
    end;
  Result := r - 6.0;
end; 

Test

「RandTest.zip」をダウンロード

| | コメント (0) | トラックバック (0)

2017.04.23

DelphiとLazarusでのドロップファイル処理の違い

ファイルやフォルダの日付を一括変更したくてフリーソフトのTouchを探したのですが、フォルダの日付も変更できるものを見つけることが出来なかったので自力で作成しました。
細かなことが面倒だったので、ファイル・フォルダに関わらずまとめてドロップすれば一気に日付を変更する、ファイル日付は作成・更新・最終アクセス日付を指定した日時で全部変更する、ということにしました。また、実行ファイルのショートカットにファイル・フォルダをドロップしたら、その時点の日時で変更する、日時を指定したい場合は実行ファイルを起動して日時を指定後にそのフォームにドロップする、ということにしました。

Touch

で、今回もDelphiとLazarusの両方で実行ファイルサイズを見比べならが(^^;作成したのですが、Lazarusでのドロップファイル処理の仕方がDelphiとは異なっていたのでメモしておくことにしました。こうやって比べてみると、私的にはLazarusの方がスマートな感じがしていいかなと思います。

Delphiの場合

// ファイル・フォルダがドロップされた際のイベント
procedure TForm1.DropFiles(var Msg: TMsg; var Done: Boolean);
var
nFile, i: integer;
fname: string;
FileName: array[0..260] of char;
DT: TDateTime;
begin
Done := True;
if Msg.message = WM_DROPFILES then
begin
SetForegroundWindow(Application.Handle);
// 変更後の日付と時刻を合成(DateNewの整数部とTimeNewの小数部を足す)
DT := Trunc(DateNew.DateTime) + Frac(TimeNew.DateTime);
try
nFile := DragQueryFile(Msg.wParam, $FFFFFFFF, nil, 0);
for i := 0 to nFile - 1 do
begin
DragQueryFile(Msg.wParam, i, FileName, SizeOf(FileName));
fname := FileName;
if DirectoryExists(fname) then
ChangeDirTime(fname, DT)
else
SetFileDate(fname, DT, DT, DT);
end;
finally
DragFinish(Msg.wParam);
end
end else
Done := False;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
//ドロップファイルイベントの登録
Application.OnMessage := DropFiles;
DragAcceptFiles(Handle, True);

Lazarusの場合

// ファイル・フォルダがドロップされた際のイベント
procedure TForm1.DropFiles(Sender: TObject; const Files:Array of AnsiString);
var
i: integer;
fname: string;
DT: TDateTime;
begin
SetForegroundWindow(Handle);
// 変更後の日付と時刻を合成(DateNewの整数部とTimeNewの小数部を足す)
DT := Trunc(NewDate.Date) + Frac(NewTime.Time);
// Lengthで配列の大きさを取得してリストを取得する
for i := 0 to Length(Files) - 1 do
begin
fname := Files[i];
if DirectoryExists(fname) then
ChangeDirTime(fname, DT)
else
SetFileDate(fname, DT, DT, DT);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
// Lazarusではファイルドロップイベントを以下のように設定する
Application.AddOnDropFilesHandler(@DropFiles);

procedure TForm1.FormDestroy(Sender: TObject);
begin
// 終了時にイベント解除
Application.RemoveOnDropFilesHandler(@DropFiles);
end;

実行ファイルサイズは
Delphi10.1 Starter   2,167,808 バイト
Lazarus1.7+FPC3.1  2,788,352 バイト

LazarusではDateTimePickerコンポーネントが標準ではインストールされていないので、手動でDateTimeCtrlsパッケージをインストールする必要があります。おそらく、この追加コンポーネントがファイルサイズが大きくなっている原因ではないかと思います。

「touch.zip」をダウンロード

サンプルファイルにはDelphi/LazarusのソースファイルとDelphiで作成した実行ファイルが含まれています。

| | コメント (0) | トラックバック (0)

より以前の記事一覧

その他のカテゴリー

Delphi | Inno Setup | Lazarus | Nightly Build | Raspberry Pi | VisualStudio/C#