Bラボデジタルサイネージ（長期用テキスト2/3）

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fuser-images.githubusercontent.com%2F29194025%2F137117015-af05fe4f-7c32-4e38-876f-e153ff2f2f7d.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&w=1400&fit=max&s=2bbf46fb5c1d8dec16f2fcb5d089fde2)

https://github.com/ohayota/digital-signage-template

本記事は、[株式会社 函館ラボラトリ](http://www.hakolab.co.jp)が運営する「[Bラボ](http://www.hakolab.co.jp/b-lab/index.html)」における、大人向け「看板アプリ（デジタルサイネージ）を作るコース」用テキストです。
一からコードを書くわけではなく、あらかじめ用意したコードの一部を改変しながら、デジタルサイネージを作っていきましょう。

http://www.hakolab.co.jp/b-lab/index.html

テンプレートプログラムは、下記GitHubリポジトリの`LongTerm/Part2_Start`ディレクトリに入っています。

https://github.com/ohayota/BLabo-digital-signage-sample/tree/main/LongTerm/Part2_Start


（12/16時点、本テキストは執筆中です）


# 前回のおさらい

RModuleのスクリーンショットを表示できる状態にしました。

ここからは「時刻」などを表示するモジュールを追加し、RModuleは実際のデータに連動するようにします。

https://qiita.com/ohayota/items/d9a650ec5de5a19f7b9c


# 背景画像の表示

まずは背景画像を読み込みます。
関数`setup`内で読み込みます。

``` java:DigitalSignage.pde
void setup() {
  frameRate(1);
  noCursor();
  colorMode(HSB, 360, 100, 100, 100);
  
  // 色の初期設定。
  WHITE_COLOR = color(0, 0, 100);
  NEARLY_WHITE_COLOR = color(100, 2, 98);
  NEARLY_GREEN_COLOR = color(100, 5, 98);
  BLACK_COLOR = color(0, 0, 0);
  LIGHT_COLOR = color(0, 0, 80);
  GRAY_COLOR = color(0, 0, 50);
  GREEN_COLOR = color(150, 100, 60);
  
  // デジタルサイネージの背景画像を読み込む。
  background = pImageCut(loadImage("background.jpg"), CENTER, CENTER, width, height); // 追加
  
  // 初期化用関数initialize()を、draw()で画面が描画されているときに並行し実行（非同期処理）。
  thread("initialize");
}
```

関数`drawModules`内で、背景画像を表示できるようにします。

``` java:DigitalSignage.pde
void drawModules() {
  image(background, 0, 0); // 追加
  
  if (nowPageID == 0) {
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
    drawTemperatureRModule(Area.area1);
    drawBrightnessRModule(Area.area2);
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }
}
```

実行してみると、背景画像が表示されるようになっています。

![0003.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/f8938616-25f6-4746-1c1a-e188ecd11b31.jpeg)



# テキスト描画用関数の準備

このあとの実装で、文字を表示する関数`text`が使われます。
文字を表示するには、「サイズ」「色」「左揃え／中央揃え／右揃え」などの設定が必要です。
これを毎回数行にわたって書かなくてもいいように、1つの関数を呼ぶだけで良くします。

``` java:DigitalSignage.pde
// 表示するテキストのスタイルを指定して描画。
void drawText(int alignX, int alignY, color c, int size, String text, int x, int y) {
  pushStyle();
  textAlign(alignX, alignY);
  textSize(size);
  fill(c);
  if (alignY == BASELINE) {
    text(text, x, y+size);
  } else {
    text(text, x, y);
  }
  popStyle();
}

// 表示するテキストのスタイルを指定して描画。テキストの自動改行に対応したバージョン。
void drawText(int alignX, int alignY, color c, int size, String text, int x, int y, int w, int h) {
  pushStyle();
  textAlign(alignX, alignY);
  textSize(size);
  fill(c);
  if (alignY == BASELINE) {
    text(text, x, y+size, w, h);
  } else {
    text(text, x, y, w, h);
  }
  popStyle();
}
```

# デジタルサイネージが設置されている場所を表示する

設置場所を画面の右上に表示してみましょう。
下記記事の「LocationModule」を実装します。

https://qiita.com/ohayota/items/046b361d945a709c4605

``` java:M_Location.pde
void drawLocationModule() {
  // 文字が画像と同化するのを避けるため、背景画像の色に応じて文字色反転。
  blendMode(EXCLUSION);
  
  drawText(RIGHT, BASELINE, WHITE_COLOR, 36, LOCATION, width-100, 30);
  
  // 他モジュールの描画時にも色が反転してしまうため、もとに戻す。
  blendMode(BLEND);
}
```

関数`drawModules`の中で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  image(background, 0, 0);
  
  if (nowPageID == 0) {
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
    drawTemperatureRModule(Area.area1);
    drawBrightnessRModule(Area.area2);
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule(); // 追加
}
```


# 時刻を表示する

時刻を画面の左上に表示してみましょう。
下記記事にある内容を進めていきます。

https://qiita.com/ohayota/items/6b80137e33bfb4172de6

曜日を算出する関数をつくります。

``` java:M_Date.pde
// Zellerの公式を使った曜日計算
Youbi calcYoubi(int year, int month, int day) {
  final Youbi[] youbi = Youbi.values();
  if (month < 3) {
    year--;
    month += 12;
  }
  return youbi[(year+year/4-year/100+year/400+(13*month+8)/5+day)%7];
}
```

曜日を文字列として出すための関数をつくります。

``` java:M_Date.pde
// 曜日を日本語表記にする。月〜金でも祝日の場合は「月・祝」のように表示。
String youbiToString(Youbi youbi) {
  String str = "";
  switch (youbi) {
    case Sun:
      str = "日";
      break;
    case Mon:
      str = "月";
      if (isHoliday) str += "・祝";
      break;
    case Tue:
      str = "火";
      if (isHoliday) str += "・祝";
      break;
    case Wed:
      str = "水";
      if (isHoliday) str += "・祝";
      break;
    case Thu:
      str = "木";
      if (isHoliday) str += "・祝";
      break;
    case Fri:
      str = "金";
      if (isHoliday) str += "・祝";
      break;
    case Sat:
      str = "土";
      break;
  }
  return str;
}
```

日付と時刻を表示します。

``` java:M_Date.pde
void drawDateModule() {
  // 文字が画像と同化するのを避けるため、背景画像の色に応じて文字色反転。
  blendMode(EXCLUSION);
  
  String timeText = month + "月" + day + "日" + "（" + youbiString + "）" + nf(hour, 2) + ":" + nf(minute, 2) + ":" + nf(second, 2);
  drawText(LEFT, BASELINE, WHITE_COLOR, 36, timeText, 100, 30);
  
  // 他モジュールの描画時にも色が反転してしまうため、もとに戻す。
  blendMode(BLEND);
}
```

関数`setup()`内で、フォントを使用できるように設定します。

``` java:DigitalSignage.pde
void setup() {
  frameRate(1);
  noCursor();
  colorMode(HSB, 360, 100, 100, 100);
  
  // 色の初期設定。
  WHITE_COLOR = color(0, 0, 100);
  NEARLY_WHITE_COLOR = color(100, 2, 98);
  NEARLY_GREEN_COLOR = color(100, 5, 98);
  BLACK_COLOR = color(0, 0, 0);
  LIGHT_COLOR = color(0, 0, 80);
  GRAY_COLOR = color(0, 0, 50);
  GREEN_COLOR = color(150, 100, 60);
  
  // フォントを読み込み、デジタルサイネージで使うフォントとして設定。
  textFont(createFont("Noto Sans CJK jp Bold", 32)); // 追加
  
  // デジタルサイネージの背景画像を読み込む。
  background = pImageCut(loadImage("background.jpg"), CENTER, CENTER, width, height);
  
  // 初期化用関数initialize()を、draw()で画面が描画されているときに並行し実行（非同期処理）。
  thread("initialize");
}
```

関数`drawModules`の中で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  image(background, 0, 0);
  
  if (nowPageID == 0) {
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
    drawTemperatureRModule(Area.area1);
    drawBrightnessRModule(Area.area2);
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawDateModule(); // 追加
  drawLocationModule();
}
```

実行すると、左上に時刻が表示されます。
（ただし、現時点では曜日は「null」になります）

![0117.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/49f5d9fc-089d-7bcd-2d62-0e334b233546.jpeg)


# ページ切り替えまでの時間がわかるようにする

ページが切り替わってから、1秒経つごとに伸びていくバーを作ります。
下記記事で解説されている「ProgressBarModule」です。

https://qiita.com/ohayota/items/9f3b6f9f32b7d93ef2a7

ProgressBarModuleを描画する関数を作ります。

``` java:M_ProgressBar.pde
void drawProgressBarModule() {
  // 残り秒数を割合として算出（%）
  float progressRate = (second % STAY_SECOND) / float(STAY_SECOND-1);
  // 時間が経過したぶんだけ、白いバーが緑で塗りつぶされていく
  noStroke();
  fill(WHITE_COLOR);
  rect(0, height-PROGRESSBAR_HEIGHT, width, PROGRESSBAR_HEIGHT);
  fill(GREEN_COLOR);
  rect(0, height-PROGRESSBAR_HEIGHT, width * progressRate, PROGRESSBAR_HEIGHT);
}
```

関数`drawModules`内で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  image(background, 0, 0);
  
  if (nowPageID == 0) {
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
    drawTemperatureRModule(Area.area1);
    drawBrightnessRModule(Area.area2);
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawDateModule();
  drawLocationModule();
  drawProgressBarModule(); // 追加
}
```

実行すると、画面下にバーが表示されます。

![0028.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/8f847422-3a29-f695-acc7-40069ad7ee4b.jpeg)


# 現在表示中のページが何番目かを可視化する

複数のページがあるうちで、現在何ページ目が表示されているかを可視化します。
下記記事で解説されている「PageControlModule」です。

https://qiita.com/ohayota/items/23acc993dca47286fffb

PageControlModuleを描画する関数を作ります。

``` java:M_PageControl.pde
void drawPageControlModule() {
  for (int page = 0; page < PAGE_ALL_COUNT; page++) {
    // ページ番号をもとに点を横に並べる
    float x = width/2 + 30 * (page-(PAGE_ALL_COUNT-1)/2.0);
    float y = 1020;
    // 現在のページだけは白の点（緑の枠線付き）、それ以外は緑の点を打つ
    if (page == nowPageID) {
      fill(WHITE_COLOR);
      stroke(GREEN_COLOR);
      strokeWeight(5);
      circle(x, y, 20);
    } else {
      fill(GREEN_COLOR);
      noStroke();
      circle(x, y, 15);
    }
  }
}
```

関数`drawModules`内で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  image(background, 0, 0);
  
  if (nowPageID == 0) {
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
    drawTemperatureRModule(Area.area1);
    drawBrightnessRModule(Area.area2);
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }
  
  drawDateModule();
  drawLocationModule();
  drawProgressBarModule();
  drawPageControlModule(); // 追加
}
```

実行すると、画面下に点がいくつか表示されます。

![0005.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/59588e43-5c8f-683d-e330-4bb585f14a72.jpeg)


# 画像を全画面表示する

広告画像を全画面表示するために、「FullImageModule」を実装します。
下記記事で解説しているものです。

https://qiita.com/ohayota/items/4a9c8423459c251f4911

描画用関数を実装します。

``` java:M_FullImage.pde
void drawFullImageModule(PImage pImage) {
  image(pImage, 0, 0, width, height);
}
```

関数`drawModules`内で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  image(background, 0, 0);
  
  if (nowPageID == 0) {
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
    drawTemperatureRModule(Area.area1);
    drawBrightnessRModule(Area.area2);
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
    drawFullImageModule(adImage[0]); // 追加
  }

  drawDateModule();
  drawLocationModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```

実行すると、3ページ目に全画面表示の画像が表示されるようになっています。

![0021.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/141cdf77-af59-9933-d7d5-bc330696b694.jpeg)

# 次回

次はPart3です。
Part3では、Part2時点でスクリーンショットになっている部分を、Web APIやセンサなどに連動させていきます。

https://qiita.com/ohayota/items/c33fa256102caf3665b6