本記事は、株式会社 函館ラボラトリが運営する「Bラボ」における、大人向け「看板アプリ(デジタルサイネージ)を作るコース」用教材テキストです。
- 【Bラボデジタルサイネージ1】Processing+ラズパイでデジタルサイネージを作る!
- 【Bラボデジタルサイネージ2】ラズパイ初期設定
- 【Bラボデジタルサイネージ3】準備プログラム作成&機能実現の方針決定
- 【Bラボデジタルサイネージ4】レイアウトの基準になるグリッドを表示する「GridModule」
- 【Bラボデジタルサイネージ5】レイアウトの基準になる枠を表示する「PlaceholderModule」
- 【Bラボデジタルサイネージ6】画像を全画面表示する「FullImageModule」
- 【Bラボデジタルサイネージ7】ページの自動切り替え
- 【Bラボデジタルサイネージ8】設置されている場所の名前を表示する「LocationModule」
- 【Bラボデジタルサイネージ9】現在の時間を表示する「DateModule」
- 【Bラボデジタルサイネージ10】ページ切り替えの時間が分かる「ProgressBarModule」
- 【Bラボデジタルサイネージ11】現在の表示中のページが分かる「PageControlModule」
- 【Bラボデジタルサイネージ12】現在の天気を表示する「WeatherRModule」
- 【Bラボデジタルサイネージ13】直近2件のバス時刻表を表示する「BusRModule」
- 【Bラボデジタルサイネージ14】ごみ出しカレンダーを表示する「GomiRModule」
- 【Bラボデジタルサイネージ15】(発展編)ツイートを表示する「TwitterRModule」
- 【Bラボデジタルサイネージ16】開店/閉店を表示する「OpenCloseRModule」
- 【Bラボデジタルサイネージ17】部屋の温度を表示する「TemperatureRModule」
- 【Bラボデジタルサイネージ18】部屋の明るさを表示する「BrightnessRModule」
- 【Bラボデジタルサイネージ19】起動画面を表示する「LaunchingScreenModule」(本記事)
- 【Bラボデジタルサイネージ20】RModuleの影を実装
作るもの
現状は起動時に何も表示されない状態のため、データの読み込みが完了するまでの間、起動画面を表示できるようにします。
現状の初期化処理
これまでの記事を通して実装した各機能の実現のため、多くの画像をデジタルサイネージに使用しました。
しかし画像の読み込みやWeb APIの利用といった処理は、完了するまでに時間がかかります。
現状の関数setup()
内での初期化は、関数initialize()
を呼ぶことで実現しています。
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()
が定義されている状態で、以下のように書くとします。
thread("initialize");
上の例だと、関数initialize()
の処理を、描画処理と並行して実行することになります。
これを踏まえて関数setup()
を書き換えます。
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()
の描画が実行されることになります。
(この時点で実行しても、まだエラーになります。)
どこまで初期化されたかを管理
起動画面の「カレンダー取得」「画像取得」など、どこまで読み込みが済んだか(初期化されたか)を示すために、状態管理用の変数を宣言します。
/* 略 */
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()
に追記します。
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()
を定義します。
void drawLaunchingScreenModule() {
}
背景画像と「DIGITAL SIGNAGE」の文字、設置場所を表示します。
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);
/* ここまで追加 */
}
「カレンダー取得」「画像取得」など、どこまで読み込みが済んだかを表示する部分を作ります。
読み込みが済んだ項目は緑色、そうでない項目は黒色の文字で表示します。
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
として扱います。
ページ番号の初期値を変更します。
int nowPageID = -1; /* 変更 */
データの初期化が終わった後に、ページ番号が-1
から0
になるようにします。
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
の場合に対応できるよう条件分岐を書きます。
void draw() {
/* ここから変更 */
if (nowPageID == -1) {
drawLaunchingScreenModule();
} else {
updateDatas();
drawModules();
}
/* ここまで変更 */
}
ここまで記述して実行してみると、読み込みが終わるまで起動画面が表示され、読み込みが終われば画面が切り替わるようになります。
最後に
これで、起動画面を表示する「LaunchingScreenModule」が実装できました。
このシリーズは、次の記事で最後になります。
最後に**RModuleに影を実装**してみましょう。