高見知英の技術ログ

技術関係のログをQiitaから移行してきました。プログラミングのほか、使っているアプリの細かい仕様についてなど書いていきます。

Lazarusでコモンダイアログを表示する

このブログはLazarus(FreePascal)アドベントカレンダー7日目の記事です。

qiita.com

7日目はLazarusでファイルを開く・保存ダイアログやメッセージボックスを表示する方法を。

コモンダイアログを表示する

Lazarusは、Delphiと同様、LCLに標準ダイアログを表示することができるクラスが揃っています。いちおうコンポーネント一覧にありますが、フォームに貼り付けて使うのも面倒ですし、普通にクラスを初期化して使うと楽です。

procedure TMainForm.ButtonClick(Sender: TObject);
var
  OFD: TOpenDialog;
begin
  OFD := TOpenDialog.Create(Self);
  try
    OFD.Filter:='EXE File|*.exe';
    if OFD.Execute then Path.Text:=OFD.FileName;
  finally
    OFD.Free;
  end;
end;

なお、「これってなんのダイアログだっけ・・・?」と思ったら、コンポーネントをフォームに貼り付けてダブルクリックすると、ダイアログのテストができます。これはたしかDelphiもおなじでしたね。

TCalcuratorDialog?

なお、コンポーネントパレットを見るとTCalculatorDialogなどという見慣れない名前のダイアログがいくつか見つかります。

f:id:TakamiChie:20211203200731p:plain
TCalculatorDialog

ソースコードを見ている感じWindows APIを呼んでどうにかしている様子はないため、ダイアログを作る際の参考になるかもしれません。

単純なメッセージボックスを表示する

また、単純なメッセージボックス、確認ダイアログなどは、DialogsユニットのMessageDlgまたはQuestionDlg関数を使います。

{ QuestionDlg }
Result:= QuestionDlg('caption', 'message', mtConfirmation, [mrYes, mrNo], 0);
{ MessageDlg }
MessageDlg('caption', 'message', mtInformation, [mbOK], 0);

第三引数Buttonsが要求する値が、QuestionDlgarray of const(mr○○の定数配列)、MessageDlgTMsgDlgButtons(mb○○の定数配列)と微妙に違うので注意。

単純なメッセージボックスを「作る」

なお、ダイアログをただちに表示するわけでなく、TFormオブジェクトとして取得し、いつでも表示できるようにする という方法もあります。

CreateMessageDialogというメソッドを使用します。

lazarus-ccr.sourceforge.io

これを使えば上記MessageDlgと同等のダイアログをオブジェクトとして取得することができます。たとえばおなじダイアログを何度も表示したり、フォントや表示位置を調整したいときには便利です。

Lazarusプログラムからショートカットを作る

このブログはLazarus(FreePascal)アドベントカレンダー6日目の記事です。

qiita.com

プログラムのショートカットファイル(*.Ink)を作るには、ほかの言語と同様にCOMオブジェクトのShellLinkオブジェクトを使用します。

詳細については公式Wikiを参照。

wiki.lazarus.freepascal.org

ここのミソは二つ

  • windows, shlobj, ActiveX, ComObj;の四つをusesに加えること。
  • IPersistFile.Save()の引数にはWideString型の変数を使うこと*1

なにやらReleaseしてないのが本能的に気になりますが、Releaseメソッドを呼ぼうとするとViolationErrorが発生しますのでとりあえずこのままで。

なお、このコードではWindows APISHGetSpecialFolderLocationを使っていますが、現在のLazarusではLazFileUtilsSHGetFolderPathUTF8というメソッドがあるのでそれを使ってもOK(引数のCSIDL値についてはshlobjユニットに定義されています)。

function Register4StartupClick(): Boolean;
var
  path: WideString;
  ISlink: IShellLink;
  IPFile: IPersistFile;
begin
  path := WideString(SHGetFolderPathUTF8(CSIDL_STARTUP) + 'AppName.lnk');
  if FileExists(path) then
  begin
    Result:= DeleteFile(path);
  end
  else
  begin
    ISlink:=CreateComObject(CLSID_ShellLink) as IShellLink;
    IPFile:= ISlink as IPersistFile;
    ISlink.SetPath(PChar(Application.ExeName));
    ISlink.SetWorkingDirectory(PChar(ExtractFileDir(Application.ExeName)));
    Result:= Succeeded(IPFile.Save(PWChar(path), False));
  end;
end;

スタートアップフォルダに自アプリのショートカットを作成したり削除したりします。

*1:String型ではダメです。どうせPWCharにキャストしてるから問題ないだろーと思ったらそんなことなかった

Lazarusでテストを書く

このブログはLazarus(FreePascal)アドベントカレンダー5日目の記事です。

qiita.com

5日目はおまちかね(?)テストについて。

コンパイルして動かすLazarusのプログラムはとくに、メソッドを作ったときのテストがないのは不安です。

なのでとりあえず、Lazarusでテストプロジェクトをつくって、それを実行してみます。

Lazarus(というかFreePascal)のテストフレームワーク:FPCUnit

FreePascalのテストフレームワークとしては、FPCUnitというものがあります。

LazarusにはこのFPCUnitを使ったテストプロジェクトを作成するためのテンプレートが装備されているため、これを使うのが便利です。

f:id:TakamiChie:20211203182319p:plain
テストプロジェクトの作成

ファイルメニューの「新規…」より、FPCUnit Test Applicationを選択します。

なお、GUIを操作するのは地味に面倒くさいのでFPCUnit Console Test Applicationを使えば良いじゃん という風には思ったのですが、ちょっと使ってみた感じ使い方がよくわかりませんでした…。

そのため、今回はとりあえずGUIのあるほうを選びます。

プロジェクトグループを作る

上記の通りですが、Lazarusのテストはテストプロジェクトという形で作成していきます。

アプリ開発の最中にメインのプロジェクトとテストプロジェクトを行ったり来たりするのは面倒なので、プロジェクトグループを作っておくと良いでしょう。

プロジェクトグループの作り方については、二日目の記事にも書いたので、そちらを参照。

devblog.onpu-tamago.net

メインプロジェクトとは別に、テストプロジェクトを作り、テスト対象となるユニットファイルをプロジェクトエクスプローラの「追加」ボタンより追加していきます。

f:id:TakamiChie:20211203182232p:plain
プロジェクトエクスプローラ

Lazarusはユニットファイルが構築パスに入っていればどこに置いていてもファイル名で参照できるので、ファイルの置き場は特に気にする必要はありません。

テストユニットを作る

テストユニットファイルは、同じく新規ダイアログより、FPCUnit Test Caseを使います。

f:id:TakamiChie:20211203182855p:plain
FPC Unit Test Case

テストケース名と'Setup'メソッド等を作成するかどうか聞かれますので、適当な値を設定してOK。

テストのやり方は、ほかの一般的な○○○Unitと同様、処理対象のメソッドを呼んだあと、その戻り値を期待値とAssertEqualsメソッドで照合します。

procedure TScriptProcessTestCase.TestLaunchProgram;
var
  Proc : TScriptProcess;
  StdOut, StdErr: String;
begin
  Proc := TScriptProcess.Create();
  try
    Proc.Execute('scripts\\SPTEST_TestScript.py', StdOut, StdErr);
    AssertEquals(StdOut, 'Hello This Is a TEST' + #13#10);
  finally
    Proc.Free;
  end;
end;

使用したクラスの解放は… まあ、忘れたところでどうせテストなのでそんなに問題になることもないかと思いますが、いちおうクセをつけておくと良いかと。

いざ実行

いざいくつかテストを作って実行すると次のようなアプリが起動します。

f:id:TakamiChie:20211203183906p:plain
テストアプリ実行環境

使い方に迷うことはまあないと思いますが、ツールバーの▶ボタンを押すとテストが実行されます。結果はXMLで出力することもできるようです。

スクリプトファイルの読み込みやリソースなどは、なるべく本プロジェクトと共有しない

またここからは一般的なテスト作成の方針になるとは思いますが、プログラムの中でスクリプトファイルの読み込みやリソースファイルを使用する際は、本プロジェクトとは別のものを使うと良いでしょう。

あとあと本プロジェクトの側で修正が必要になった場合、テストの書き換えが必要になる場合があります。

面倒でも同じようなリソースファイルをテスト用に作っておくと良いでしょう。

Lazarusで別のプロセスを呼び出す

このブログはLazarus(FreePascal)アドベントカレンダー4日目の記事です。

qiita.com

Lazarusでほかのプロセスを呼び出す場合、ProcessモジュールにあるTProcessというクラスを使用します。

使い方は大まかには上記サイトに記載の通りですが、TProcess.Createメソッドでオブジェクトを作成、TProcess.Executableプロパティに実行したいファイルおよびコマンドの名前を、TProcess.ParametersプロパティにAddメソッドを使って引数を追加し、TProcess.Executeメソッドで実行します。開いたプロセスの終了を待つ場合はTProcess.WaitOnExit();メソッドを呼び出して終了待ちを行ないます。

詳細は以下をご覧ください。

github.com

Lazarusでリソースを使う

このブログはLazarus(FreePascal)アドベントカレンダー3日目の記事です。

qiita.com

Lazarusでリソースを使う画像やアイコン、スクリプトファイルなどのファイルはリソースとしてプロジェクトに取り込んでおくと便利です。

Lazarusで扱えるリソースには2種類があります。

  • resファイル:プロジェクトオプションから追加できるリソース形式。TResourceStreamオブジェクトで読み込みが可能。
  • lrsファイル:LazResというユーティリティファイルによって生成するリソース形式。LazarusResourcesというTLResourceList型のオブジェクトにより取得が可能。

どちらもリソースの名前を指定することができるという点では同じですが、resファイル形式はn番目のリソース取得したり、リソースの名前一覧を取得することができない(っぽい)のに対し、lrsファイルはリソースの個数を取得したり、n番目のリソースを取得するなどといったことができます。

res形式 lrs形式
作成方法 プロジェクトオプションより LazResユーティリティより
読み取り方法 TResourceStreamを使う LazarusResourceを使う
クラスの定義ユニット Classes LResources
for文で走査 できない できる

TResourceStreamはTStreamの子オブジェクトなので、画像などのバイナリファイルを基本に、内部のスクリプトファイルなどにはLazarusResourcesを使うと良いでしょう。

LazResをいちいち呼ぶのが面倒くさい

LazResユーティリティはLazarusインストールディレクトリの\tools\lazres.exeにあります。そこで、プロジェクトオプションより起動前にpython .\resourcemaker.py --lazarus_dir $(LazarusDir)などとしてリソースを整理するためのスクリプトを実行しておくと良いでしょう。実例は以下のあたりを参照。

github.com

lrsファイルの中身が古い?

なお、とくにlrs形式の場合、ファイルをせっかく書き換えたのになぜか参照されるリソースファイルが古い ということがあります。

どうもlrs形式の場合、ただ「実行」しただけではlrsが更新されないことがあるようです。

コンパイルまたは構築を実行してみましょう。

LazarusプロジェクトをGit管理する

このブログはLazarus(FreePascal)アドベントカレンダー2日目の記事です。

qiita.com

Lazarusでプロジェクトを作成する場合、まず気になるのがGitで管理できるかということです。

Delphiも昔は「他人が作ったコンポーネントの情報とかを自プロジェクトに含めなくて済むのでソースコードが公開しやすい!」みたいなことを思っていましたが、考えてみればPythonのpipenvみたいなものがないので他者がソースコードを持ってきてビルドする なんていうことがやりづらかった。

Lazarusのほうでもその問題は解決されてない(?)ようですが、とりあえず三者の作ったコンポーネントやクラスライブラリを使っていない場合はGit管理ができます。

要らないファイルはどれか?

さて、Git管理する場合、まず気になるのが管理対象からはずすべきファイルの存在。とりあえずGithubの新規プロジェクト生成時に使用できるPascalの.gitignoreテンプレートを使ったため抜けはあるかもしれませんが、以下のファイルを無視すると、とりあえずLazarusプロジェクトをGit管理できます。

  • 成果物・中間ファイルなど
    • *.exe
    • *.dll
    • *.o
    • lib/
  • リソースファイルなど
    • *.res:一見リソースファイルなので管理必要なのですが、リソースをまとめて実行ファイルに込める直前の中間ファイル的な存在のようなので、管理不要です
    • *.lrs:Lazarusリソースファイル。これが管理不要かどうかはシチュエーションに寄ります(うちはresourcemaker.pyで自動生成するようにしてしまったので管理不要になりました)
  • 設定ファイル
    • *.lps:Lazarusプロジェクトの設定ファイル(どのファイルをエディタで開いているか、どの行を選択しているかなど)
    • *.fpcunit.ini:Lazarusテストプロジェクトの設定ファイル
  • その他
    • backup/

というわけで、これらを無視ファイル設定すればいけそうです。

お勧めのディレクトリ構成

お勧めのディレクトリ構成は・・・といっても、Lazarusのプロジェクトエクスプローラは機能がそれほど多くなく、Lazarus自体同一プロジェクト内のファイルはuses句に名前を追記してあげればそれだけで呼べるようになるため

  • 役割ごとに別々のフォルダを作ってソースファイルを置く
  • それらをまとめてLazarusのプロジェクトエクスプローラに追加する

くらいで良さそうです。あとは今後書く予定のテストについては「テストプロジェクト」という扱いになるので、テストプロジェクトを作成する予定であれば以下のような感じがよさそう

プロジェクトルート
  |--src
  |    `--実際に作るプログラムのソースコード
  `--tests
      `--テストプロジェクトのソースコード

プロジェクトグループ

前述テストプロジェクトなんかもありますが、近いプロジェクトを別管理するのも面倒です。

そんなときのために、Lazarusには「プロジェクトグループ」という機能があります(たぶんVisual Studioなどでいうソリューション相当)。

あ、メニュー項目名は日本語化してる前提です。

  • 公式Wikiの内容を参考にProject Groupsの機能を有効にする。
  • メインメニュー>パッケージ>パッケージをインストールもしくはアンインストール
  • 右側の「インストール可能パッケージ」の一覧より「lazprojectgroups」を選択して「選択対象をインストール」をクリック
  • 「保存してIDEを再構築」をクリック
  • 確認ダイアログが出るのでそのままOK

しばらくIDEが再構築されたあと自動的に再起動されるので、プロジェクトメニューより「New Project group...」をクリックして使います。

f:id:TakamiChie:20211129094454p:plain
Project group

やってることはプロジェクト名をクリックするとプロジェクトを開き直すだけなんですが、意外と使いやすいので有効にしておくと良いです。

肝心のGit管理機能は・・・

さて、肝心のLazarusのGit管理機能は・・・というと、残念ながらLazarusはGitに対応しておりません。

個人的には外部ツールにVisual Studio Codeを追加してそれを使ってます(Github Desktopなどでもいいでしょうが細かい編集などがしづらいので)。外部ツールの設定でVisual Studio Codeなどを指定しておくと良さそう(プロジェクトグループを作成しているとこの書き方じゃダメかも)。

f:id:TakamiChie:20211129095354p:plain
ツールの追加

というわけでLazarusでもソースコードをGit管理していきましょう。

Lazarusってなんぞや

このブログはLazarus(FreePascal)アドベントカレンダー1日目の記事です。

qiita.com

さて、今回は以前もちょこっと話題にしたLazarusという環境について2,3書いてみようと思い、勢いでこのカレンダーを立ち上げました(DelphiアドベントカレンダーはあるけどDelphi言語を使うわけではないからDelphiのほうではないですしね…)

Lazarusとは

Lazarusとは、現在Embarcaderoが販売しているDelphiという開発環境で扱う、Delphiという言語の元になった言語*1Pascalのもう一つの潮流、FreePascal用の開発環境です。

昔のDelphiに割とよく似た構成をしており、Delphiを過去使ったことがある人はまあ迷うことなく使えるでしょう。

f:id:TakamiChie:20211129084007p:plain
Lazarusスクリーンショット

言語としてもだいぶDelphiと似通っており、とりあえずこれまたDelphiを使ったことがある人ならばそこまで悩むことなく使えると思います。

Delphi6とかそのへんとおなじ水準です。割と古い記法が使えます。無名関数もジェネリクス(配列などの型指定)もできないし、GCもありません。ただまあ、LCL(DelphiVCLみたいなもの)がまともに使えるのでまあまあ使える といった感じ。

YouはどうしてLazarusを?

なんでまたLazarusなんてマイナーな環境を使ったか というと、だいたい以下のような理由で

  • PCで常駐型のアプリケーションを作ろうと思ったため(PythonでもGUIは作れるがさすがにそれだと起動に時間がかかりすぎる)
  • C#などだとVisual Studioありきになってしまうが、SSD容量の問題でVisual Studioが入らないのと、以前Visual Studioを使ったときにアップデート地獄*2に悩まされたことからあまり使いたくない
  • かといって本家DelphiのCommunity Editionは初期のビルド対象が今更Win32で、さすがにそれはないだろと思ったこと(とはいえLazarusが64ビット対応しているかというとそういうわけではないかも)
  • 昔ながらのDelphiっぽい環境で開発を行ないたい気分も少しあった

ということで、とりあえずこれを使ってアプリを作ってみよう と思い立ったわけです。

Lazarusのインストール

とりあえずLazarusをインストールするには、Lazarusのサイトより使っているOSのパッケージをダウンロード

www.lazarus-ide.org

Chocolateyをお使いの場合choco install -y lazarusでもいけます。最近wingetでもインストールできるらしい。

Lazarus環境の日本語化

UI部分の一部しか日本語化できないため、別に日本語化と言えるほどカッコいいものではありませんが、Lazarusの環境は設定より日本語化できます。

  • メインメニュー>Tools>Options
  • 設定の検索で「Language」と入力
  • Environment>Generalを選択
  • 右ペインのLanguageコンボボックスからjaを選択
  • 「次に表示されるオブジェクト」から日本語化されるようなので、Files>RestartでIDEを再起動

f:id:TakamiChie:20211129093135p:plain
Lazarusの日本語設定

作ったものはこんな感じ

とりあえず作ったものはこんな感じです。

github.com

いけそうなときは0000Studioで開発作業を配信していますので、覗いていただけると幸いです。

*1:たしかBorlandが開発していた当初、「これはObjectPascalではなくDelphi言語だ」みたいなことを書いている記事があった気がするのでそう書く

*2:起動する度にIDEのなんらかのアップデートにより作業が止まる