PowerPointでデジタルサイネージを作ろう

最近は、企業の事務所内にも大きなモニターを利用したデジタルサイネージを見るようになりました。その目的の殆どは総務部による一方的な情報提供に終始してる部分もありますが、イントラネットやグループウェアでは伝わらないような情報提供にあります。

そんなデジタルサイネージシステムには、BrightSignデジサインのようなものが多いですが、正直価格は高いです(モニターはAmazonで調達するのが安いです)。

しかし、高価なこれらデジタルサイネージなど使わずとも、手元にあるものでこれらよりもずっと優れた作品を作る手段があります。それが、PowerPoint。動画もイケて自動で紙芝居も可能。けれど一歩踏み込んでPowerPoint VBAで仕込めばより高度でデータ処理も自動化できるサイネージが作れます。

今回使用するファイルや資料

特殊なイベント用として、SlideShowBeginのサンプルも追加しました。

事前準備

ワイド画面対応にする

通常、PowerPointでファイルを作成すると昔のアニメのように4:3で作成されてしまいます。しかし、最近のモニターは16:9で構成されていることが多く、そのままでは画面の両端に黒い帯が出来てしまい、格好が悪い。まずは作り始める前にPowerPointのスライドサイズを16:9のワイド画面対応にしましょう。

  1. デザインタブを開く
  2. スライドのサイズを開く
  3. ワイド画面(16:9)を選択する

図:16:9を使うようにしましょう

VBAを有効にしたpptmファイルにする

今回は動きのある動的なデジタルサイネージコンテンツとするため、ただの紙芝居ではなくVBAを利用するため、今回のファイルはpptx形式ではなく、pptm形式で保存してあげる(でないと、VBAコードを書いても排除されてしまいます)。

  1. ファイルタブを開く
  2. 名前を付けて保存を選択する
  3. 参照をクリックして保存先を選択
  4. その際のファイルの種類について「マクロ有効プレゼンテーション」を選ぶ
  5. 保存ボタンを押す

起動時にセキュリティ警告が出ることがありますが、コンテンツの有効化をクリックしてもらえれば問題ありません。

配置したテキストボックスの名前を調べる

VBAで動的にテキストボックスの値を書き換えたりするのが今回の目的の1つですが、PowerPoint上の各種パーツには表向きプロパティが見えません。しかし、これらのコントロール名が分からなければ、書き換えようがありません。実は表向き見えないだけで、各パーツ類にはきちんと自動でコントロール名が与えられている。

このコントロール名を以下のVBAで事前に調べておき、VBA内で指定して内容を書き換えます。ちなみにこのコントロール名はスライド単位であり、スライドをコピーするとコントロール名も同じ名前のままです。ファイル内では同じコントロール名のボックスがあることになりますが、スライド単位で指定するので、問題なくアクセスが可能です。

  1. 対象のパーツをクリックする
  2. 以下のコマンドを実行する。
  3. パーツの名前とIDが返ってくるので、控えておく。
'現在選択中のシェイプパーツの名前を取得するコマンド
Public Sub getActiveShapeName()

  Dim shp As Shape

  With ActiveWindow.Selection

    If .Type = ppSelectionNone Or _
      .Type = ppSelectionSlides Then Exit Sub

    For Each shp In .ShapeRange
      Debug.Print shp.Name
      Debug.Print shp.Id
    Next shp

  End With

End Sub

OnSlideShowPageChangeのバグ対策

PowerPoint VBAを利用する上でぶつかる問題の1つに「スライド変更時にVBAを実行させる」特別なイベントにコードを書いても動かない事がある点です。2013の頃からあるバグで、OnSlideShowPageChangeという関数名で作るとそれが実行可能になっています。スライドの何枚目に来たら実行するのがポピュラーな使い方です。

VBAが実行されない理由はVBAプロジェクト画面にて、標準モジュールしかなく、対象のスライドが見えないというPowerPoint特有の問題があります。この問題の簡単な解決法はどのスライドでも良いので、適当にフォームコントロールを入れてあげること。こうする事でOnSlideShowPageChangeのメソッドがきちんと働くようになります。

  1. 適当なスライドを選択する
  2. 開発タブを開く(表示されていない場合には表示しておきましょう)
  3. ラベルコントロールなどの適当なコントロールを張り付ける
  4. Visual Basicを開く
  5. プロジェクトにSlide〇〇があればOKです。上書き保存しましょう。

図:プロジェクトにSlideがいるのを確認しましょう。

HDMIエクステンダーを準備する

今回は、VBAを利用した動的なデジタルサイネージをPowerPointで作成します。そのため、PCが必要になります。しかし、モニターに有線でPCをつなげて再生では、場所的な制約が出来てしまいます。そこで、PCは離れた場所から無線を使って表示内容を飛ばしてあげる仕組みを用意するとグッドです。

其のためのハードウェアがこのHDMIエクステンダー。自分が試してみた所、壁越しでもそこそこイケて、およそ30mはHDMIを無線化してもきちんと届きました。反応速度もMiracastやらAirPlayのようなWiFiベースよりずっと高速です。モニタ側にはHDMIエクステンダーのレシーバーとHDMIポートを接続。

PC側は送信用のHDMIトランスミッターをHDMIポートと接続します。二点間はペアリングして置きます。たったこれだけ。ClickShareのようなUSB出力ベースでも良いでしょう。中華製の安い品GUANYEEのHDMIエクステンダーですが、価格の割に結構使えています。

図:1PCで2画面に送れるスグレモノ

一歩踏み込んだ使い方

データやグラフを流用する

Excelを利用する方法

Excelなどであらかじめデータの塊を準備しておき、それらをPowerPointに持ってくることで再利用することが可能です。またこの時、Excel側のデータを修正した場合には、リンクの更新(VBAであればPresentation.UpdateLinks)をすることで反映する事が可能です。Excel側のグラフをPowerPoint側に張り付ける手順は以下の通りです。

  1. Excel側のグラフをコピーする
  2. PowerPoint側の張り付けたいスライドに移動
  3. ホームタブの張り付けボタンの下の▼をクリック
  4. 形式を選択して貼り付けを選ぶ
  5. ダイアログでは、リンク貼り付けを選び、Excelグラフオブジェクトになっていることを確認
  6. OKを押すとリンク貼り付けがなされ、起動するたびに更新データを取りに行くか?聞くようになる。
  7. この時、グラフの背景色などはExcel側のものが反映されるので、透過したい場合などはそちらで設定を変更しておくこと。

図:PowerPoint側はあくまでも土台

注意点

この方法でExcelのグラフとリンクさせた場合、以下の2つの注意点があります。

  1. Excelのファイルを移動した場合には、手動でリンクを再設定する必要があります。
  2. 環境によってはPowerPoint起動時にリンク更新で謎のExcelゾンビプロセスが発生し、タスクマネージャからプロセスの強制終了もできない現象に遭遇する事があります。

リンクの手動更新は以下の手順で行います。

  1. PowerPointのメニューからファイルを開く
  2. 情報の中にある「ファイルへのリンクの編集」をクリックする
  3. ダイアログが出てくるので1個ずつ選択してリンク元の変更をしていく

また、後者の謎のゾンビプロセスですがこれは対処法がない為、Excelのグラフに直接リンクせずに、Excel側でグラフを画像化するVBAを記述してエクスポート。その画像ファイルをPowerPoint側でリンクをすれば回避は可能です。画像化したグラフのリンク挿入は挿入時にファイルにリンクを選べばOK

図:リンクの手動更新ダイアログ

図:ファイルにリンクで画像を挿入

Google NewsのRSSからニュースフラッシュを作る

データを蓄積するExcel側に於いて、RSSデータを取得するコードを記述しておきます。ただし、VBAでRSSの解析をさせるのは凄く面倒臭いので、今回はRSS解析結果を返す、Google Apps Scriptを補助的に利用しています。

Google News RSS解析スクリプトのURL

https://script.google.com/macros/s/AKfycbyA_eNG_efVg5fjrjAhvLMbbueG95ZYikL42SwDoET2i0VgLQfL/exec?keyword=とまと

上記のURLに於いて、param1に検索ワードを繋げれば、そのキーワードのニュースを取得し、RSSで返します。帰ってきたデータをExcelに蓄積しておきます。PowerPoint側はこの蓄積されてるデータを1枚のスライド内で、順番に書き換えてニュースフラッシュを実現します。

Google News RSSを取得するコード

下記のコードは、Excel側に記述しているコードです。

'Google NewsのJSONデータを取得する先のベースURL
Const baseuri As String = "https://script.google.com/macros/s/AKfycbyA_eNG_efVg5fjrjAhvLMbbueG95ZYikL42SwDoET2i0VgLQfL/exec?"
Const proxyuri As String = "プロキシーサーバがある場合はここにアドレスを入力"

'Google Newsのデータを取得してシートに貼り付ける関数
Public Function GetGNewsRSS()
  '変数と定数の宣言
  Dim access_token As String
  Dim ret As String
  Dim param As String
  Dim url As String
  Dim Json As Variant
  Dim xmlHttp As Object
  Dim keyword As String
  
  '変数の初期化
  keyword = EncodeURL(ThisWorkbook.Worksheets("rssdata").Range("B1").Value)
  param = "keyword=" & keyword
  
  'JSONをパースする用の変数
  Dim doc
  Dim jsn As Object
  
  'HTMLDocumentを取得
  Set doc = CreateObject("HtmlFile")
  'scriptタグを追加
  doc.write "<script>document.JsonParse=function (s) {return eval('(' + s + ')');}</script>"
  
  '変数を初期化
  url = baseuri & param
  
  'POST通信でAPIを叩いてデータを取得
  With CreateObject("WinHttp.WinHttpRequest.5.1")
    .Open "GET", url, False
    'http://nakagami.blog.so-net.ne.jp/2006-08-29-1
    'http://d.hatena.ne.jp/festiva1300/20070511/p1
    .setProxy 2, proxyuri  'プロキシサーバのURLとポート番号
    .setRequestHeader "Content-Type", "application/json; charset=UTF-8"
    .send
    
    '返ってきた値をもとにデータを処理
    Select Case .status
      Case 200
        'JSONデータを取得する
        Json = .responseText
    End Select
  End With
  
  'JSONデータをパースする
  Dim o As Object
  Set jsn = doc.JsonParse(Json)
  
  'カンマ区切りを配列に変換
  Dim i As Variant
  Dim arrlength As Variant
  Dim tempArray As Variant
  Dim temprss As Object
  tempArray = Split(jsn, ",")
  arrlength = UBound(tempArray) - LBound(tempArray)
  
  '書き込み先シートを指定
  Dim ss As Variant

  For i = 0 To arrlength
    'データを1件ずつ取得する
    Set o = CallByName(jsn, i, VbGet)
    
    'スプレッドシートに書き込む
    ThisWorkbook.Worksheets("rssdata").Cells(i + 3, 1).Value = CallByName(o, "url", VbGet)
    ThisWorkbook.Worksheets("rssdata").Cells(i + 3, 2).Value = CallByName(o, "title", VbGet)
  
  Next i
  
  MsgBox "Google Newsの" & ThisWorkbook.Worksheets("rssdata").Range("B1").Value & "に関するニュースを取得しました。"
End Function

数分毎にデータを更新させる

デジタルサイネージの場合、どうしてもその内容が静的なものになりがち。その為、内容がリアルタイムではなく更新する為に都度手動で作業を行わなければなりません。今回、データ蓄積場所としてExcelシートを利用していますが、データの更新および再度PowerPointで再リンクを行う作業を、Timerを利用して動的に出来たら、動的なサイネージコンテンツを作れます。

但し、Timerの為に常にVBAが稼働した状況になるので、解除する為のコマンドを用意してあげないと、PowerPointを終了出来なくなるので、必ず装備しましょう。Custom UI Editorを利用すればPowerPointにもリボンを装備可能です。

Excel側のコード

5分毎に指定のコードを実行するように予約するコードです。

'5分起きにgetRealtimeで値をとってくるルーチン
Public Function realtimegetter()
    '一定間隔で実行する
    'ここに実行する関数や処理を記述
    Call hogehoge

    '10分後に自分自身を実行
    Application.OnTime Now() + TimeValue("00:05:00"), "realtimegetter"
End Function

'スケジュールされたVBAを解除する
Public Function realtimestopper()
    On Error Resume Next
    Application.OnTime Now() + TimeValue("00:05:00"), "realtimegetter", , False
End Function
  • realtimegetterが5分毎に実行するコード。自分自身の起動を5分後に予約しています。次に実行されると、改めて5分後の予約が実行される仕組みです。
  • realtimestopperが予約を解除するコード。これがないと、VBAが動き続けてしまいます。

PowerPoint側のコード

PowerPoint側に上記のコードを記述しない理由は、データの塊はやはりExcelでやらせたほうが効率的であるのと、プレゼンテーション中はOnSlideShowPageChangeで動かすので、これだと複数の予約が毎回入ってしまう為。PowerPoint側はデータが更新の有無を問わず、同じフォルダ内にあるExcelファイルに接続して、データを処理する部分を用意してあげればオッケーです。

'ウェイトを掛けるためのタイマー
Private Sub customTimer(timeSpan)
     Dim startTime
     startTime = Timer
     
     Do While Timer < startTime + timeSpan
         DoEvents
     Loop
End Sub

'スライドショー実行中にVBA実行する特殊なコマンド
Sub OnSlideShowPageChange(ByVal ss As SlideShowWindow)
  Dim n As Long
  n = ss.View.CurrentShowPosition

  Select Case n
    Case 3
        DoEvents
        
        '3枚目のスライドの時にマクロ発火
        'パスを取得
        Dim wbpath As String
        wbpath = ActivePresentation.Path & "\boardgetter.xlsm"
        
        'スライド再描画用
        Dim osld As Slide
        
        'エクセルデータを非表示で参照する
        Dim xlApp As Object
        Dim Books As Object
        Dim MyArray
        Dim arraylength As Variant
        
        Set xlApp = CreateObject("Excel.Application")
        Set Books = xlApp.Workbooks.Open(wbpath)
        xlApp.Visible = False
        
        'ガッツリデータを取得
        MyArray = Books.Worksheets("rssdata").Range("A3:B32")
        arraylength = UBound(MyArray) - LBound(MyArray) + 1
        
        'タイマーを使ってRSSデータで書き換える
        Dim i As Integer
        For i = 0 To arraylength - 1
            '特定のテキストボックスの値を書き換える
            ActivePresentation.Slides(3).Shapes("TextBox 5").TextFrame.TextRange.Text = MyArray(i + 1, 2)
    
            '8秒ウェイトを掛ける
            customTimer (8)
            
        Next i
        '終了処理
        Books.Close
        Set xlApp = Nothing
        Set Books = Nothing
    Case Else
  End Select
End Sub

特別なイベントプロシージャ

概要

Excel VBAやAccess VBAと異なり、PowerPointでVBA使おうなんていう変わった人は少ないと思うのですが、それが故にVBAが扱えるとは言え、PowerPoint VBAにはひとつ取っつきにくいイベントがあります。それが「スライドショー開始時」や「スライドショー終了時」のイベント類です。これらのイベントですが、表向きVBAのコーディング画面上には登場しません。

また、利用するにもちょっとだけ手間がありまして、意味を理解し実装できれば、よりディープにVBAが扱えるようになるでしょう。とりわけ、PowerPoint VBAに於いては、これらのイベントは何かと使えると、より動きのあるプレゼン資料となり、「デジタルサイネージコンテンツ」としては、より魅力的なものになるでしょう。(但し、このイベントは本来、これらのコードをアドイン化して読み込ませる必要があります。今回は、アドイン化せずにリボンの自動実行を利用してアドイン無しで実現しています)

なお、他にもApplicationオブジェクトのイベントを見てみると、他にも色々なイベントが用意されています。

作り方

リボン側のコード
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" xmlns:mso="http://schemas.microsoft.com/office/2009/07/customui" 
onLoad="OnLoadman">

</customUI>
  • onLoadにonLoadmanという関数を割り当ててます。PowerPointファイルを開いた時に自動で実行されます。
  • アドインの場合には、Public Sub Auto_Open()にて自動実行をさせる事ができますが、今回はアドイン無しなので使いません。
  • これらは、pptmファイルに対して、こちらのようにリボンをCustom UI Editorにて追加すると楽に記述できます。
VBA側のコード(Class1)

素直にこれらのイベントを扱う事はできません。以下の手順で準備が必要になります。

  1. VBA開発画面に入る
  2. 新規に空のクラスモジュール(Class1)を追加します。標準モジュールではありません。
  3. クラスモジュール内にプロシージャを記述していきます。主に以下のような感じ
Option Explicit
 
Public WithEvents PPTEvent As Application
 
Private Sub PPTEvent_SlideShowBegin(ByVal Wn As SlideShowWindow)
    MsgBox ("スライドショー実行")
End Sub
  • general部にて、WithEventsにてPPTEventという変数にApplicationを宣言するのがポイントです。これはクラスモジュール内でないと行えません。
  • 宣言したPPTEventに対してハイフンで繋げて、SlideShowBeginでスライドショー開始時イベントになります。引数にも注目。
  • 主に、変数の初期化やプレゼンテーションを開いた時に、関連するExcelのデータを更新させるようなコードなどを仕込んでおけば、より自動化が見込めます。
VBA側コード(Module1)

続いて、以下の手順で標準モジュールを追加し、コードを記述します。

Public cPPTObject As New Class1

'リボン起動時に初期化するコード
Public Sub OnLoadman(ribbon As IRibbonUI)
    Set cPPTObject.PPTEvent = Application
End Sub
  • 1行目、Class1のコードをcPPTObjectとして利用
  • onLoadmanが自動実行されるので、ここで初期化が実行されます。そうすることで、スライドショー実行時にClass1のSlideShowbeginが実行されます。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)