【Bラボデジタルサイネージ19】起動画面を表示する「LaunchingScreenModule」

![](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/BLabo-digital-signage

本記事は、[株式会社 函館ラボラトリ](http://www.hakolab.co.jp)が運営する「[Bラボ](http://www.hakolab.co.jp/b-lab/index.html)」における、大人向け「看板アプリ（デジタルサイネージ）を作るコース」用教材テキストです。

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

1. [【Bラボデジタルサイネージ1】Processing+ラズパイでデジタルサイネージを作る！](https://qiita.com/ohayota/items/f4aa3e1b452060eb52ad)
2. [【Bラボデジタルサイネージ2】ラズパイ初期設定](https://qiita.com/ohayota/items/88fbcf177e6606311efb)
3. [【Bラボデジタルサイネージ3】準備プログラム作成＆機能実現の方針決定](https://qiita.com/ohayota/items/41a746d2258673726541)
4. [【Bラボデジタルサイネージ4】レイアウトの基準になるグリッドを表示する「GridModule」](https://qiita.com/ohayota/items/df704f523b5aa4dbb180)
5. [【Bラボデジタルサイネージ5】レイアウトの基準になる枠を表示する「PlaceholderModule」](https://qiita.com/ohayota/items/f52a10660009794afd9f)
6. [【Bラボデジタルサイネージ6】画像を全画面表示する「FullImageModule」](https://qiita.com/ohayota/items/4a9c8423459c251f4911)
7. [【Bラボデジタルサイネージ7】ページの自動切り替え](https://qiita.com/ohayota/items/9d2c28f9cbf2f559be31)
8. [【Bラボデジタルサイネージ8】設置されている場所の名前を表示する「LocationModule」](https://qiita.com/ohayota/items/046b361d945a709c4605)
9. [【Bラボデジタルサイネージ9】現在の時間を表示する「DateModule」](https://qiita.com/ohayota/items/6b80137e33bfb4172de6)
10. [【Bラボデジタルサイネージ10】ページ切り替えの時間が分かる「ProgressBarModule」](https://qiita.com/ohayota/items/9f3b6f9f32b7d93ef2a7)
11. [【Bラボデジタルサイネージ11】現在の表示中のページが分かる「PageControlModule」](https://qiita.com/ohayota/items/23acc993dca47286fffb)
12. [【Bラボデジタルサイネージ12】現在の天気を表示する「WeatherRModule」](https://qiita.com/ohayota/items/d9b1d21037dcedcfd78c)
13. [【Bラボデジタルサイネージ13】直近2件のバス時刻表を表示する「BusRModule」](https://qiita.com/ohayota/items/93d7c9b5328188b3c5b6)
14. [【Bラボデジタルサイネージ14】ごみ出しカレンダーを表示する「GomiRModule」](https://qiita.com/ohayota/items/40ed5db352a688637f83)
15. [【Bラボデジタルサイネージ15】（発展編）ツイートを表示する「TwitterRModule」](https://qiita.com/ohayota/items/6367fad7beaf9ad9ea1c)
16. [【Bラボデジタルサイネージ16】開店／閉店を表示する「OpenCloseRModule」](https://qiita.com/ohayota/items/284b5c7e7101f602016f)
17. [【Bラボデジタルサイネージ17】部屋の温度を表示する「TemperatureRModule」](https://qiita.com/ohayota/items/386c27a021601fb3ccfd)
18. [【Bラボデジタルサイネージ18】部屋の明るさを表示する「BrightnessRModule」](https://qiita.com/ohayota/items/76279927b4ef35eaf628)
19. **【Bラボデジタルサイネージ19】起動画面を表示する「LaunchingScreenModule」（本記事）**
20. [【Bラボデジタルサイネージ20】RModuleの影を実装](https://qiita.com/ohayota/items/57242f18653ccf7eb83b)


# 作るもの

現状は起動時に何も表示されない状態のため、データの読み込みが完了するまでの間、起動画面を表示できるようにします。

![0007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/56680223-5eb5-892a-1713-7e1d4315cde9.png)


# 現状の初期化処理

これまでの記事を通して実装した各機能の実現のため、多くの画像をデジタルサイネージに使用しました。
しかし画像の読み込みやWeb APIの利用といった処理は、完了するまでに時間がかかります。

現状の関数`setup()`内での初期化は、関数`initialize()`を呼ぶことで実現しています。

``` 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("NotoSansCJKjp-Bold", 32));
  
  background = pImageCut(loadImage("background.jpg"), CENTER, CENTER, width, height);
  
  GPIO.pinMode(SWITCH_PIN, GPIO.INPUT);
  i2c = new I2C(I2C.list()[0]);
  spi = new SPI(SPI.list()[0]);
  spi.settings(500000, SPI.MSBFIRST, SPI.MODE0);
  
  initialize();
}
```

関数`initialize()`では、すべてのモジュールで使うデータを用意するため、実行完了まで多くの時間を要します。
これは、`initialize()`を実行完了するまで、関数`draw()`が実行されない（なにも画面に描画されない）ということを意味します。

起動画面を実現するには、画面の描画のために`draw()`を実行しつつ、`initialize()`でのデータ初期化を並行して実行する必要があります。
そこで、データ初期化を描画と並行して実行するために、「非同期処理」を実装してみます。


# 非同期処理を用いた初期化

Processingでは、関数`thread()`を使うことで、非同期処理が実現できます。
たとえば、関数`initialize()`が定義されている状態で、以下のように書くとします。

``` java:例
thread("initialize");
```

上の例だと、関数`initialize()`の処理を、描画処理と並行して実行することになります。
これを踏まえて関数`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("NotoSansCJKjp-Bold", 32));
  
  background = pImageCut(loadImage("background.jpg"), CENTER, CENTER, width, height);
  
  GPIO.pinMode(SWITCH_PIN, GPIO.INPUT);
  i2c = new I2C(I2C.list()[0]);
  spi = new SPI(SPI.list()[0]);
  spi.settings(500000, SPI.MSBFIRST, SPI.MODE0);
  
  thread("initialize"); /* 変更 */
}
```

これで、関数`initialize()`は`setup()`内で実行されますが、関数`setup()`は`initialize()`の終了を待たずに終了するようになりました。
`initialize()`が実行中でも、並行して関数`draw()`の描画が実行されることになります。

（この時点で実行しても、まだエラーになります。）


# どこまで初期化されたかを管理

![0007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/56680223-5eb5-892a-1713-7e1d4315cde9.png)

起動画面の「カレンダー取得」「画像取得」など、どこまで読み込みが済んだか（初期化されたか）を示すために、状態管理用の変数を宣言します。

``` java:DigitalSignage.pde
/* 略 */

int nowPageID = 0;

/* ここから追加 */
// 起動画面用の初期化済フラグ
boolean isInitializedImages = false;
boolean isInitializedDates = false;
boolean isInitializedWeather = false;
boolean isInitializedBus = false;
boolean isInitializedGomi = false;
boolean isInitializedTwitter = false;
/* ここまで追加 */

final String AD_PATH = "ad/";

/* 略 */
```

読み込みが完了した項目の値をtrueに変えるよう、関数`initialize()`に追記します。

``` java:Initialize.pde
void initialize() {
  initializeDate();
  isInitializedDates = true; /* 追加 */
  
  initializeImage();
  initializeGrid();
  initializePlaceholder();
  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();
}
```

# LaunchingScreenModuleの描画

新しく`M_LaunchingScreen.pde`を作り、関数`drawLaunchingScreenModule()`を定義します。

``` java:M_LaunchingScreen.pde
void drawLaunchingScreenModule() {

}
```

背景画像と「DIGITAL SIGNAGE」の文字、設置場所を表示します。

``` java: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);
  /* ここまで追加 */
}
```

「カレンダー取得」「画像取得」など、どこまで読み込みが済んだかを表示する部分を作ります。
読み込みが済んだ項目は緑色、そうでない項目は黒色の文字で表示します。

``` java: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);
  }
  /* ここまで追加 */
}
```

起動画面を示すページ番号は`-1`として扱います。
ページ番号の初期値を変更します。

``` java:DigitalSignage.pde
int nowPageID = -1; /* 変更 */
```

データの初期化が終わった後に、ページ番号が`-1`から`0`になるようにします。

``` java:Initialize.pde
void initialize() {
  initializeDate();
  isInitializedDates = true;
  
  initializeImage();
  initializeGrid();
  initializePlaceholder();
  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();

  updateNowPageID(true); /* 追加 */
}
```

関数`draw()`内で、ページ番号が`-1`の場合に対応できるよう条件分岐を書きます。

``` java:DigitalSignage.pde
void draw() {
  /* ここから変更 */
  if (nowPageID == -1) {
    drawLaunchingScreenModule();
  } else {
    updateDatas();
    drawModules();
  }
  /* ここまで変更 */
}
```

ここまで記述して実行してみると、読み込みが終わるまで起動画面が表示され、読み込みが終われば画面が切り替わるようになります。

![0007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/56680223-5eb5-892a-1713-7e1d4315cde9.png)


# 最後に

これで、起動画面を表示する「LaunchingScreenModule」が実装できました。
このシリーズは、次の記事で最後になります。
最後に**[RModuleに影を実装](https://qiita.com/ohayota/items/57242f18653ccf7eb83b)**してみましょう。

https://qiita.com/ohayota/items/57242f18653ccf7eb83b