Delphi で作った複数のフォームを Python で呼び出す方法

# はじめに
以前の記事（Delphi Advent Calendar 2022 14日目）で [Delphi のフォームを Python から使う方法](https://qiita.com/iihi/items/9dc35a2435591b5d3bb1) を載せましたが、その続きとしてサンプルを元に２つのパターンで動かしてみました。

その一つ目として Delphi で作った複数のフォームを Python で呼び出してみます。

## 前提条件
 [Delphi のフォームを Python から使う方法](https://qiita.com/iihi/items/9dc35a2435591b5d3bb1) の記事の前提条件と Delphi FMX for Python, Delphi4PythonExporter のインストールが済んでることとします。詳細はそちらの記事をご確認ください。

### 補足
基本的に情報が少ないため、模索しながら実施してますので、本来の想定された実装の仕方かどうかは分かりません。また、Python についての知識が乏しいため、使い方が正しいかどうか分かりませんので、その点はご了承ください。

## Delphi でフォームを作成して Python で実行
### Delphi でフォームを作成
まず Delphi で以下のようなアプリを作りました。画像はいらすとやから拝借しましたが、何でも良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543326/b9d770d7-b61f-5566-a20e-8136f55d25d8.png)
アプリは単純で、Form1 のボタンをクリックすると Form2 を表示します。
（これを Python で同じ動きになるようにしていきます）

デザインはこんな感じです。予め画像は読込んでおきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543326/bdce1d7d-1bf7-9f2d-96fb-a86105674aff.png)



<details><summary>Unit1.pas, Unit2.pas のコード</summary>

```Unit1.pas
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Edit,
  FMX.Objects, FMX.StdCtrls, FMX.Controls.Presentation,
  FMX.Layouts, Unit2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    Layout1: TLayout;
    procedure Button1Click(Sender: TObject);
  private
    { private 宣言 }
  public
    { public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.Show();
end;

end.
```

```Unit2.pas
unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Objects, FMX.Controls.Presentation, FMX.Layouts;

type
  TForm2 = class(TForm)
    Image1: TImage;
    Layout1: TLayout;
  private
    { private 宣言 }
  public
    { public 宣言 }
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

end
```
</details>

### Python 用に出力
これをツールの「Export to Python」→ 「Export Entire Current Project」で Python 用のコードを生成します。

以下のファイルができます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543326/63bc0c7c-6a0a-1bfa-96a9-89d802ecf4d5.png)

<details><summary>Project1.py, Unit1.py, Unit2.py のコード</summary>

```Project1.py
from delphifmx import *
from Unit1 import Form1

def main():
    Application.Initialize()
    Application.Title = 'Sample App'
    Application.MainForm = Form1(Application)
    Application.MainForm.Show()
    Application.Run()
    Application.MainForm.Destroy()

if __name__ == '__main__':
    main()
```

```Unit1.py
import os
from delphifmx import *

class Form1(Form):

    def __init__(self, owner):
        self.Layout1 = None
        self.Button1 = None
        self.Image1 = None
        self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Unit1.pyfmx"))

    def Button1Click(self, Sender):
        pass
```

```Unit2.py
import os
from delphifmx import *

class Form2(Form):

    def __init__(self, owner):
        self.Layout1 = None
        self.Image1 = None
        self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Unit2.pyfmx"))
```
</details>

:::note info
ここで「Export Select Forms」で出力すると Unit1.py と Unit2.py で別々のアプリケーションの形でファイルが生成されます。下記の記述が Form1 と Form2 それぞれに入ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543326/1acba3d7-3622-7d13-aab1-68a2142380d7.png)
また、今回はフォームを２つ作りましたが、フレームが入っていると、どちらを選んでも Export to Python 機能の仕様でエラーになり出力できないようです。
:::

### Python コードを編集
まず上記で生成した Unit1.py を編集します。

**・Unit1.py**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543326/011fa0b3-a7c6-6b49-d9fc-ae79d32985de.png)
Unit2 を呼ぶために import と、ボタンクリックイベントの処理を追加します。

<details><summary>Unit1.py の編集後コード</summary>

```Unit1.py
import os
from delphifmx import *
from Unit2 import Form2

class Form1(Form):

    def __init__(self, owner):
        self.Layout1 = None
        self.Button1 = None
        self.Image1 = None
        self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Unit1.pyfmx"))

    def Button1Click(self, Sender):
        Form2(Application).Show()
```
</details>

### Python 実行
PyScripter で Unit1.py を開いて実行、または、Unit1.py ファイルをダブルクリックで実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543326/d4c43136-f03e-c219-7098-15e132ba1a62.png)
実行すると Form1 が表示され、ボタン押下で Form2 が表示されることが確認できます。

## まとめ
以上のように、Delphi で作成して実行したアプリと、同様な表示・動きが Python から実行できました。
この程度ならば、簡単に Delphi のフォームを Python から使うことができます。
また、本記事では FMX で作成しましたが、VCL でも同様に作成できます。

次回の記事ではもう一つのパターンで、Delphi で作ったフォームを Python で使い、そこから Delphi のメソッドを呼んだり、画像データを表示させたりしたいと思います。

**追記**
本記事の続きとして、もう一つのパターンの使い方を記載しました。
[Python から Delphi のメソッドを使う方法](https://qiita.com/iihi/items/233d939f551d7c2b8b6f)