R Shiny : タブに表示したアイコンを動的に変更する方法

本日の話題は、Shinyのタブのアイコンを動的に切り替えて表示する方法です。

タブにアイコンを表示することは標準機能として簡単にできますが、そのアイコンを動的に切り替える方法がいまいちわからなかったので
調査した結果をメモします。

f:id:okdata:20190916124858p:plain:w400

上記の画像でクリップのアイコンをプログラムで切り替える方法です。
例によってややマニアックな話題ですが、このブログは自分が必要と思って調べた話題をメモ的に残しているので
こういった話題が多くなりますがご了承ください。

tabPanelへのアイコンを表示する方法の基本を確認

Shinyのダッシュボードのタブにアイコンを表示することは標準の機能を使えば簡単に実現できます。
使い方は、以下のマニュアルを始めいろいろなブログなどで確認できます。

ではこの基本形を参考プログラムを以下に示しました。
この例では、見やすくするためにダッシュボードのBodyを切り出しています。基本はtabPanelの中で`icon’の種類を指定するだけです。

library(shiny)
library(shinydashboard)
dbBody =
dashboardBody(
tabsetPanel(id = "tbID",
tabPanel(value = "tab1",
title = "Sample",
icon = icon("flag"),
#contents
br(),
"This is sample tab!"
)
)
)
)
ui = {
dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dbBody
)
}
server <- function(input, output) {
}
shinyApp(ui, server)

プログラムを実行するとタブに旗のアイコンが表示されます。このアイコンの種類を変えるには、
font awesomeかgryphiconsのアイコンを指定する必要があります。そのあたりの詳細はここを参照してください。

※なおtabsetPanelid=tabPanelvalue=は省略することも可能です。
プログラムからタブを指定して表示させる場合などにはこれらのIDを一意の名称で指定しておく必要があります。

アイコンの動的な変更

さてここで本日の話題のタブのアイコンを動的に切り替える方法についてです。

結論から言えば、ポイントは、タブのタイトル部分をuiOutputで表示することです。

まずは以下のサンプルプログラムを参照ください。

library(shiny)
library(shinydashboard)
library(shinyWidgets)
tabImage = data.frame(
val = c("clip", "caution", "crown")
)
tabImage$img = c(
sprintf("<img src='./clip.png' width=20px><div class='tbicn'>%s</div></img>", tabImage$val[1]),
sprintf("<img src='./caution.png' width=20px><div class='tbicn'>%s</div></img>", tabImage$val[2]),
sprintf("<img src='./crown.png' width=20px><div class='tbicn'>%s</div></img>", tabImage$val[3])
)
dbBody =
dashboardBody(
tags$head(tags$style("
                         .tbicn{
                         display: inline;
                         vertical-align: middle;
                         padding-left: 10px;
                         }")),
tabsetPanel(id = "tbID",
tabPanel(value = "tab1",
title = uiOutput("title_panel"),
fluidRow(
column(width=3,
pickerInput("tabImg", label="Mark",
choices = tabImage$val,
choicesOpt = list(content = tabImage$img))
),
column(width=9,
textAreaInput("inputTextInd1", height = "2.6em", label = "", placeholder = "comments here on this trial")
)
)
)
)
)
ui = {
dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dbBody
)
}
server = function(input, output) {
output$title_panel = renderUI({
switch(input$tabImg,   "clip" = tagList("Trial ",img(src="./clip.png", width="20px", height="20px")),
"caution" = tagList("Trial ",img(src="./caution.png", width="20px", height="20px")),
"crown" = tagList("Trial ",img(src="./crown.png", width="20px", height="20px")))
})
}
shinyApp(ui, server)

このプログラムを走らせると以下のような画面になります。

f:id:okdata:20190916124626p:plain:w400

このプログラムでは3つのアイコン(画像)を使っていますが(clip, caution, crown)、
この画像は自身の環境に合わせて正しいフォルダーの所在と画像名を指定してください。
R-Shinyのプロジェクトで管理している場合には、作法に従ってプログラムがある階層と同じところにwww
ホルダーを作ってその中に画像を入れてください。そのうえで画像の場所は、img src="./clip.png"のように./画像名ですね。

tabPanelへの表示

tabPanelへの表示は比較的簡単です。
プログラムの44から48行目でタブに表示する画像を作り、
プログラムの22行目title = uiOutput("title_panel"),で表示しています。

表示する内容はtagListで文字(”Trial”)と画像(img)を並べて表示しています。

プルダウンによる選択リストへの画像の表示

続いてどの画像を表示するかの選択側のプログラムですが、この選択にはshinywidgetpickerInputを利用しています。

一般的にはプルダウンでの選択にはselectInputを使いますが、これを利用して選択肢に画像を表示させる方法が調べた限りややこしそうだったので、
shinywidgetの作者によるこの回答
このサイトを参考にpickerInputを利用して作成しました。

プログラムの4−6行目にtabImage$valにプルダウンの選択肢を入れて、プログラムの7−11行目でtabImage$imgでそれぞれの選択肢に対する
画像をセットしています。それを利用してプログラムの25−27行目でpickerInputで表示させています。

このpickerInputのなかでchoicesOptの部分が画像表示の肝になっています。このあたりは上記のサイトを参考にとりあえず動くサンプルは作成していますが、本来はもう少し具体的にchoicesOptの詳細を調査して他の手法などについても検討したいところです。ところが、どうも調査が甘いのかselectInputchoicesOptの使い方に関する詳細がわかるサイトがあまり見つかりませんでした。

とりあえず以下のサイトにはいくつか有効なサンプルが掲載されていますので参考になるかと思われますのでメモしておきます。また、詳しくわかったらこのブログにUPしたいと思います。

なお、プログラム14−19行目はリスト画面でテキストとアイコンを並べて表示するためにセットしています。
tbicnのクラスではインライン表示をするように指定しています。もしこの部分がないと文字とアイコンが
縦並びに出力されますので、そちらがお好みの場合にはこの部分をコメントアウトしてください。

さらなる拡張

この例ではタブが一つの例ですが、実際のプログラムでは複数のタブを持つことが多いかと思います。その場合には、
tabPanelvalueが重複しないように設定したり、uiOutput("title_panel")のidをタブごとにtitle_panel1, title_panel2のように
変更して一意に設定しておく必要があるのでご注意ください。