PowerPointでデジタルサイネージを作ろう
最近は、企業の事務所内にも大きなモニターを利用したデジタルサイネージを見るようになりました。その目的の殆どは総務部による一方的な情報提供に終始してる部分もありますが、イントラネットやグループウェアでは伝わらないような情報提供にあります。
そんなデジタルサイネージシステムには、BrightSignやデジサインのようなものが多いですが、正直価格は高いです(モニターはAmazonで調達するのが安いです)。
しかし、高価なこれらデジタルサイネージなど使わずとも、手元にあるものでこれらよりもずっと優れた作品を作る手段があります。それが、PowerPoint。動画もイケて自動で紙芝居も可能。けれど一歩踏み込んでPowerPoint VBAで仕込めばより高度でデータ処理も自動化できるサイネージが作れます。
目次
今回使用するファイルや資料
- デジタルサイネージ用のPowerPointファイル
- サイネージコンテンツ用Excelファイル
- RSSを整形して返すスクリプト用スプレッドシート
- リボン用Custom UI Editor
- 40インチ以上の大きなモニター
- HDMIを無線化する為のエクステンダー
特殊なイベント用として、SlideShowBeginのサンプルも追加しました。
事前準備
ワイド画面対応にする
通常、PowerPointでファイルを作成すると昔のアニメのように4:3で作成されてしまいます。しかし、最近のモニターは16:9で構成されていることが多く、そのままでは画面の両端に黒い帯が出来てしまい、格好が悪い。まずは作り始める前にPowerPointのスライドサイズを16:9のワイド画面対応にしましょう。
- デザインタブを開く
- スライドのサイズを開く
- ワイド画面(16:9)を選択する
図:16:9を使うようにしましょう
VBAを有効にしたpptmファイルにする
今回は動きのある動的なデジタルサイネージコンテンツとするため、ただの紙芝居ではなくVBAを利用するため、今回のファイルはpptx形式ではなく、pptm形式で保存してあげる(でないと、VBAコードを書いても排除されてしまいます)。
- ファイルタブを開く
- 名前を付けて保存を選択する
- 参照をクリックして保存先を選択
- その際のファイルの種類について「マクロ有効プレゼンテーション」を選ぶ
- 保存ボタンを押す
起動時にセキュリティ警告が出ることがありますが、コンテンツの有効化をクリックしてもらえれば問題ありません。
配置したテキストボックスの名前を調べる
VBAで動的にテキストボックスの値を書き換えたりするのが今回の目的の1つですが、PowerPoint上の各種パーツには表向きプロパティが見えません。しかし、これらのコントロール名が分からなければ、書き換えようがありません。実は表向き見えないだけで、各パーツ類にはきちんと自動でコントロール名が与えられている。
このコントロール名を以下のVBAで事前に調べておき、VBA内で指定して内容を書き換えます。ちなみにこのコントロール名はスライド単位であり、スライドをコピーするとコントロール名も同じ名前のままです。ファイル内では同じコントロール名のボックスがあることになりますが、スライド単位で指定するので、問題なくアクセスが可能です。
- 対象のパーツをクリックする
- 以下のコマンドを実行する。
- パーツの名前とIDが返ってくるので、控えておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
'現在選択中のシェイプパーツの名前を取得するコマンド 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のメソッドがきちんと働くようになります。
- 適当なスライドを選択する
- 開発タブを開く(表示されていない場合には表示しておきましょう)
- ラベルコントロールなどの適当なコントロールを張り付ける
- Visual Basicを開く
- プロジェクトに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側に張り付ける手順は以下の通りです。
- Excel側のグラフをコピーする
- PowerPoint側の張り付けたいスライドに移動
- ホームタブの張り付けボタンの下の▼をクリック
- 形式を選択して貼り付けを選ぶ
- ダイアログでは、リンク貼り付けを選び、Excelグラフオブジェクトになっていることを確認
- OKを押すとリンク貼り付けがなされ、起動するたびに更新データを取りに行くか?聞くようになる。
- この時、グラフの背景色などはExcel側のものが反映されるので、透過したい場合などはそちらで設定を変更しておくこと。
図:PowerPoint側はあくまでも土台
注意点
この方法でExcelのグラフとリンクさせた場合、以下の2つの注意点があります。
- Excelのファイルを移動した場合には、手動でリンクを再設定する必要があります。
- 環境によってはPowerPoint起動時にリンク更新で謎のExcelゾンビプロセスが発生し、タスクマネージャからプロセスの強制終了もできない現象に遭遇する事があります。
リンクの手動更新は以下の手順で行います。
- PowerPointのメニューからファイルを開く
- 情報の中にある「ファイルへのリンクの編集」をクリックする
- ダイアログが出てくるので1個ずつ選択してリンク元の変更をしていく
また、後者の謎のゾンビプロセスですがこれは対処法がない為、Excelのグラフに直接リンクせずに、Excel側でグラフを画像化するVBAを記述してエクスポート。その画像ファイルをPowerPoint側でリンクをすれば回避は可能です。画像化したグラフのリンク挿入は挿入時にファイルにリンクを選べばOK
図:リンクの手動更新ダイアログ
図:ファイルにリンクで画像を挿入
Google NewsのRSSからニュースフラッシュを作る
データを蓄積するExcel側に於いて、RSSデータを取得するコードを記述しておきます。ただし、VBAでRSSの解析をさせるのは凄く面倒臭いので、今回はRSS解析結果を返す、Google Apps Scriptを補助的に利用しています。
Google News RSS解析スクリプトのURL
1 |
https://script.google.com/macros/s/AKfycbyA_eNG_efVg5fjrjAhvLMbbueG95ZYikL42SwDoET2i0VgLQfL/exec?keyword=とまと |
上記のURLに於いて、param1に検索ワードを繋げれば、そのキーワードのニュースを取得し、RSSで返します。帰ってきたデータをExcelに蓄積しておきます。PowerPoint側はこの蓄積されてるデータを1枚のスライド内で、順番に書き換えてニュースフラッシュを実現します。
Google News RSSを取得するコード
下記のコードは、Excel側に記述しているコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
'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 |
タイマーでスライドのテキストを書き換える
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
'ウェイトを掛けるためのタイマー 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シートを利用していますが、データの更新および再度PowerPointで再リンクを行う作業を、Timerを利用して動的に出来たら、動的なサイネージコンテンツを作れます。
但し、Timerの為に常にVBAが稼働した状況になるので、解除する為のコマンドを用意してあげないと、PowerPointを終了出来なくなるので、必ず装備しましょう。Custom UI Editorを利用すればPowerPointにもリボンを装備可能です。
Excel側のコード
5分毎に指定のコードを実行するように予約するコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
'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ファイルに接続して、データを処理する部分を用意してあげればオッケーです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
'3枚目のスライドの時にdatagetterを発動 Sub OnSlideShowPageChange(ByVal ss As SlideShowWindow) Dim n As Long n = ss.View.CurrentShowPosition Select Case n Case 3 Call datagetter End Select End Sub 'リアルタイムデータをExcel側から取得する Public Sub datagetter() '3枚目のスライドの時にマクロ発火させるために、OnSlideShowPageChangeから呼び出し 'パスを取得 Dim wbpath As String wbpath = ActivePresentation.Path & "\datagetter.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(i, 8)で値取り出し MyArray = Books.Worksheets("RSSDATA").Range("A2:G7") arraylength = UBound(MyArray) - LBound(MyArray) + 1 'タイマーで300ms毎に値を書き換え Dim i As Integer Dim cnt As Integer cnt = 6 For i = 0 To arraylength - 2 '更新日時を書き換える ActivePresentation.Slides(cnt).Shapes("Rectangle 3").TextFrame.TextRange.Text = MyArray(i + 1, 2) 'カウンタを加算 cnt = cnt + 1 Next i '終了処理(保存せずにBookは閉じる) Application.DisplayAlerts = False Books.Close savechanges:=False Application.DisplayAlerts = True Set xlApp = Nothing Set Books = Nothing End Sub |
特別なイベントプロシージャ
概要
Excel VBAやAccess VBAと異なり、PowerPointでVBA使おうなんていう変わった人は少ないと思うのですが、それが故にVBAが扱えるとは言え、PowerPoint VBAにはひとつ取っつきにくいイベントがあります。それが「スライドショー開始時」や「スライドショー終了時」のイベント類です。これらのイベントですが、表向きVBAのコーディング画面上には登場しません。
また、利用するにもちょっとだけ手間がありまして、意味を理解し実装できれば、よりディープにVBAが扱えるようになるでしょう。とりわけ、PowerPoint VBAに於いては、これらのイベントは何かと使えると、より動きのあるプレゼン資料となり、「デジタルサイネージコンテンツ」としては、より魅力的なものになるでしょう。(但し、このイベントは本来、これらのコードをアドイン化して読み込ませる必要があります。今回は、アドイン化せずにリボンの自動実行を利用してアドイン無しで実現しています)
なお、他にもApplicationオブジェクトのイベントを見てみると、他にも色々なイベントが用意されています。
作り方
リボン側のコード
1 2 3 4 |
<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)
素直にこれらのイベントを扱う事はできません。以下の手順で準備が必要になります。
- VBA開発画面に入る
- 新規に空のクラスモジュール(Class1)を追加します。標準モジュールではありません。
- クラスモジュール内にプロシージャを記述していきます。主に以下のような感じ
1 2 3 4 5 6 7 |
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)
続いて、以下の手順で標準モジュールを追加し、コードを記述します。
1 2 3 4 5 6 |
Public cPPTObject As New Class1 'リボン起動時に初期化するコード Public Sub OnLoadman(ribbon As IRibbonUI) Set cPPTObject.PPTEvent = Application End Sub |
- 1行目、Class1のコードをcPPTObjectとして利用
- onLoadmanが自動実行されるので、ここで初期化が実行されます。そうすることで、スライドショー実行時にClass1のSlideShowbeginが実行されます。
関連リンク
- OnSlideShowPageChangeが正常に動かないバグ
- Office 2013 ドキュメントに特定のスクリプト可能な ActiveX コントロールを挿入することはできません。
- PC映像をワイヤレス出力するHDMIエクステンダー サンワサプライから発売
- ワイヤレス HDMIエクステンダー 壁を透過 GUANYEE WHD-G800
- Update Links In Powerpoint
- Excel 2010からAccess2010(accdb)へ接続する際に正しいパスワードを使っていても「パスワードが正しくありません」(コード3031)が発生することがある
- もう全部パワポで良いや!PowerPoint魔改造アドイン7+1選
- [PowerPoint]ドキュメントを開いたときに自動的にマクロを実行する