ChatGPT-4 でソフトウェアマニュアル作りを効率化！実践的なテクニックの紹介


# 1. はじめに

## 1.1. 本日ご紹介すること

　ChatGPT に出会ってしまった日から、何ができるのか、毎日毎日新しい発見が楽しすぎて、あまり眠れていません。皆さんおはようございます！

今日は、ソフトウェアのマニュアル作りを楽にするために、ChatGPT-4 にソースコードからマニュアルを自動生成してもらいたいと思います。

:::note info
本記事で言っている、ソフトウェアのマニュアルとは、GitHubなどの `README.md` をイメージしています。
エンジニア向けに、ソフトウェアの概要と、使い方がぱっとわかるものがあればいいなと思っています。
:::

## 1.2. 経緯

　いなくなってしまった先輩の、何の説明資料もないソフトウェア（大体の場合，そんなコードにはコメントもない）を引き継いで、血の涙を流しながら修正したことはないでしょうか...？

　そんな愚行をおかさないように、後輩に自分の背中を見せつつ

「まずソフトウェアのマニュアルを書こうね。上司への報告書はあとでいいから。まずマニュアルを作ろうね」

とさんざん言ってきましたが...


**正直に言うと、自分が作ったソフトウェアのマニュアルを書くのめっちゃ面倒くさいですよね！** 
（可能なら、永遠に設計やコーディングがしたい）


ということで、ChatGPT にソフトウェアのマニュアル作成を手伝ってもらう... いや作ってもらおうと、ここ数日トライアルを重ねてきた結果を報告します！

最後は想像以上の驚きのクオリティになったので、ぜひ見て行ってください！

# 2. ChatGPT と考えた依頼フォーマットと生成例

## 2.1. 依頼文のフォーマット

　一番初めに、ChatGPTにソースコードだけを渡して、「このソースコードのマニュアルを書いて」と色々と試してみたのですが、思ったようなマニュアルが出てきませんでした。

ソースコードだけではうまくマニュアルを作れないようなので、以下のようなイメージでソースコードと一緒に送る依頼文のフォーマットの追加を検討しました。このフォーマット作成作業自体を ChatGPT に手伝ってもらい、その過程もできれば紹介したいのですが、本記事に書くには長すぎるので省略します（いつか記事書けるといいな...）。

![input.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3130775/8f9a6e63-7db1-7bc4-6bdd-d4ffa1a967af.png)


最終的に ChatGPT と相談してできあがった、依頼文のフォーマットはこちらです。

**依頼文のフォーマット**

* ソフトウェアのソースコード（全ファイルまたは主要ファイル）
* ソフトウェアの目的・機能の概要
* 使用されているプログラミング言語とバージョン
* 主要な外部ライブラリや依存関係（あれば）
* READMEファイル（もしあれば）

上の内容だけだと、ちょっとわかりにくいので、例題を作ってもらいました。

**改善された入力フォーマットへの入力例**

- ソフトウェアのソースコード: [example_source_code.zip](https://www.example.com/example_source_code.zip)
- ソフトウェアの目的・機能の概要: シンプルなWebアプリケーションで、ユーザーがTODOリストを作成・管理できる
- 使用されているプログラミング言語とバージョン: Python 3.8
- 主要な外部ライブラリや依存関係: Flask, SQLAlchemy 
- READMEファイル: [README.md](https://www.example.com/README.md)

これを書いてソースコードと送る程度なら、面倒くさがりの私でもできそうです。

## 2.2. 生成例

　ChatGPTが出してくれた上記例題を使って、どのようなマニュアルが完成するのかをテストしてもらいました。

:::note info
ソースコードはこちらからは与えていないので、コードはChatGPTが頭の中でイメージしているようです。
:::

以下が生成されたマニュアルのスクショです（冒頭のみ）。

![image01-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3130775/d9683469-35ef-33cf-4944-a2146a7059f9.png)


すべてスクショすると長くなるので、以下マークダウンを貼りつけます。

---
シンプルなTODOリストWebアプリケーション - 解読書

概要:
このシンプルなTODOリストWebアプリケーションは、Python 3.8を使用し、FlaskとSQLAlchemyを主要な外部ライブラリとして利用しています。ユーザーはこのアプリケーションを通じて、TODOリストの作成や管理ができます。

アーキテクチャ:
MVC (Model-View-Controller) アーキテクチャが採用されており、RESTful APIを使用したクライアント-サーバーアーキテクチャが構築されています。

開発環境:
開発はmacOS上のVisual Studio Codeで行われ、Gitを用いてバージョン管理が行われています。

テスト環境:
テストにはpytestが使用され、GitHub Actionsを用いたCI/CD環境が構築されています。

設定ファイル:
- config.py: データベース接続情報を含む
- .env: APIキー、ポート番号などの環境変数を設定

使用方法:
README.mdファイルには、インストール手順や起動方法が記載されています。

デプロイ方法:
README.mdファイルにデプロイ手順が記載されています。

---

さすがに使用方法やデプロイ方法だけは自分で書かないといけないようですが，やっとほしいイメージに近いものが出てきました！

# 3. 実際にマニュアルを作ってもらおう！

　例題の結果が良かったので、実際にソースコードと依頼文からマニュアルを作ってもらおうと思います。

## 3.1. 例題1: 猫の品種識別アプリ

　こちらも別の記事で紹介したいと思っていますが、以下のような、猫の品種を判別する Deep Learning アプリケーションを ChatGPTと作成しました。

![test_cat01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3130775/49f12971-6e99-dfaa-f7fd-447066d51a18.jpeg)

```
Maine_Coon: 50.59%
Ragdoll: 44.43%
Persian: 3.62%
```

このソフトウェアのマニュアルを生成してもらいたいと思います。

### 3.1.1. ソースコード

いずれも Python で作成しています。長いので、折りたたんだものを貼り付けます。

<details><summary>データセットを作成するときに使用するコード: create_dataset.py</summary><div>

```python: create_dataset.py
# !wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz
# !wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz

# !tar -xvf images.tar.gz
# !tar -xvf annotations.tar.gz

import os
import shutil
import random

def create_cat_dataset(src_dir, dest_dir, train_ratio=0.8):
    if not os.path.exists(dest_dir):
        os.makedirs(dest_dir)
        os.makedirs(os.path.join(dest_dir, "train"))
        os.makedirs(os.path.join(dest_dir, "val"))

    cat_breeds = ["Abyssinian", "Bengal", "Birman", "Bombay", "British_Shorthair", "Egyptian_Mau", "Maine_Coon", "Persian", "Ragdoll", "Russian_Blue", "Siamese", "Sphynx"]

    cat_files = {breed: [] for breed in cat_breeds}
    
    for f in os.listdir(src_dir):
        for breed in cat_breeds:
            if f.endswith(".jpg") and f.startswith(breed):
                cat_files[breed].append(f)
                break

    for breed in cat_breeds:
        random.shuffle(cat_files[breed])

        train_breed_dir = os.path.join(dest_dir, "train", breed)
        val_breed_dir = os.path.join(dest_dir, "val", breed)

        os.makedirs(train_breed_dir)
        os.makedirs(val_breed_dir)

        split_idx = int(len(cat_files[breed]) * train_ratio)
        train_files = cat_files[breed][:split_idx]
        val_files = cat_files[breed][split_idx:]

        for f in train_files:
            shutil.copy(os.path.join(src_dir, f), os.path.join(train_breed_dir, f))

        for f in val_files:
            shutil.copy(os.path.join(src_dir, f), os.path.join(val_breed_dir, f))

src_dir = "images"
dest_dir = "cat_dataset"

create_cat_dataset(src_dir, dest_dir)
```

</div></details>

<details><summary>メインのコード: cat_breed_classifier.py</summary><div>

```python: cat_breed_classifier.py
import argparse
import os
import sys

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torchvision import datasets, models, transforms
from PIL import Image

# 学習の関数
def train_model(model, criterion, optimizer, scheduler, dataset_path, num_epochs=25):
    # データの読み込みと前処理
    # 画像のリサイズ、クロップ、正規化などを行う
    data_transforms = {
        'train': transforms.Compose([
            transforms.RandomResizedCrop(224),  # ランダムなリサイズとクロップ
            transforms.RandomHorizontalFlip(),  # ランダムな水平反転
            transforms.ToTensor(),  # テンソルへの変換
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  # 正規化
        ]),
        'val': transforms.Compose([
            transforms.Resize(256),  # リサイズ
            transforms.CenterCrop(224),  # 中心をクロップ
            transforms.ToTensor(),  # テンソルへの変換
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  # 正規化
        ]),
    }

    # 画像データセットの読み込み
    image_datasets = {x: datasets.ImageFolder(os.path.join(dataset_path, x), data_transforms[x]) for x in ['train', 'val']}
    # データローダーの作成
    dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4, shuffle=True, num_workers=4) for x in ['train', 'val']}
    # データセットのサイズを取得
    dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
    # クラス名を取得
    class_names = image_datasets['train'].classes

    # GPUが利用可能な場合はGPUを使用し、そうでない場合はCPUを使用する
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    # 各エポックを処理
    for epoch in range(num_epochs):
        # 学習フェーズと検証フェーズを処理
        for phase in ['train', 'val']:
            # 学習モードか検証モードを設定
            if phase == 'train':
                model.train()
            else:
                model.eval()

            # 損失と正解数を初期化
            running_loss = 0.0
            running_corrects = 0

            # 各バッチを処理
            for inputs, labels in dataloaders[phase]:
                # 入力データとラベルをデバイスに転送します。
                inputs = inputs.to(device)
                labels = labels.to(device)

                optimizer.zero_grad()

                # 順伝搬（学習時のみ勾配計算を有効に）
                with torch.set_grad_enabled(phase == 'train'):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)
                    loss = criterion(outputs, labels)

                    # 逆伝搬と最適化（学習時のみ）
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()

                # 統計情報を更新
                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels.data)

            # 学習時には学習率スケジューラを更新
            if phase == 'train':
                scheduler.step()

            # エポックの損失と正解率を計算
            epoch_loss = running_loss / dataset_sizes[phase]
            epoch_acc = running_corrects.double() / dataset_sizes[phase]

            # 損失と正解率を表示
            print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))

    # 学習済みモデルを返す
    return model


# 画像の読み込みと前処理
def load_image(image_path):
    img = Image.open(image_path)
    preprocess = transforms.Compose([
        transforms.Resize(256), # リサイズ
        transforms.CenterCrop(224), # 中心をクロップ
        transforms.ToTensor(), # テンソルへの変換
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 正規化
    ])
    img_tensor = preprocess(img)
    img_tensor.unsqueeze_(0)
    return img_tensor

# 予測の関数
def predict(model, image_path, class_names, top_k=3):
    img_tensor = load_image(image_path)
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    img_tensor = img_tensor.to(device)
    model.eval()
    with torch.no_grad():
        outputs = model(img_tensor)
        _, preds = torch.topk(outputs, top_k, dim=1)
        probabilities = torch.softmax(outputs, dim=1)
        top_probabilities, top_indices = torch.topk(probabilities, top_k, dim=1)

    return [(class_names[index], float(prob)) for index, prob in zip(top_indices[0], top_probabilities[0])]


# メイン関数
def main(args, class_names, device, num_epochs):
    if args.mode == 'train':
        # 学習モード
        # モデルの読み込み
        model_ft = models.resnet18(pretrained=True)
        num_ftrs = model_ft.fc.in_features
        model_ft.fc = nn.Linear(num_ftrs, len(class_names))
        model_ft = model_ft.to(device)

        # 損失関数と最適化アルゴリズムの定義
        criterion = nn.CrossEntropyLoss()
        optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)
        # 学習率スケジューラの定義
        exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)

        # モデルの学習
        model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler, args.dataset_path, num_epochs=num_epochs)
        # 学習済みモデルの保存
        torch.save(model_ft.state_dict(), args.model_path)

    elif args.mode == 'predict':
        # 予測モード
        # モデルの読み込み
        model_ft = models.resnet18()
        num_ftrs = model_ft.fc.in_features
        model_ft.fc = nn.Linear(num_ftrs, len(class_names))

        # 学習済みモデルのパラメータをロード
        model_ft.load_state_dict(torch.load(args.model_path))
        model_ft = model_ft.to(device)

        # 画像に対して予測を実行
        predictions = predict(model_ft, args.image_path, class_names, top_k=args.top_k)
        # 予測結果を表示
        for breed, prob in predictions:
            print(f"{breed}: {prob * 100:.2f}%")


if __name__ == '__main__':
    # クラス名の定義
    class_names = ['Abyssinian', 'Bengal', 'Birman', 'Bombay', 'British_Shorthair',
            'Egyptian_Mau', 'Maine_Coon', 'Persian', 'Ragdoll', 'Russian_Blue',
            'Siamese', 'Sphynx']
    # デバイスの設定
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    # エポック数の設定
    num_epochs = 25

    # コマンドライン引数のパーサーの定義
    parser = argparse.ArgumentParser()
    parser.add_argument('--mode', choices=['train', 'predict'], required=True, help="実行モードを指定（'train'または'predict'）")
    parser.add_argument('--model_path', required=True, help='学習済みモデルのファイルパスを指定')
    parser.add_argument('--dataset_path', help='データセットのフォルダパスを指定（学習モード時に使用）')
    parser.add_argument('--image_path', help='分類したい猫の画像ファイルへのパスを指定（予測モード時に使用）')
    parser.add_argument('--top_k', type=int, default=3, help='上位k個の予測結果を表示する（デフォルトは3）')

    # コマンドライン引数の解析
    args = parser.parse_args()

    # メイン関数の実行
    main(args, class_names, device, num_epochs)
```

</div></details>

詳しいコードの説明は、このあと ChatGPT がしてくれるので、省略します。

### 3.1.2. ChatGPTへの依頼方法

　本記事で検討しているマニュアル生成手順は、どの ChatGPT が相手でも同じように実行できるようにしたいです。

そのために、「他の ChatGPT に同じ作業をお願いするときは、どう頼めばいいか」と ChatGPT に聞いてみました。

![image03-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3130775/2b23ce67-0be3-77fe-1af2-88648637bee9.png)

人が読むと、文章に違和感があるのですが、このまま使用します。

:::note info
具体的な使い方としては、上の文章に続けて、ChatGPT に
「以下に示すのが実際の入力フォーマット，ソースコードです。そこから、上記の指示にしたがってソフトウェアの解読書を作成してください。」
などと書いたあと、依頼文とソースコードを貼って送るだけです。
:::

:::note info
上の文章を自分で違和感がないように修正してみたりしたのですが、むしろ結果が悪くなることが多かったので、修正せずにこのまま使っています。
:::

<details><summary>上記文章をコピーして使用してみたい人用</summary><div>

<br>

いいねください！

```md
依頼内容の概要：

このチャットでは、私たちAIにソフトウェアのソースコードを解析し、その振る舞いをエンジニア向けに説明する解読書を作成してもらいたいという依頼がありました。そのために、依頼者が提供すべき情報のフォーマットを提示し、改善を行いました。また、依頼者が提供できる情報が限られている場合、私たちAIはソースコードからできるだけ多くの情報を抽出し、機能やアーキテクチャを理解するよう努力します。

以下が改善後の依頼文のフォーマットです。

- ソフトウェアのソースコード（全ファイルまたは主要ファイル）
- ソフトウェアの目的・機能の概要
- 使用されているプログラミング言語とバージョン
- 主要な外部ライブラリや依存関係（あれば）
- READMEファイル（もしあれば）

私たちAIは、提供された情報に基づいて解読書を作成し、以下のように対応します。

1. ソースコードからできるだけ多くの情報を抽出し、機能やアーキテクチャを理解しようとします。
2. 標準的なライブラリやツールが使用されている場合、それらのドキュメントやチュートリアルを参考にして、アプリケーションの動作を理解します。
3. ソースコード内のコメントやREADMEファイルを利用して、開発者の意図やアプリケーションの使用方法を理解します。
```

</div></details>

### 3.1.3. 生成結果

依頼文は以下のようにしました。

- ソフトウェアのソースコード： (cat_breed_classifier.py, create_dataset.py）
- ソフトウェアの目的・機能の概要：写真から猫の品種を判別するアプリケーション
- 使用されているプログラミング言語とバージョン：Python 3.8
- 主要な外部ライブラリや依存関係：PyTorch 2.0
- READMEファイル：README.md

この依頼文と、ソースコードから、ChatGPTが生成してくれた結果（マニュアル）は以下になります。

![image04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3130775/cd4ee0f9-1cc8-5da2-9841-ffd0a2889b9b.png)

以下全文です。

---

### ソフトウェアの解読書

このソフトウェアは、猫の品種を判別するアプリケーションです。Python 3.8を使用しており、PyTorch 2.0という機械学習ライブラリを主に利用しています。ソースコードは、`cat_breed_classifier.py` と `create_dataset.py` の2つのファイルから構成されています。
#### cat_breed_classifier.py

このファイルは、猫の品種分類モデルの学習と予測を行うメインのスクリプトです。
##### 主な関数 
- `train_model()`: モデルの学習を行います。データセットの読み込み、前処理、モデルの学習、および検証が含まれます。 
- `load_image()`: 画像ファイルを読み込み、前処理を行い、テンソルに変換します。 
- `predict()`: 学習済みのモデルを使って、画像から猫の品種を予測します。上位k個の予測結果も提供されます。 
- `main()`: 学習モードと予測モードの両方で実行されるメイン関数です。コマンドライン引数に応じて、適切なモードを実行します。
#####  学習

学習時には、ResNet-18という事前学習済みのニューラルネットワークモデルを使用しています。最後の全結合層は、猫の品種数に対応する出力ユニット数に置き換えられます。学習には、クロスエントロピー損失関数、SGDオプティマイザー、および学習率スケジューラが使用されています。
##### 予測

予測時には、学習済みのモデルをロードし、入力画像に対して順伝搬を実行して、猫の品種の確率分布を取得します。最も確率の高いk個の品種とその確率が表示されます。
####  create_dataset.py

このファイルは、猫の品種分類モデルの学習のためのデータセットを作成するためのスクリプトです。元の画像ファイルが格納されたディレクトリから、学習用と検証用のデータセットを作成します。
#####  主な関数
- `create_cat_dataset()`: この関数は、元の画像ファイルが格納されたディレクトリを指定し、画像データを学習用と検証用のデータセットに分割します。また、各データセットのディレクトリ構造を作成し、品種ごとにサブディレクトリを作成します。 
- `split_dataset()`: 与えられたデータセットを学習用と検証用に分割し、指定された比率で画像を分配します。
- `copy_images()`: 分割されたデータセットを、それぞれの品種のサブディレクトリにコピーします。
- `main()`: このスクリプトのエントリーポイントです。コマンドライン引数に応じて、適切なデータセット作成オプションを実行します。

#### データセット作成
このスクリプトを実行すると、指定された元の画像ファイルディレクトリから、学習用データセットと検証用データセットが作成されます。デフォルトでは、データセットは8:2の比率で分割されます。各データセットは、品種ごとにサブディレクトリが作成され、画像ファイルがコピーされることで構成されます。

#### 使い方
1. まず、猫の画像データを格納したディレクトリを用意します。各画像ファイルは、対応する猫の品種名で名付けられていることが推奨されます。
2. 次に、create_dataset.py を実行し、学習用データセットと検証用データセットを作成します。
3. cat_breed_classifier.py を実行し、学習モードで猫の品種分類モデルを学習させます。
4. 学習が完了したら、cat_breed_classifier.py を予測モードで実行し、猫の画像を入力して品種を予測します。

このソフトウェアを使用することで、猫の品種を識別するアプリケーションを容易に構築できます。また、このソフトウェアはオープンソースであり、自由に改変および再配布が可能です。

___


###  3.1.4. コメント

最高だ...

ただ中をよく読むと、create_dataset.py の主な関数に誤りがあります。`create_cat_dataset()` はありますが、他の関数は存在しません。間違えてしまうことはあるようなので、すべてを鵜呑みせずにチェックするようにした方がよいと思います。

（人も間違えることはあるので、個人的にはここまで作ってくれたんだからと、100%にならないことは気にしてはいません。）

:::note info
**小ネタ** 
* 何回か実行すると結果が変わることがあります（入力の指示が足りていないのかもしれません）。思っているのと違うなとなったときは、望みの結果になるまで、リトライするといいかもしれません。
* 長すぎるコードを送ると「続けてください」と言っているのに，続きを書かずに始めから書き直すことが何回かありました。そのときは、「このあとから、続きから書いてください」と、ChatGPTの回答を後ろに貼り付けてお願いするとうまくいくことが多かったです。
:::

## 3.2. 例題2: GUIアプリ

　先ほどうまくいったのは、ChatGPT自身が作成したアプリだからなのではないかと疑い、次は私がフルスクラッチで作成したコードを使用します。

ソースコード全文と概要はこちらの記事をご確認ください。ちょっとおしゃれなGUIの作り方を紹介しています。

https://qiita.com/key353/items/e59927dcf20916f9d4ac

![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3130775/5fcc9cc7-a664-4656-91b0-8b0d336b824f.gif)


2例目なので詳細は割愛し、ChatGPTにより生成された結果だけを貼り付けます。

依頼文はこちら。

- ソフトウェアのソースコード：（例：plot_view_gui.py, plot_control.py）
- ソフトウェアの目的・機能の概要：CSVファイルからプロットを行うおしゃれなGUIアプリ
- 使用されているプログラミング言語とバージョン：Python 3.8
- 主要な外部ライブラリや依存関係：CustomTkinter, matplotlib, pandas, numpy
- READMEファイル：README.md

以下，生成されたマニュアル全文です。

---

### ソフトウェア解読書: CSVデータプロットGUIアプリ
#### 概要

このGUIアプリケーションは、CSVファイルからデータを読み込み、データをプロットする機能を提供します。アプリケーションはPythonのTkinterライブラリを使用して構築されており、2つのファイルで構成されています。
1. plot_control.py: アプリケーションのコントロール機能を定義したファイル
2. plot_view_gui.py: アプリケーションのGUI構成要素を定義したファイル

#### ファイル構成
##### plot_control.py

このファイルでは、CSVデータを読み込み、プロットに関する制御を行うクラスが定義されています。以下にその主なクラスと機能をリストアップします。
- CsvData: CSVファイルからデータを読み込み、データ操作を提供するクラス
- PlotControl: プロットに関する制御を行うクラス。プロット設定の変更やプロットの更新を行います。

##### plot_view_gui.py

このファイルでは、アプリケーションのGUI構成要素を定義しています。以下にその主なクラスと機能をリストアップします。
- App: アプリケーションのメインウィンドウを表すクラス。フォームのセットアップを行い、CSVファイルのパスを管理します。また、update_canvasメソッドを持っており、プロットを更新することができます。
- ReadFileFrame: ファイル読み込み機能を持つフレーム。CSVファイルのパスを指定するテキストボックス、ファイルを選択するボタン、ファイルを開くボタンが含まれています。ファイルを開くボタンが押されると、親クラスであるAppクラスのupdate_canvasメソッドが呼び出されてプロットが更新されます。
- PlotMainFrame: プロット表示用のメインフレーム。プロットが表示されるキャンバス、プロットの設定を変更するサブフレーム、プロットを保存するボタンが含まれています。プロットの更新は、updateメソッドによって行われます。
- PlotConfigFrame: プロットの設定を調整するためのサブフレーム。線の太さを変更するスライダーや、線種を選択するコンボボックスが含まれています。これらのウィジェットが変更されると、親クラスであるPlotMainFrameクラスのupdateメソッドが呼び出されてプロットが更新されます。

#### アプリケーションの動作手順

1. メインウィンドウが表示され、ReadFileFrameとPlotMainFrameが配置されます。
2. ReadFileFrameでCSVファイルを選択し、開くボタンを押すと、プロットがPlotMainFrameのキャンバスに表示されます。
3. PlotConfigFrameで線の太さや線種を調整すると、プロットが更新されます。
4. PlotMainFrameの保存ボタンを押すと、プロットが画像ファイルとして保存されます。

#### カスタマイズと拡張

このアプリケーションはカスタマイズや拡張が容易にできるように設計されています。以下にいくつかのカスタマイズや拡張の例を示します。 

1. グラフの種類を増やす: 現在のアプリケーションでは折れ線グラフのみがサポートされていますが、棒グラフや円グラフなど他のグラフタイプをサポートするように拡張することができます。これには、PlotControlクラスに新しいグラフタイプの描画ロジックを追加し、PlotConfigFrameにグラフタイプを選択する新しいウィジェットを追加する必要があります。 
2. プロットの複数表示: 1つのCSVファイルに複数のデータセットが含まれている場合、それらを一度に表示する機能を追加することができます。これには、PlotMainFrameに複数のキャンバスを追加し、PlotControlクラスに複数のデータセットを扱うロジックを追加する必要があります。 
3. データフィルタリングや変換: CSVデータに対してフィルタリングや変換を行う機能を追加することができます。これには、CsvDataクラスにデータ操作のロジックを追加し、それに対応するGUIウィジェットをReadFileFrameやPlotConfigFrameに追加する必要があります。

このように、このアプリケーションは機能追加やカスタマイズに対応しやすい設計がなされています。エンジニアは、アプリケーションの要件に応じて独自の機能を容易に追加することができます。

#### 開発環境と依存関係

このアプリケーションはPythonで開発されており、以下の環境で動作確認済みです。
- Python 3.6以上

また、以下の外部ライブラリに依存しています。
- Tkinter: Pythonの標準GUIライブラリ
- pandas: CSVファイルの読み込みやデータ操作に使用されています
- matplotlib: グラフの描画や画像ファイルへの保存に使用されています

#### まとめ

このソフトウェア解読書では、CSVデータプロットGUIアプリケーションの概要、ファイル構成、アプリケーションの動作手順、カスタマイズと拡張、開発環境と依存関係について説明しました。この情報をもとに、エンジニアはアプリケーションの理解やカスタマイズを行うことができます。

---

### 3.2.1. コメント

例題1 よりパワーアップして帰ってきました。素晴らしい。

ただ，こちらも`plot_control.py` に `CsvData` クラスは存在しないです。ここまで書いてくれれば個人的に十分だとは思いますが、ChatGPT への依頼の仕方を改良すれば，もしかするとこのようなミスはなくなるかもしれません。他は完ぺきだと思います。

> ###カスタマイズと拡張
> このアプリケーションはカスタマイズや拡張が容易にできるように設計されています。以下にいくつかのカスタマイズや拡張の例を示します。

特にこの部分について私は何も言っていないし、コードにも書いていないです。

自分が頑張って作ったコードを、このように説明してくれるなんて、最高じゃないでしょうか。 

# 4. まとめ

　本記事では、ソフトウェアのマニュアル作りについていかに楽をするかのために、ChatGPTを使ってマニュアル作りを効率化する例と実践的な方法を紹介しました。

私がイメージしていた以上のものが出てきてしまって、この結果に文句はないのですが、十分に ChatGPT の力が引き出せているかというと、まだまだ依頼文や指示方法などに改良の余地がありそうです。

予想以上にいいねをいただいた（ありがとうございます）GUI 作成記事の続きも書きたいし，あれも書きたい、これも書きたいと時間がなさすぎますが、こんなに生き生きとするのも久しぶりなので頑張りたいと思います！