Python for Delphi について
−その2−

(最終更新:2003年2月10日)
  1. サンプル作りました

    最初の項を書いてからずいぶん時が流れたような気がしますが...^^;)、ようやっと人に見せられる(いろんな意味で)サンプル作りました。
    僕は自分のWebに写真を載せるときにJavaScriptを使っていて、そのスクリプトに画像の大きさを与える仕様にしてしまったもので、JPEGファイルの画像のサイズを知りたいという要望が結構頻繁にあります。 これまではPaint Shop Proというソフトウェアの「イメージ情報」コマンドを使っていましたが、このコマンドではイメージのウィンドウを1つずつ選択して実行する必要がある、表示された画像サイズをコピー&ペーストできないという仕様になっていて、僕の用途にはしっくりきません。 そこで自分で作ってしまおう、と思い立ったわけであります。

    1. まずはPythonスクリプトから...

      こんなのを書きました。 これで全てのJPEGファイルに対応できるかどうか、微妙なところ(whileの中で見なきゃいけないマーカがもっとある)ですが、多くはこれでOKでしょう。 っていうか、ぼくの環境(画像の処理にはPaint Shop Pro 7Jを使用)では問題なしです。
      Pythonコードはこちらをどうぞ Pythonスクリプトとして単体で使う場合には、最後の方の if __name__ == 以下のくだりを参考にしてください。

    2. Delphi上では、こう...

      そうそう、わすれちゃいけません。まずはDelphi+Python for Delphiをインストールしといてね。
      Delphiのフォームはこんな感じに作りました。
      ボタンが2つとメモが1つと、OpenDialogが1個。あとのニシキヘビマークがPython関係のコンポーネントです。

      TPythonEngineは、その名の通り、Pythonのエンジンです。
      TPythonGUIInputOutputは、もしかしたらいらなかったかも。チュートリアルには必ず入ってたんで、勢いで入れてしまいました。
      TPythonDelphiVarも名前の通りですね。Delphiの世界とPythonの世界の両方に存在するオブジェクトで、これを経由して相互に変数値をやり取りできます。
      この例では変数を2つ同時に使いたかったので、TPythonDelphiVarを2つ置いています。 それぞれのインスタンス名はデフォルトのまま(PythonDelphiVar1及びPythonDelphiVar2)で、各々のVarNameプロパティはvar1とvar2にしました。
      またOpenDialogはオプションの ofAllowMultiSelect をTrueにしています。

      大サービス!Delphiのコードはこんな感じ。

      (2003年1月30日追記)
      Delphiコードを新しくしました。その関係で画面イメージが若干変わっています。

      
      unit jpg_sz_main;
      
      interface
      
      uses
        Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
        StdCtrls, PythonEngine, PythonGUIInputOutput, ShellAPI;
      
      type
        TForm1 = class(TForm)
          SelectButton: TButton;
          OpenDialog1: TOpenDialog;
          Memo1: TMemo;
          PythonEngine1: TPythonEngine;
          PythonGUIInputOutput1: TPythonGUIInputOutput;
          ExitButton: TButton;
          PythonDelphiVar1: TPythonDelphiVar;
          PythonDelphiVar2: TPythonDelphiVar;
          Label1: TLabel;
          ClearButton: TButton;
          procedure SelectButtonClick(Sender: TObject);
          procedure FormCreate(Sender: TObject);
          procedure Exec(FileList: TStrings);
          procedure ExitButtonClick(Sender: TObject);
          procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
          procedure ClearButtonClick(Sender: TObject);
        private
          { Private 宣言 }
          FileList: TStrings;
          protected
            procedure CreateParams(var Params: TCreateParams); override;
        public
          { Public 宣言 }
        end;
      
      var
        Form1: TForm1;
      
      implementation
      
      {$R *.DFM}
      
      procedure TForm1.FormCreate(Sender: TObject);
      var
        StrList: TStringList;
        s: String;
      begin
        StrList := TStringList.Create;
        StrList.Add('import sys');
        StrList.Add('sys.path = sys.path[2:]');
        s := ExtractFilePath(Application.ExeName) + 'Python\Lib';
        StrList.Add('sys.path.append(r"' + s + '")');
        s := ExtractFilePath(Application.ExeName) + 'Python';
        StrList.Add('sys.path.append(r"' + s + '")');
        StrList.Add('import jpg_sz');
        StrList.Add('var1.value = jpg_sz.VERSION');
        GetPythonEngine.ExecStrings(StrList);
        Form1.Caption := 'jpg_sz  ver. ' + PythonDelphiVar1.ValueAsString;
      end;
      
      procedure TForm1.SelectButtonClick(Sender: TObject);
      begin
        if OpenDialog1.Execute then
        begin
          FileList := OpenDialog1.Files;
        end;
        if FileList.Count <> 0 then
        begin
          Exec(FileList);
        end;
      end;
      
      procedure TForm1.Exec(FileList: TStrings);
      var
        StrList: TStringList;
        n: Integer;
        s: String;
      begin
        StrList := TStringList.Create;
        for n := 0 to FileList.Count - 1 do begin
          s := 'J = jpg_sz.JPG_SZ(r"' + FileList[n] + '")';
          StrList.Add(s);
          StrList.Add('var1.value = J.Read()');
          GetPythonEngine.ExecStrings(StrList);
          StrList.Clear;
          if PythonDelphiVar1.ValueAsString = '0' then
          begin
            StrList.Add('var1.value, var2.value = J.getResult()');
            GetPythonEngine.ExecStrings(StrList);
            s := FileList[n] + ': ('
                 + PythonDelphiVar1.ValueAsString + ', '
                 + PythonDelphiVar2.ValueAsString + ')';
            Memo1.Lines.Append(s);
          end;
        end;
      end;
      
      procedure TForm1.CreateParams(var Params: TCreateParams);
      begin
        inherited CreateParams(Params);
        Params.ExStyle := Params.ExStyle or WS_EX_ACCEPTFILES;
      end;
      
      procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
      var
        FileName: array [0..MAX_PATH] of Char;
        i, total: Integer;
        FileList: TStringList;
      begin
        FileList := TStringList.Create;
        total := DragQueryFile(Msg.Drop, $FFFFFFFF, nil, 0);
        for i := 0 to total - 1 do
        begin
          DragQueryFile(Msg.Drop, i, FileName, SizeOf(FileName));
          FileList.Add(FileName);
        end;
        DragFinish(Msg.Drop);
        Exec(FileList);
      end;
      
      procedure TForm1.ExitButtonClick(Sender: TObject);
      begin
        Form1.Close;
      end;
      
      procedure TForm1.ClearButtonClick(Sender: TObject);
      begin
        Memo1.Clear;
      end;
      
      end.
      
      
      TForm1.FormCreateのところではPythonの世界を初期化しています。
      最初にsys.path = sys.path[2:]としているのは(環境のせいかな?)不要なPythonPathを取り去るためです。 いらないものを取り去った上で、必要なパスを追加しています。 今回の例では、コンパイルしてできるDelphiの.exeバイナリと同じディレクトリにpython20.dllを置き、同じ階層にPythonというサブディレクトリを作り、その中にPythonのライブラリおよび今回作った上記のjpg_sz.pyを置きます。

      実際の処理はファイル選択ボタンを押したときに全て行っています。
      選択されたファイル(群)の名前はFileListという変数に入ってきますが、この変数の宣言をグローバルに置いたのは、あとで別のprocedureから使うように変更するかもしれない、という理由からで、今回のサンプルではローカルに宣言しても構いません。
      あとは簡単です。forで回して、ひとつづつPythonのクラスインスタンスを作り、値をもらってきて、その都度Memoに1行追加しているだけ。
      この例では、Memoへ1行追加する文字列はDelphi側で作ってしまいましたが、本来であればPythonスクリプト内に実装して置くほうがよいと思います。 そうすれば出力フォーマットを変更したい時でもPythonの方だけ直せば済みますから。

      バイナリをここへ置いておきますので、試してみたい方はどうぞ。 ただし、持って行かれた方はお手数ですが私宛にご一報ください。 (メールアドレスは、一番下のフレームにあります。サーチエンジン等からお越しいただいた方で、フレーム表示されていない方は、ぜひこちらよりどうぞ。
      また、このプログラムを使ったことにより何が起こっても一切の保証はできませんので悪しからず。

      (2003年1月30日追記)
      プログラムを新しくしました。
      ダウンロードされる方はこちらよりうどうぞ。
      jpg_sz_installer.exe(1,243,732 バイト)
      McAfee VirusScan(定義ファイル4.0.4244)でウィルスチェック済み
      今回の改訂で、Delphiで作ったUIを多言語化しました。 と言っても日本語とそれ以外(英語)だけですが。
      またインストーラとしてNSISを使わせていただいています。 そうそう、ドラッグ&ドロップにも対応しています。

      (2003年1月31日追記)
      上記で採用のインストーラ、僕が使った設定だとWindows98系だと起動時に「Japanese」を選択しても文字化けすることが判明。
      すいませんが、Windows98系の方インストーラは「English」でお願いします。 (なんかインストールされるアプリにも同じ現象が出ているみたい。もっともこちらは化けずに英語表示されるわけですが。原因調べます。)
      インストーラの文字化け、たぶんMS UI Gothicフォントを持っているシステムならたぶん解消できたと思います。

      (2003年2月10日追記)
      Pythonコード部分を新しくしました。 たぶん、これでほとんどのJPEGファイルに対応できたんじゃないかな。 バージョン1.12とさせていただきます。ダウンロードはこちらからどうぞ。
      jpg_sz112_installer.exe(1,243,995 バイト)
      McAfee VirusScan(定義ファイル4.0.4246)でウィルスチェック済み