あの日VBAを知らない私がVBAを作成しちゃった

## はじめに
GxPの蔡です。
本記事は[グロースエクスパートナーズアドベントカレンダー2023](https://qiita.com/advent-calendar/2023/gxp)の4日目の記事です。

　まずは、少し自己紹介をしていきたいと思います。
　私は2023年4月入社した新卒社員です。入社前は大学院で臨床心理学専攻していました。文系で心理学なのになぜIT企業に来たかと言いますと、個人の興味もありますが、時代の流れという観点でIT技術を学ぶことに損はないと思い、趣味で勉強するとともに、プロも目指せばもっと有意義ではないかと思ったからです。
　これまでコードを書いたことがなく、全くの初心者ですが、ChatGPTが誕生したことで、プログラムを書くことが簡単になったと感じました。もちろん、メリットとデメリットもありましたが、今回はChatGPTを使って作業効率化のためのVBAを作成ことについて書きます。

 
 ## 背景
 　前述したように、VBAを使うことにしましたが、なぜExcelのVBAが必要だったかについて述べます。

 　今参加している業務では、お客様の業務内容のデジタル推進をすることですが、短時間ですべての内容をデジタル化しても不現実なので、今の段階で改善できるようなところを整理していき、業務改善をするという言い方のほうが正しいかもしれません。そこで、現状についてフローチャートを作成し、視覚化することによって、その課題や強みと弱みを見出していく必要があるとなりました。
 　業務連携の面を考慮して、Excelでフローチャートを作成することが妥当でした。フローチャートには主に、Excelの“挿入-図形-正方形/長方形”と“挿入-図形-線矢印”を使用しています(図1.)。

　しかし、このままでは色が黒白と青い線しかないので、少々見落としやすいのではないかと感じました。では、もっと見やすくするためにはどうしたほうが良いでしょう？図1.でいえば、書類が送られています。書類によってPDFやExcel、Wordなど形式が異なりますので、色付けたり、アイコンを付けたら見やすくなるのではないかと思いました(図2.)。
　
```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 17
}
</style>
footer  図１.　フローチャートの例

 title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623108/a647f010-c337-95aa-1094-d4229cc88101.png>
```



```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 17
}
</style>
footer  図２.　アイコン付けたフローチャート

 title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623108/3b7ce3f7-fac8-71bd-fa84-dda8f3188d9b.png>
 ```

　アイコンを追加したことによって、どんな書類が送られているかが見やすくなりました。こういった図形が300個あっても一目瞭然になるかと思いました。
　ここで問題ですが、会社間のやり取りでは書類を1回送付しただけでは終わりません。その書類に基づいて何かの資料が作成され、ほかの所に送ったり、どこかに保存したりします。そんな書類を一つ一つ手動でアイコンを貼り付けると、ミスが起こりやすく、時間がかかってしまいます。なにより、面倒です。
　そこで、貼り付けるという面倒な作業を自動化したいと思いました。

 ## VBAマクロの作成
 　VBAを学んでいないので、どこから始めればいいか分かりませんが、一から勉強したくとも、すぐに作れる自信がありません。さらに、提出期限もあれば、制限された時間で成果を出す必要があります。そこでChatGPTの力を借りて、とりあえずアイコン貼り付けという作業を自動化してみました。


  ### 手順
  
  今回使ったのは[GPT-4](https://chat.openai.com)

  ChatGPTにVBA作成をする前に、まずどんなことを実現させたいかを具体的に考えました。
1. アイコンの貼り付けだけをしてほしい。
1. アイコンの位置を図形の左上に固定したい。
1. アイコンのサイズを変更できるようにしたい。(大きさを統一したい)
1. 貼ってほしい図形のみに貼り付けてほしい。(書類のみ、正方形は貼り付けない)

　そして、貼り付け元のアイコンを準備しました。アイコンはこれから増えるかもしれませんので、どのように使うアイコンを指定するのかが問題です。セルで指定するか、画像のIDで指定するか、名前で指定するかについても迷いましたが、画像の名前で指定するほうが良い気がしました。
 
 　名前は図3.のように、"フローチャート:書類１"という図形名であることが分かりました。次は必要な部分をChatGPTに明言して作ってもらいます。

 ```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 17
}
</style>
footer   図３.　図形の名前の見え方

 title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623108/ce521f59-8083-b0a1-5e33-0c711591b4a0.png>
 ```


### 試行錯誤
　```VBAを書いてください。[業務フロー]シートに指定した図形の左上に[凡例]シートの指定したアイコンを貼り付けたい。[凡例]には"pdf_icon"や"excel_icon"などがあり、こういうアイコンを[業務フロー]シートにある"フローチャート:書類"という文字が含まれた図形すべてに貼り付けたい。"フローチャート:書類"には内容が異なる文字があり、"書類1"はPDF、"書類2"はPDF、"書類3"はExcel、"書類4"はExcelである。ExcelやPDFに応じてexcel_iconとpdf_iconを付けたい。サイズは約30pxに指定したい。```
 
　以上のプロンプトを入れたら以下のコードが返信された：
 ```
 Sub AddIconToShapes()

    Dim wsFlow As Worksheet, wsLegend As Worksheet
    Dim shp As Shape, icon As Shape
    Dim iconName As String
    Dim iconSize As Double
    
    ' Initialize worksheets
    Set wsFlow = ThisWorkbook.Worksheets("業務フロー")
    Set wsLegend = ThisWorkbook.Worksheets("凡例")
    
    iconSize = 30 ' Set icon size to 30px
    
    ' Loop through each shape in the Flow sheet
    For Each shp In wsFlow.Shapes
        ' Check if the shape is "フローチャート: 書類"
        If InStr(shp.Name, "フローチャート: 書類") > 0 Then
            Select Case shp.TextFrame.Characters.Text
                Case "書類3", "書類4"
                    iconName = "excel_icon"
                Case "書類1", "書類2"
                    iconName = "pdf_icon"
                ' Add more cases as needed
                Case Else
                    iconName = ""
            End Select
            
            ' If icon name found, add the icon to the shape's top left corner
            If iconName <> "" Then
                Set icon = wsLegend.Shapes(iconName).Duplicate
                icon.Top = shp.Top
                icon.Left = shp.Left
                icon.Width = iconSize
                icon.Height = iconSize
                icon.Cut
                wsFlow.Paste
            End If
        End If
    Next shp

End Sub
```

　ExcelにAlt＋F11でVBAの画面が現れます、「挿入」の「標準モジュール」を選択すると、コード入力できるようになります。
 　そこで、早速試してみましたが、`error1004:指定した名前のアイテムが見つかりませんでした`が出現した。よく見たら、「凡例」シートのIcon名が"Picture"のままでした。
 　「凡例」シートのIcon名を修正し、もう一度実行したら、エラーなく実行できた。アイコンを貼り付けてもらったものの、全部変な位置に貼り付けていた(図4.)。

 ```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 25
}
</style>

footer 図４.　一回目の生成されたVBAのテスト結果

 title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623108/714400cc-afb9-7a56-32cc-07ad73aa2fad.png>
 ```

　`エラーなく実行成功した。しかし、アイコンをコピーペーストしているが、図形の左上には貼り付けていなかった。`とChatGPTにどうなったかを伝えた。

 GPTはPasteのメソッドをShapeRangeのPasteメソッドに変更し、位置を調整していくと返事した。
 ```
 Sub AddIconToShapes()

    Dim wsFlow As Worksheet, wsLegend As Worksheet
    Dim shp As Shape, icon As Shape
    Dim iconName As String
    Dim iconSize As Double
    
    ' Initialize worksheets
    Set wsFlow = ThisWorkbook.Worksheets("業務フロー")
    Set wsLegend = ThisWorkbook.Worksheets("凡例")
    
    iconSize = 30 ' Set icon size to 30px
    
    ' Loop through each shape in the Flow sheet
    For Each shp In wsFlow.Shapes
        ' Check if the shape is "フローチャート: 書類"
        If InStr(shp.Name, "フローチャート: 書類") > 0 Then
            Select Case shp.TextFrame.Characters.Text
               Case "書類3", "書類4"
                   iconName = "excel_icon"
               Case "書類1", "書類2"
                   iconName = "pdf_icon"
                ' Add more cases as needed
                Case Else
                    iconName = ""
            End Select
            
            ' If icon name found, add the icon to the shape's top left corner
            If iconName <> "" Then
                Set icon = wsLegend.Shapes(iconName).Duplicate
                icon.Top = shp.Top
                icon.Left = shp.Left
                icon.Width = iconSize
                icon.Height = iconSize
                icon.Cut
                wsFlow.PasteSpecial Format:="Picture"
                Set icon = wsFlow.Shapes(wsFlow.Shapes.Count)
                icon.Top = shp.Top
                icon.Left = shp.Left
            End If
        End If
    Next shp

End Sub
 ```

　今回もエラーが現れ(図5.)、`wsFlow.PasteSpecial Format:="Picture"`に問題があったようです。エラーメッセージをそのままChatGPTに返信しました。

  ```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 23
}
</style>
footer   図５．実行時のエラー

 title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623108/e76631a8-5e19-d77d-c70f-60c823188f4b.png>
 ```

　`wsFlow.PasteSpecial Format:="Picture"`を`wsFlow.Paste`に変更しましたら、図形に貼り付け成功しました(図6.)。しかし、これではどんなファイルなのか文字が隠されて分からなくなってしまいます。アイコンを25pxに変更し、アイコンの左半分を図形の左側に突き出るようにしたいと、ChatGPTに注文しました。
 
  ```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 22
}
</style>
footer   図６．アイコン貼り付け成功した例

 title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623108/0d05ff1b-8ef5-d702-31bc-94fb60b9136a.png>
 ```


 ```
 ...            
            ' If icon name found, add the icon to the shape's top left corner
            If iconName <> "" Then
                wsLegend.Shapes(iconName).Copy
                wsFlow.Paste
                Set icon = wsFlow.Shapes(wsFlow.Shapes.Count)
                icon.Top = shp.Top
                icon.Left = shp.Left - (iconSize / 2) ' adjust to half outside
                icon.Width = iconSize
                icon.Height = iconSize
            End If
 ...
 ```

 　上記がアイコンの位置を調整したコードでしたが、実行する時にエラーが出たり出なかったりしていました。`wsFlow.Paste`の行に黄色く光っていましたので、このエラーもChatGPTに返信しました。
  　何回かのやり取りをして、ChatGPTが勝手にcutのメソッドを使用したり、凡例にあった元となるアイコンをコピーした後に削除したりすることもありましたが、そういったところについて`削除しないでください、コピーとペーストのみにしてください`を返信中に明言しました。　
 
  ```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 23
}
</style>
footer   図７．連続実行時に現れたエラーコード

 title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623108/eeb457cf-b2fd-9e9f-aa69-dd3c84a69e01.png>
 ```

```
    ' If icon name found, add the icon to the shape's top left corner
    If iconName <> "" Then
        wsLegend.Shapes(iconName).Copy
        wsFlow.PasteSpecial Format:="Picture (Enhanced Metafile)"
        Set icon = wsFlow.Shapes(wsFlow.Shapes.Count)
        icon.Top = shp.Top
        icon.Left = shp.Left - (iconSize / 2) ' adjust to half outside
        icon.Width = iconSize
        icon.Height = iconSize
    End If
```
　連続実行する時にエラーが出たり出なかったりすることについて、ChatGPTは連続実行中に遅延が発生した可能性があるということで、以下のようにApplication.Waitを追加することになりました。
 　1秒のインターバルを入れているようで、実行必要なシートの数が多くなると、効率が悪くなると思いますが、今の段階ではまだ許容範囲内と思いましたので、このままにしました。必要な場合はこの行を削除すれば良いと思いました。
  　また、これからも機能を追加したり、VBAを勉強していきたいと思いましたので、その時に必要があればまた修正していきたいと思います。
```
...

' If icon name found, add the icon to the shape's top left corner
If iconName <> "" Then
    wsLegend.Shapes(iconName).Copy
    Application.Wait Now + TimeValue("0:00:01") ' Wait for 1 second
    wsFlow.PasteSpecial Format:="Picture (Enhanced Metafile)"
    
    ...
End If

...
```
  ```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 27
}
</style>
footer   図８．アイコン貼り付ける位置を調整した例

 title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623108/4e89ce12-a85b-079c-1bf8-0499c6d5e6ef.png>
 ```
 
## 結果
　結果として、図8.で示したように、異なる書類に対して指定したアイコンを貼ることが可能になりました。必要な場所にほかのアイコンを手動で入れられます（メールのアイコン等）。
　このマクロのおかげで、作業フローの作成は本来のスケジュールより1日早めに完成することができました。本来のスケジュールは残り4日ぐらいで、作成には、テストと機能追加を含めて半日ほどかかりました。残り2.5日は手動修正と作成をして、30本ぐらい出来上がりました。

 ## 感想

　ChatGPTは簡単なプログラムを短時間で作成することが可能で、私のような初心者が1から勉強して作成することに時間がかかる場合はChatGPTはとても役立ちます。しかし、より完全なプログラムはやはり自分で勉強して作成するほうが良いと思いました。自分のスキル向上にも繋がり、より完全なプログラムを作成することができると思います。
 　また、ChatGPTにはまだ全体的な流れに沿って話すことに問題があり、会話が長くなると、本来していなかった内容が突然現れたりしましたので、ChatGPTには簡潔に必要なポイントをまとめて伝える必要があると思いました。
　完成度は高くないかもしれませんが、VBAへの興味を引き出すきっかけとして、ChatGPTはとても役立っていると感じました。これからもC＃やPython、Javaなど色々な言語を勉強してみたいと思います。

 ## 最後に
  
 　完全未経験者でも、複雑で難しい物は作れないかもしれませんが、ChatGPTを使えば簡単な成果は出せると思います。
  　この記事は未経験者の方にとって、興味を引き出せることができれば嬉しいです。

  　ありがとうございました。