本記事は、Shiny Advent Calendar 2017の12日目の記事です。
今回は、Shiny100本ノックの第13弾です。
前回・前々回と、Drag&DropでShiny上にデータを読み込み、そのデータをプロットしてみる、ということを実践しました。
www.randpy.tokyo
www.randpy.tokyo
こちらを公開したところ、
これ便利ですね。画像ファイルをドラッグ&ドロップして表示するサンプルとかも見てみたいです。
— R_はじめました。 (@R_beginner) 2017年12月9日
という嬉しい反応をいただきました!
@R_beginnerさんありがとうございます!
と、いうことで!
やってみます!
Githubに一応コード置いてあります。といっても前回と大体一緒ですが、よろしければこちらも是非.
github.com
完成形のイメージ
まずは完成形イメージを共有します。ドラッグ前
ドラッグ後
前回・前回同様、赤枠部分に画像をドラッグ&ドロップします。
すると、右側に画像が表示されるという、非常にシンプルなShinyアプリケーションです。
これだけだと機能として寂しいですが、皆さん色々組み合わせて面白いアプリケーションに変身させてください!
ソースコード解説
ソースコードを紹介します。CSSファイルは前回・前々回と同様なので割愛します。
drag_and_drop.js
まずは、jsファイルから紹介します。var datasets = {}; var f1 = function(e) { e.preventDefault(); //ドラッグを許可するための定型文 }; var f2 = function(e) { e.preventDefault();//ドラッグを許可するための定型文 var file = e.dataTransfer.files[0]; var reader = new FileReader();//ファイルを読み込むためのオブジェクト reader.name = file.name;//onload処理で名前を取り出すために追加...bad know-howらしい reader.onload = function() { Shiny.onInputChange("imgfile", reader.result); }; reader.readAsDataURL(file);//この読み込みが終わってから上のonload処理が始まる };
前回は、csvファイル内のテキスト情報を読み込む必要があったので、
reader.onload = function(e) { datasets[e.target.name] = e.target.result; Shiny.onInputChange("mydata", datasets); }; reader.readAsText(file);
という風に、reader.readAsText()と書いていました。
しかし今回ドラッグ&ドロップで読み取るのは、画像ファイルなのでここを「reader.readAsDataURL()」と変える必要があります。
そして前回同様「Shiny.onInputChange()」メソッドを使って、shinyのinput変数として画像ファイル情報を渡してあげます。
server.Rのソースコード
次に、server.Rです。library(shiny) server = function(input, output, session) { observeEvent(input$imgfile, { output$image = renderUI({ img(src = input$imgfile, alt=input$imgfile) }) }) }
renderUIメソッドを使って、ui.R側で描写する内容を記述していきます。
「src = input$imgfile」と書くことで、imgタグのsrc属性に、jsファイルで受け取った画像情報を渡すことができます。
alt属性にも同じ内容を入れていますが、ここは何でもOkです。
ui.Rのソースコード
最後に、ui.Rです。library(shiny) shinyUI( fluidPage( tags$head(tags$link(rel = "stylesheet", href = "styles.css", type = "text/css"), tags$script(src = "drag_and_drop.js")), sidebarLayout( sidebarPanel( h2("赤枠に画像ファイルをドロップ"), div(id="drop-area", ondragover = "f1(event)", ondrop = "f2(event)") ), mainPanel( htmlOutput("image") ) ) ) )
今回変更したところは、mainPanel内のみです。
ここではhtmlOutputメソッドを使うことで、server.Rで記述した以下の内容をそのまま描写してくれます。
output$image = renderUI({ img(src = input$imgfile, alt=input$imgfile) })
ちょっとつまったところ
当初はShinyでよく使うrenderImageメソッドを使って画像を出力しようと思っていたのですが、なぜかうまくsrc属性が渡せず…結構粘ったのですが、諦めてもうrenderUIで渡せばいいやん!と気づいてからは一瞬でした笑
まとめ
前回・前々回で行ったドラッグ&ドロップによるデータ読み込み機能の番外編として、画像ファイル読み込み機能を実装してみました。リクエストというのは初めての経験だったのですが、非常に嬉しいですね!
@R_beginnerさんが求めていたものがこれだったのか、少し不安ではありますす……。
もし「こういう機能欲しいんだけどどうすればいい?」というのがあれば、どんどんリクエストくださいませ!