某天,我发现了Shiny这个东西,当时兴冲冲的尝试官网上各类各样的例子,最后发现这个东西彷佛只能充当一个“玩具”。若是要在本地运行,它须要一个完整的R环境,这对至关一部分用户来讲是极度不友好的。另外,Rstudio主张将Shiny部署在https://www.shinyapps.io/,可是看到这个价格以及资源限制之后进一步被劝退了。html
毕竟不少科研工做者的出发点是将本身的研究过程和结果分享展现给他人,而不是出于商业的目的,部署在服务器上供他人使用须要持续投入计算资源和维护成本,这不是长久之计。node
那么,若是咱们实现了一个精妙的Shiny App,如何0成本的分享给别人,且别人可以方便的使用呢?为了达到这个目的,最好的结果是将R中的Shiny App转换为一个独立运行的exe文件,也就是一个这样的桌面应用:react
对,我实现了,过程当中仍是踩了一些坑的,如今就把具体的方法分享给你们。这是我本身思考的方法,由于本人也是刚开始研究,可能还有些地方理解的不是很清楚,若是您有更好的建议,恳请不吝斧正。git
刚开始我是看了这个stone大神写的贴做为启蒙:https://zhuanlan.zhihu.com/p/121003243,可是我没能在本身电脑上实现,由于electricShine这个东西是一个写死的包,写死既被动,在调用npm的时候总会有小小的问题致使全盘失败。虽然没有成功实现,可是我确定是不服的。后来我又看了某机构的博客:https://foretodata.com/how-to-make-a-standalone-desktop-application-with-shiny-and-electron-on-windows/,感受上可行,尝试之后发现跑通了,确实能够。可是以上都很差做为最终的解决方案。github
那么一个最为方便且易于实现的思路是这样的:shell
连接:https://sourceforge.net/projects/rportable/files/R-Portable/3.6.3/npm
强烈建议这个3.6.3版本,比较稳定,4.0.0编译暂时有问题。windows
安装比较简单,注意将路径设置为咱们新建的工做目录,安装完成便可。bash
如今咱们要开启R-Portable做为R环境
打开Rstudio,鼠标点:Tools>Global Options>General>Change R version>Browse
定位咱们刚才安装的R-Portable路径(C:\myShinyApp\R-Portable\App\R-Portable)
而后点选择文件夹,选择64位版本服务器
一路点OK,最后重启Rstudio
.libPaths()里有咱们刚才装好的R-Portable就行了:
> .libPaths() [1] "C:/Users/XXX/Documents/R/win-library/3.6" [2] "C:/myShinyApp/R-Portable/App/R-Portable/library"
注意:这里出现了两个路径,[1]是我原来就有的,[2]是刚装的,ShinyApp中全部要用到的包必须装在[2]里。
golem包是开发Shiny App的辅助开发工具,用它可让开发过程更加方便。
先在Rstudio中安装这个包:
install.packages('golem',dependencies = T)
安装完成后,在Rstudio中点菜单:File>New Project>New Directory>Package for Shiny App using golem
将Directory name随意设置为shinyapptest,路径定位到咱们的工做目录
建立完成后,咱们就在Rstudio中开辟了一个新的Project和工做环境,且工做目录出现了一个相似于R包的结构:
根据golem的Document,咱们主要关注./dev
中的三个脚本01_start.R
,02_dev.R
,03_deploy.R
以及./R
中的三个脚本app_ui.R
,app_server.R
,run_app.R
。
假如咱们如今要实现文章开头例2提到的csv表格查看器。
载入csv文件的按钮就是一个模块(按钮自己是模块的UI,读取csv文件是这个模块的功能),咱们运行./dev/02_dev.R
中的add_module
添加一个模块
## Add modules ---- ## Create a module infrastructure in R/ golem::add_module( name = "csv_file" ) # Name of the module
结果./R
路径下生成了一个以mod_
为前缀的模块文件,
把mod_csv_file.R
这个文件的内容改为这样的:
#' csv_file UI Function #' @description A shiny Module. #' @param id,input,output,session Internal parameters for {shiny}. #' @noRd #' @importFrom shiny NS tagList mod_csv_file_ui <- function(id, label = "CSV file"){ ns <- NS(id) tagList( fileInput(ns("file"), label), checkboxInput(ns("heading"), "Has heading"), selectInput(ns("quote"), "Quote", c( "None" = "", "Double quote" = "\"", "Single quote" = "'" )) ) } #' csv_file Server Function #' @noRd mod_csv_file_server <- function(id, stringsAsFactors) { moduleServer( id, ## Below is the module function function(input, output, session) { # The selected file, if any userFile <- reactive({ # If no file is selected, don't do anything validate(need(input$file, message = FALSE)) input$file }) # The user's data, parsed into a data frame dataframe <- reactive({ read.csv(userFile()$datapath, header = input$heading, quote = input$quote, stringsAsFactors = stringsAsFactors) }) # We can run observers in here if we want to observe({ msg <- sprintf("File %s was uploaded", userFile()$name) cat(msg, "\n") }) # Return the reactive that yields the data frame return(dataframe) } ) }
模块的定义包含两个部分:mod_csv_file_ui
定义模块UI,mod_csv_file_server
定义模块功能,若是要使用这个模块只需在Shiny App的app_ui
中调用前者,app_server
中调用后者就能够了。
咱们将app_ui.R
改成这样的:
#' The application User-Interface #' @param request Internal parameter for `{shiny}`. #' DO NOT REMOVE. #' @import shiny #' @noRd app_ui <- function(request) { tagList( # List the first level UI elements here fluidPage( sidebarLayout( sidebarPanel( mod_csv_file_ui("datafile", "User data (.csv format)") # 调用模块UI ), mainPanel( dataTableOutput("table") ) ) ) ) }
为了节省空间我把golem导入外部资源的部分去除了。
而后将app_server.R
改为这样的:
#' The application server-side #' @param input,output,session Internal parameters for {shiny}. #' DO NOT REMOVE. #' @import shiny #' @noRd app_server <- function(input, output, session) { datafile <- mod_csv_file_server("datafile", stringsAsFactors = FALSE) # 调用模块function output$table <- renderDataTable({ datafile() }) }
改好这些文件之后咱们在./dev/run_dev.R
脚本中测试一下咱们的Shiny App:
> # Detach all loaded packages and clean your environment > golem::detach_all_attached() 错误: $ operator is invalid for atomic vectors 此外: Warning message: In FUN(X[[i]], ...) : DESCRIPTION file of package 'shiny' is missing or broken
运行到上面这一条提示咱们尚未装shiny这个包,那就装吧:
install.packages(pkgs = 'shiny', lib = .libPaths()[length(.libPaths())], # 保证装到R-Portable的lib里 dependencies = T) # 保证同时安装依赖
再次运行这一条,发现成功了:
> # Detach all loaded packages and clean your environment > golem::detach_all_attached() >
最后运行run_app
# Run the application library(golem) library(shiny) source('./R/app_server.R') source('./R/app_ui.R') source('./R/mod_csv_file.R') source('./R/run_app.R') run_app()
出现下面这个界面Shiny App基本上就成了,能够打开一个csv文件本身测试一下。
假若有一天,咱们精妙的Shiny App终于大功告成了,那么能够将他打成package并安装到R-Portable中。
先准备一下devtools:
if(!requireNamespace("devtools")){ install.packages("devtools") library(devtools) }
而后打包shinyapp,路径为当时golem建立的项目路径:
devtools::build(path = "C:/myShinyApp/shinyapptest") √ checking for file 'C:\myShinyApp\shinyapptest/DESCRIPTION' ... - preparing 'shinyapptest': √ checking DESCRIPTION meta-information ... - checking for LF line-endings in source and make files and shell scripts - checking for empty or unneeded directories - building 'shinyapptest_0.0.0.9000.tar.gz' [1] "C:/myShinyApp/shinyapptest/shinyapptest_0.0.0.9000.tar.gz"
安装这个打包成功的packageshinyapptest_0.0.0.9000.tar.gz
:
install.packages( pkgs = 'C:/myShinyApp/shinyapptest/shinyapptest_0.0.0.9000.tar.gz', lib = .libPaths()[length(.libPaths())], repos = NULL, # 这个参数必定要的 dependencies = T ) # 尝试用包直接运行app shinyapptest::run_app()
shiny具体的开发文档仍是要研究一下:https://shiny.rstudio.com/articles/。好了,R的工做完成了剩下的交给electron-quick-start。
去这个连接下载zip压缩文件:https://nodejs.org/download/release/v12.16.2/node-v12.16.2-win-x64.zip
我装的是v12.16.2版本,若是嫌下载慢的话,想一想办法,这里我分享一个网盘给大家:
连接: https://pan.baidu.com/s/1QbLJcfhRqTsgUeQ10Wy7wA
提取码: 4gzh
这是解压版,安装版也是同理的。下载完成后解压到指定目录,能够是咱们的工做目录,解压完之后是这样的:
在这个目录中新建两个文件夹node_global
和node_cache
:
新建一个系统变量,变量名是NODE_PATH
,值是nodejs的解压或安装目录C:\myShinyApp\node-v12.16.2-win-x64
:
新建另外一个关键的系统变量,变量名是NODE_TLS_REJECT_UNAUTHORIZED
,值是0
,我以为这个变量很关键:
编辑Path
环境变量,新建这两个值:C:\myShinyApp\node-v12.16.2-win-x64
和C:\myShinyApp\node-v12.16.2-win-x64\node_global
(忽略图中的大小写笔误)
如今,以管理员身份打开优秀的Windows Powershell,检查node和npm是否安装正常:
> node -v v12.16.2 > npm -v 6.14.4
配置一些必要的npm参数:
> npm config set prefix "C:\myShinyApp\node-v12.16.2-win-x64\node_global" > npm config set cache "C:\myShinyApp\node-v12.16.2-win-x64\node_cahce" > npm config set strict-ssl false > npm config set registry http://registry.npm.taobao.org/
以上配置就是为了可以成功安装这个包
> npm install electron-packager -g # 出现如下信息说明成功 # + electron-packager@15.2.0 # added 18 packages from 9 contributors, removed 10 packages and updated 8 packages in 4.188s
若是方便在命令行用git的话(我通常是用WSL+Cmder),就先cd
到C:\myShinyApp\electron-quick-start
,而后clone项目:
$ git clone https://github.com/listen2099/electron-quick-start.git
若是不方便用git,就直接下载链接中的zip文件解压到C:\myShinyApp\electron-quick-start
:https://github.com/listen2099/electron-quick-start/archive/master.zip
拉取或解压成功后:
再次以管理员身份打开优秀的Windows Powershell:
> cd C:\myShinyApp\electron-quick-start > npm install # 出现如下信息就明名安装成功 # > electron@5.0.7 postinstall C:\myShinyApp\electron-quick-start\node_modules\electron # > node install.js # added 148 packages from 139 contributors in 4.326s
接下来是关键的一步:
将R-Portable路径C:\myShinyApp\R-Portable\App\R-Portable
下的全部文件复制并替换到C:\myShinyApp\electron-quick-start\R-Portable-Win
路径:
?还记得吗?这个环境里有咱们安装好的R环境、写好的ShinyApp以及依赖的R包(其实,ShinyApp也做为包安装在这个R环境了,依稀记得包名叫shinyapptest)。
回到C:\myShinyApp\electron-quick-start
,编辑这个目录下的app.R
文件,这个文件是程序的入口,那么你猜这个文件应该写什么?要不就试试写这一行内容保存:
# app.R shinyapptest::run_app()
最后一次打开优秀的Windows Powershell,完成最后的打包
> cd C:\myShinyApp\electron-quick-start > npm run package-win # 出现如下信息就说明成功了 # Packaging app for platform win32 ia32 using electron v5.0.7 # Wrote new app to ElectronShinyAppWindows\electron-quick-start-win32-ia32
C:\myShinyApp\electron-quick-start
文件夹下出现了一个新的目录:
双击exe文件:
成功!