それわVBA案件ですね

エクセルVBAネタを書いています

PDF文書の翻訳ツールを作る2(クリップボードの中身を取得する)

熱くなったり寒くなったり、お外も忙しい感じな今日この頃です


こんちくわ 壁|ω・)ノ


さて、前回の記事の続きです。
今回は作った翻訳ツールで利用したクリップボード内の文字列情報取得について紹介したいと思います。



でわいきます ̄▽ ̄)ノ



Contents



  

前回の記事では・・・

以下のことを書きました

  • 英語のPDF文献をコピーしてWeb翻訳ツールにペーストすると、文章の途中に改行コードが挿入された状態でペーストされてしまうために翻訳がうまく機能しない
  • 改行コード(Chr(10)/vbCrLf)を半角スペースに変更するマクロを作ったけれども、ワークシートにコピペして、処理してWebページに貼りつけるなど翻訳操作にメッチャ手間がかかる
  • 操作の多くを自動化して手間を削減し、かつGoogleよりも精度が高いDeepL翻訳サイトを利用するマクロを作った


fukucyndip.hatenablog.com


  

それで、作った翻訳マクロではどんな工夫したの?

前回紹介した ”改行 → 半角スペース変換” マクロを使った翻訳作業では

    1. 文献の該当部部分をコピー  
    2. エクセルのワークシートのどっかのセルをダブルクリック  
    3. ペースト  
    4. VBE起動
    5. 改行をスペースに変換マクロを起動
    6. 出来上がった文章が入ったセルをダブルクリッ  
    7. 文書全体を選択
    8. コピー
    9. ブラウザを起動(DeepL)して翻訳サイトにアクセス
    10. 文書をペーストしてEnter
    11. 待つ  


という手順が必要だったわけですが、PDF文書 → ワークシート, ワークシート → DeepLサイト へのコピペがたるいんですよねぇ。全部手作業だし。。ってか、結局改行処理しかマクロ使ってないし。

そこでふと気が付いたのが、

 クリップボードの中身を直接取り出して変数に入れれたらいいぢゃん!
 なんかできる気がする。。



そこで、Google先生に伺ったところ・・・、
ありました。VBAで取り出す方法が!




クリップボードの中身を取り出して変数に代入する

クリップボードの中身を取得するにはMSFormsライブラリの DataObject クラス のメンバーであるGetFromClipboardメソッドと GetText メソッドを利用します。

さらに、MSFormsライブラリを利用する下準備として、Microsoft Forms 2.0 Object Library を有効にするための参照設定を行います(ツール → 参照設定 → "Microsoft Forms 2.0 Object Library" チェックボックスをオンにする)

f:id:FukuCyndiP:20210227234607p:plain


コード例

Sub クリップボードの中身を取り出す()
'クリップボードの中身は文字列な前提です
    Dim cbContents As Variant
    
    Dim clipBoard As MSForms.DataObject: Set clipBoard = New MSForms.DataObject
    With clipBoard
        Call .GetFromClipboard
        cbContents = .GetText
    End With
    
    Debug.Print cbContents
    
End Sub

DataObjectクラスのメンバーを利用するために、DataObjectクラスから "clipBoard" という名前のインスタンスを新しく生成(New)しました。その後、GetFromClipboard メソッドでクリップボードインスタンス "clipBoard" に一旦移して、GetTextメソッドを使って変数 cbContents に代入しています。


これでめでたくクリップボードの中身を取り出す術を手に入れました。



クリップボードの中身を空にする

実際の翻訳作業ではPDF文書の コピー → 翻訳 を何度も繰り返すことが想定されます(少なくとも私はやる)。それで、直前にクリップボードに残っていた文字列が悪さをしないとも限らないわけで、処理としてはきちんと中身を変数に移した後はクリップボードは空にしておいた方がいい気がしました。

この空にする処理なのですが、Google先生に伺ったところ Windows API を利用との答えが返ってきて ('ω')クッ となっていたところ、Application.CutCopyMode = False が使えるということに偶然気が付きました。

ただし単独ではだめなので、ワークシートのどこかのセルのコピーとセットにします。


  Sub ClearClilpBoad()
    'ワークシートのどこでもいいのでコピーした後に、 Application.CutCopyMode = Falseしたらクリップボードが空になる
    ' Application.CutCopyMode = False単独では空にならない
    
    ActiveSheet.Range("A1").Copy
    Application.CutCopyMode = False
  End Sub

(十分に検証したわけでわありませんが、ワークシートのセルを一旦クリップボードに入れるだけなので、セルに何が入ってても大丈夫かなと。)



クリップボードの中身が空かどうかを確認する

次に、確認作業のオハナシです。
PDF文書のコピーは "選択 → 右クリ → コピー" でもいいですが、"Ctrl + C" を利用する方も多いかと思います。で、この Ctrl + C はたまにうまくいかないことがある気がします(筆者の印象のみです。未検証です)。それでもし、うまくクリップボードに目的の文字列が入らなかった場合にはもちろん翻訳が失敗するわけで、それがプログラムの不具合と誤認されては癪に障る ユーザーに不都合がないようコピーに失敗した旨教えてあげないといけません。

そこで、クリップボードに中身が入っていることを確認する処理を挟むことにしました。

VBAにはクリップボードの状態を確認するプロパティがちゃんと備え付けられていて、Application.ClipboardFormats(1) の値を参照することで、クリップボードに何のフォーマットデータが入っているか確認できます(事前の参照設定は、なんと不要)。

Value Description
0 Text
1 Value
2 Picture
5 CSV format

 などなど(詳しくは公式 https://docs.microsoft.com/ja-jp/office/vba/api/excel.xlclipboardformat をご覧ください)。

それで、空の場合は Application.ClipboardFormats(1)=-1 となりますので、これを条件構文に組み込めばよいですね。



クリップボードの中身を返すFunction プロシージャ

というわけで、プリップボードの中身を返す機能単位として以下のようなFunctionプロシージャを作りました

  Function ClipBordContents() As String
    'クリップボードの中身を取り出す
    
    'クリップボードの中身有無判定。空なら Application.ClipboardFormats(1) =-1 となる
    ' ”vbEmpty” を使いたいので、クリップボードが空なら合計値を0(ゼロ)にする
    Dim cbContent As Variant: cbContent = Application.ClipboardFormats(1) + 1
    
    Dim clipBoad As MSForms.DataObject: Set clipBoad = New MSForms.DataObject
    With clipBoad
        If Not cbContent = vbEmpty Then
            Call .GetFromClipboard
            ClipBordContents = .GetText
        Else
            Call MsgBox("文書がコピーできてませんよう", vbExclamation)
            End
        End If
    End With
    
    Call ClearClilpBoad
End Function
  
'--------------------------------------

Sub ClearClilpBoad()
    'ワークシートのどこでもいいのでコピーして、 Application.CutCopyMode = Falseしたらクリップボードが空になる
    ' Application.CutCopyMode = False単独では消えない
    
    ActiveSheet.Range("A1").Copy
    Application.CutCopyMode = False
End Sub


戻り値のあるFunction プロシージャですが、引数として受け取るものがありませんので、関数呼び出しとしては

  a=ClipBordContents()    

と、教科書に書いてある関数の記述みたいになりますが、まぁしょうがないでしょうか。。 かっこ()は省略可能ですが、省略するとそれが Functionプロシージャ であることが余計わかりにくくなるので、つけておいた方がいい気がします。



まとめ

本日の記事では、以下のことを紹介しました。

  • 当初は手作業が多くってたるかった一連の翻訳作業を全体としてマクロ化することでかなり楽ちんになった
  • コピーした文書の改行処理をメモリ上で行ったことで、文字列情報の転記が操作をなくしたことがポイント
  • クリップボード情報を変数に取り込む処理としてMSFormsライブラリの DataObject クラス のメンバーであるGetFromClipboardメソッドと GetText メソッドが利用できる

クリップボードの中身を取り出す処理というのは実は今回初めて書きましたが、実際に書いてみると思ってたよりもハードルが高くなかったと感じました。クリップボードを使ったデータのやり取りはエクセル外アプリケーションとのデータやり取りに利用できると思います。もちろん他にもっといい方法はあるのでしょうが、今回のコーディングを通じてエクセル外アプリをVBAで操作する得体のしれない恐れみたいなものがかなり軽減された気がしました。

次回はDeepLサイトに取り出した文字列を貼るコードを紹介したいと思います



でわまた~  ̄▽ ̄)ノシ



因みに

今回は取り出すだけでしたが、同じくMSFormsライブラリの DataObject クラスメンバーのSetTextメソッド、PutInClipBoardメソッドを使って書き込むこともできます。

Sub クリップボードに文字列をセットして取り出す()

    Dim cbContents As Variant
    
    Dim clipBoard As MSForms.DataObject: Set clipBoard = New MSForms.DataObject
    With clipBoard
      'クリップボードにセット
        Call .SetText("こんちくわ")
        Call .PutInClipboard

      'クリップボードから取り出す
        Call .GetFromClipboard
        cbContents = .GetText
    End With
    
    Debug.Print cbContents
    
End Sub  

SetTextメソッドで文字列をインスタンスにセットして、PutInClipBoardメソッドでクリップボードに書き込むという感じで、ちょうど取り出しとは逆のプロセスなんですね。