1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-12-16

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

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

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

前回のおさらい

Part2では、DateModuleやProgressBarModuleなど、時間の経過によって変化するモジュールを作りました。

ここからは、スクリーンショットが表示されているだけの状態であるRModuleを、実際のデータに連動するようにします。

必要なもの

  • Part1で配置した電子部品
  • OpenWeatherのアカウント
    • API Key
  • Googleアカウント
    • バス時刻表APIのURL
    • ごみカレンダーAPIのURL
  • Twitterアカウント(Developer Accountとして、Twitter APIへの申請が承認されているもの)
    • Consumer Key
    • Consumer Key Secret
    • Access Token
    • Access Token Secret

OpenWeatherのAPI Keyを取得する方法については、下記記事「必要な変数の定義」を参照してください。

バス時刻表API、ごみカレンダーAPIを用意する方法については、下記2記事の手順を参照してください。

Twitter APIのキー4つを取得する手順については、下記記事「キーの取得」を参照してください。

RModuleの背景を作る

Part1でスクリーンショットだけ表示されるようにしたRModuleですが、このあとはスクリーンショットを置き換えていきます。
手始めに、各RModuleの背景を生成できるようにしましょう。

背景画像の生成には、PGraphicsの関数maskというものを使います。
下記記事の「pgraphicsのmask関数」を参照してください。

mask関数で使う「型紙」にあたる部分を、関数で生成できるようにします。

DigitalSignage.pde
// RModuleの背景画像をマスクするための図形(角丸の四角)を返す。
PGraphics sizeToModuleMask(Size size) {
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  PGraphics moduleBackgroundMask = createGraphics(w, h);
  
  moduleBackgroundMask.beginDraw();
  
  moduleBackgroundMask.noStroke();
  moduleBackgroundMask.fill(255);
  moduleBackgroundMask.rect(0, 0, w, h, RMODULE_RECT_ROUND);
  
  moduleBackgroundMask.endDraw();
  
  return moduleBackgroundMask;
}

初期化の段階で、initializeRModuleBackground()という関数を作り、RModuleの背景を一括生成します。

Initialize.pde
// 各RModuleの背景を作成。
void initializeRModuleBackground() {
  RModule module;
  int w;
  int h;
  PImage back;
  
  module = RModule.Weather;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(WEATHER_PATH + "background.jpg");
  weatherBackground = createGraphics(w, h);
  weatherBackground.beginDraw();
  weatherBackground.colorMode(HSB, 360, 100, 100, 100);
  weatherBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  weatherBackground.fill(0, 0, 0, 40);
  weatherBackground.noStroke();
  weatherBackground.rect(0, 0, w, h);
  weatherBackground.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  weatherBackground.mask( sizeToModuleMask( moduleSize(module) ) );
  
  module = RModule.Bus;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(BUS_PATH + "background.jpg");
  busBackground = createGraphics(w, h);
  busBackground.beginDraw();
  busBackground.colorMode(HSB, 360, 100, 100, 100);
  busBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  busBackground.fill(0, 0, 0, 40);
  busBackground.noStroke();
  busBackground.rect(0, 0, w, h);
  busBackground.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  busBackground.mask( sizeToModuleMask( moduleSize(module) ) );
  
  module = RModule.Gomi;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(GOMI_PATH + "background.jpg");
  gomiBackground = createGraphics(w, h);
  gomiBackground.beginDraw();
  gomiBackground.colorMode(HSB, 360, 100, 100, 100);
  gomiBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  gomiBackground.fill(0, 0, 0, 40);
  gomiBackground.noStroke();
  gomiBackground.rect(0, 0, w, h);
  gomiBackground.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  gomiBackground.mask( sizeToModuleMask( moduleSize(module) ) );
  
  module = RModule.Twitter;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  twitterBackground = createGraphics(w, h);
  twitterBackground.beginDraw();
  twitterBackground.colorMode(HSB, 360, 100, 100, 100);
  PImage twitterLogo = loadImage(TWITTER_PATH + "2021 Twitter logo - blue.png");
  twitterBackground.background(0, 0, 100);
  // Twitterのロゴを縮小し、右上に表示。
  final float logoRatio = twitterLogo.height / float(twitterLogo.width); // 縦横比
  final float logoWidth = 80.0;
  final float logoHeight = logoWidth * logoRatio;
  twitterBackground.image(twitterLogo, w-logoWidth-50, 50, logoWidth, logoHeight);
  twitterBackground.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  twitterBackground.mask( sizeToModuleMask( moduleSize(module) ) );
  
  module = RModule.OpenClose;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(OPENCLOSE_PATH + "background_open.jpg");
  openCloseBackgroundOpen = createGraphics(w, h);
  openCloseBackgroundOpen.beginDraw();
  openCloseBackgroundOpen.colorMode(HSB, 360, 100, 100, 100);
  openCloseBackgroundOpen.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  openCloseBackgroundOpen.fill(0, 0, 0, 40);
  openCloseBackgroundOpen.noStroke();
  openCloseBackgroundOpen.rect(0, 0, w, h);
  openCloseBackgroundOpen.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  openCloseBackgroundOpen.mask( sizeToModuleMask( moduleSize(module) ) );
  
  back = loadImage(OPENCLOSE_PATH + "background_close.jpg");
  openCloseBackgroundClose = createGraphics(w, h);
  openCloseBackgroundClose.beginDraw();
  openCloseBackgroundClose.colorMode(HSB, 360, 100, 100, 100);
  openCloseBackgroundClose.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  openCloseBackgroundClose.fill(0, 0, 0, 40);
  openCloseBackgroundClose.noStroke();
  openCloseBackgroundClose.rect(0, 0, w, h);
  openCloseBackgroundClose.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  openCloseBackgroundClose.mask( sizeToModuleMask( moduleSize(module) ) );
  
  module = RModule.Temperature;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(TEMPERATURE_PATH + "background.jpg");
  temperatureBackground = createGraphics(w, h);
  temperatureBackground.beginDraw();
  temperatureBackground.colorMode(HSB, 360, 100, 100, 100);
  temperatureBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  temperatureBackground.fill(0, 0, 0, 40);
  temperatureBackground.noStroke();
  temperatureBackground.rect(0, 0, w, h);
  temperatureBackground.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  temperatureBackground.mask( sizeToModuleMask( moduleSize(module) ) );
  
  module = RModule.Brightness;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(BRIGHTNESS_PATH + "background_bright.jpg");
  brightnessBackgroundBright = createGraphics(w, h);
  brightnessBackgroundBright.beginDraw();
  brightnessBackgroundBright.colorMode(HSB, 360, 100, 100, 100);
  brightnessBackgroundBright.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  brightnessBackgroundBright.fill(0, 0, 0, 40);
  brightnessBackgroundBright.noStroke();
  brightnessBackgroundBright.rect(0, 0, w, h);
  brightnessBackgroundBright.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  brightnessBackgroundBright.mask( sizeToModuleMask( moduleSize(module) ) );
  
  back = loadImage(BRIGHTNESS_PATH + "background_not_bright.jpg");
  brightnessBackgroundNotBright = createGraphics(w, h);
  brightnessBackgroundNotBright.beginDraw();
  brightnessBackgroundNotBright.colorMode(HSB, 360, 100, 100, 100);
  brightnessBackgroundNotBright.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  brightnessBackgroundNotBright.fill(0, 0, 0, 40);
  brightnessBackgroundNotBright.noStroke();
  brightnessBackgroundNotBright.rect(0, 0, w, h);
  brightnessBackgroundNotBright.endDraw();
  // 背景画像を角丸四角の形に合わせてくり抜く。
  brightnessBackgroundNotBright.mask( sizeToModuleMask( moduleSize(module) ) );
}

関数initialize()の中で呼び出すようにします。

Initialize.pde
void initialize() {
  initializeImage();
  initializeRModuleBackground(); // 追加
  
  updateNowPageID(true);
}

各RModuleの実装

ここからは、スクリーンショットだけ表示されているRModuleを、APIやセンサなどと連動できるようにします。
このあとの実装のために、現在RModuleの描画に使っている7関数の中で、image関数が使われている記述を削除(またはコメントアウト)してください。

void drawWeatherRModule(Area area) {
  RModule module = RModule.Brightness;
  Size size = moduleSize(module);

  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);

  //image(loadImage(WEATHER_PATH + "sample.jpg"), x, y); // 削除かコメントアウト
}

下記記事の解説を参照しながら進めてください。

起動画面を表示できるようにする

起動画面については、下記記事で解説しています。

起動時に、「どの項目まで読み込みが完了したか」を示す変数を、順番にtrueにしていきます。
「どの項目まで読み込みが完了したか」を示す変数は以下です。

DigitalSignage.pde
// 起動画面用の初期化済フラグ
boolean isInitializedImages = false;
boolean isInitializedDates = false;
boolean isInitializedWeather = false;
boolean isInitializedBus = false;
boolean isInitializedGomi = false;
boolean isInitializedTwitter = false;

初期化用関数を下記のように書き換えます。

Initialize.pde
void initialize() {
  initializeDate();
  isInitializedDates = true;

  initializeImage();
  initializeRModuleBackground();
  isInitializedImages = true;

  isUpdatedWeather = updateWeather();
  isInitializedWeather = true;

  isUpdatedBus = updateBus();
  isInitializedBus = true;

  isUpdatedGomi = updateGomi();
  isInitializedGomi = true;

  isUpdatedTwitter = updateTwitter();
  isInitializedTwitter = true;

  isUpdatedOpenClose = updateOpenClose();
  isUpdatedTemperature = updateTemperature();
  isUpdatedBrightness = updateBrightness();
}

起動画面描画用の関数を作成します。

M_LaunchingScreen.pde
void drawLaunchingScreenModule() {
  drawFullImageModule(background);

  drawText(CENTER, BASELINE, GREEN_COLOR, 148, "DIGITAL SIGNAGE", width/2, height/2-380);
  drawText(CENTER, BASELINE, BLACK_COLOR, 48, LOCATION, width/2, height/2-180);
  stroke(BLACK_COLOR);
  strokeWeight(5);
  noFill();
  line(300, height/2-200, width-300, height/2-200);

  /* ここから追加 */
  if (isInitializedDates) {
    drawText(CENTER, BASELINE, GREEN_COLOR, 36, "カレンダー取得", width/2, height/2+40);
  } else {
    drawText(CENTER, BASELINE, BLACK_COLOR, 36, "カレンダー取得", width/2, height/2+40);
  }

  if (isInitializedImages) {
    drawText(CENTER, BASELINE, GREEN_COLOR, 36, "画像取得", width/2, height/2+100);
  } else {
    drawText(CENTER, BASELINE, BLACK_COLOR, 36, "画像取得", width/2, height/2+100);
  }

  if (isInitializedWeather) {
    drawText(CENTER, BASELINE, GREEN_COLOR, 36, "天気情報取得", width/2, height/2+160);
  } else {
    drawText(CENTER, BASELINE, BLACK_COLOR, 36, "天気情報取得", width/2, height/2+160);
  }

  if (isInitializedBus) {
    drawText(CENTER, BASELINE, GREEN_COLOR, 36, "バス情報取得", width/2, height/2+220);
  } else {
    drawText(CENTER, BASELINE, BLACK_COLOR, 36, "バス情報取得", width/2, height/2+220);
  }

  if (isInitializedGomi) {
    drawText(CENTER, BASELINE, GREEN_COLOR, 36, "ごみカレンダー取得", width/2, height/2+280);
  } else {
    drawText(CENTER, BASELINE, BLACK_COLOR, 36, "ごみカレンダー取得", width/2, height/2+280);
  }

  if (isInitializedTwitter) {
    drawText(CENTER, BASELINE, GREEN_COLOR, 36, "ツイート取得", width/2, height/2+340);
  } else {
    drawText(CENTER, BASELINE, BLACK_COLOR, 36, "ツイート取得", width/2, height/2+340);
  }
  /* ここまで追加 */
}

関数draw()内で、起動画面を表示できるようにします。

DigitalSignage.pde
void draw() {
  if (nowPageID == -1) {
    // データの初期化が完了するまで、起動画面を表示する。
    drawLaunchingScreenModule(); // 追加
  } else {
    // データを更新したあと、各モジュールを描画する。
    updateDatas();
    drawModules();
  }
}

これで起動画面が表示されるようになります。

RModuleの影を表示する

実装してきたRModuleを立体的に見せるため、影をつくります。
下記記事の通りに進めてください。

最後に

これでデジタルサイネージの実装は完了です。
もし余力があれば、各モジュールをカスタマイズしてみてください。

お疲れ様でした!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?