shiny【1】——基本框架和步骤

介绍如何使用shiny创建网页。


ui构建HTML交互,server来render输出和对输入作出反馈。

  1. 新建一个shiny项目,在app.R中设定用户函数和开发者函数,并输出可视化结果

  2. 增加UI控制

  1. 增加习惯

我们将通过在服务器函数中定义输出来实现它们。

根据我的使用习惯记录一下基本步骤。新建一个shiny项目,在app.R中写入各种参数。

1. 选主题

在shiny扩展包中有很多主题可以选择,我选择的是shinydashboard,接下来的使用和修改也是在这个基础上进行的。主要是ui,serve和shinyAPP这三个部分。

2. 搭框架

页面需要有基本的布局,shinydashboard主题中我主要使用到的有两块:

  • 侧边栏

  • 页面

其中页面中有两个重要的部分:

  • 页面布局

  • 各种类型的页面元素

2.1 侧边栏

shinydashboard中为例:

library(shinydashboard)

ui <- dashboardPage(
  skin = "purple", # 设置主题颜色

  dashboardHeader(title = "database"), # 设置网页标题

  dashboardSidebar( 
    sidebarMenu(
      menuItem("sidebar1", tabName = "sidebar1", icon = icon("dashboard"))
    )
  ), # 设置主题侧边栏

# 设置网页内容
  dashboardBody(
    tags$head(tags$style(HTML('
      .main-header .logo {
        font-family: "Georgia", Times, "Times New Roman", serif;
        font-weight: bold;
        font-size: 20px;
      }
    '))), # 更改网页标题的字体和大小

    tabItems(
      # 侧边栏页面内容
      tabItems(
        tabName = "sidebar1",
        column(
          width = 8,
          box(
            title = "siderbar title"
          )
        )
      )
    )
  )
)

server <- function(input, output, session) {
  output$files <- renderTable(input$upload)
}

shinyApp(ui, server)
  • dashboardHeader(title = “title name”) 设置标题。

  • dashboardSidebar是布局侧边栏,在其中使用menuItem设置具体的分页面。

  dashboardSidebar(
    sidebarMenu(
      menuItem("Page1", tabName = "Page1", icon = icon("home")),
      menuItem("Page2", tabName = "Page2", icon = icon("fa-solid fa-database")),
      menuItem("Page3", tabName = "Page3", icon = icon("table"))
      ))
  • dashboardBody可以指定每个分页面内容的具体布局和组分。

2.2 页面布局

页面中可以填充各种各样的元素,这些元素都要按照一定的布局去排布,布局有各种形式,下面逐一介绍。**将多个元素使用特定的函数组合成一个“单个元素”,该“单个元素”具有自己的属性和面板功能。**使用布局功能将面板和元素组织到一个布局中。添加元素作为布局函数的参数。

不同的布局可以嵌套使用。

2.2.0 直接布局

代码规则:

fluidPage(
  fluidRow(
    column(4, 
      ... 
    ),
    column(8, 
      ...
    )
  ),
  fluidRow(
    column(6, 
      ...
    ),
    column(6, 
      ...
    )
  )
)

...处放置各种组件。

2.2.1 fluidRow布局

一个网址可能不止一个页面,在当前主题(shinydashboard)中,使用dashboardPage替代fluidPage设置页面,tabItems负责设置每个子页面,单个页面上使用fluidRow对进行排布,分成上下布局板块。每个元素之间用,隔开,比如box(),infoBox()等等。

column函数是在UI中设置列,其中参数:

  • width:设置元素的宽度,取值范围在1~12之间。

  • offset:可以默认,也可以设置数值,从上一列的末尾偏移此列的列数。本质上是调整元素的位置。

2.2.2 flowLayout布局

2.2.3 sidebarLayout布局

2.2.4 splitLayout布局

2.2.5 verticalLayout布局

2.2.6 tabsetPanel布局

图标

通常在该网页上搜索自己想要的图标:https://fontawesome.com/icons,在可使用icon的地方使用语法:icon = icon("fa-solid fa-database")导入图标。

搜索框

shinyWidgets包提供了几个非常好看的搜索框。

install.packages("shinyWidgets")

该包提供多个搜索组件:

shinyWidgets::searchInput(
              inputId = "id", 
              label = "Enter your search :", 
              placeholder = "This is a placeholder", 
              btnSearch = icon("search"), 
              btnReset = icon("remove"), 
              width = "100%"
            )

修改标题字体

注意,字体样式的定义要放在ui的page函数里面,引用自定义的字体样式时,直接使用自定义的名即可,比如对Title设置字体加粗,定义的样式名为my-title,用法如下:

ui <- fluidPage(
  tags$head(
    tags$style(HTML('
      /* 自定义标题字体样式 */
      .my-title {
        font-family: "Helvetica", sans-serif; /* 修改字体 */
        font-size: 24px; /* 修改字体大小 */
        font-weight: bold; /* 修改字体粗细 */
        color: #ff0055; /* 修改字体颜色 */
      }
    '))
  ),
  title = tagList(
    span(
      class = "my-title", # 应用自定义样式类
      "Title"
    )
  )
)

侧栏缩进伴随标题缩写

模仿左图中AdminLTE缩写成“ALT”:

加载包(有这么多包是因为忘记了在哪个包中定义的):

library(shiny)
library(shinyjqui)
library(shinydashboard)
library(shinyWidgets)
library(shinydashboardPlus)
library(shinyAce)
library(styler)
library(shinyEffects)

决定侧栏缩进后标题能够有缩写的是"logo-lg"这个样式,HTML行是额外对标题的字体进行设置,text一行表示侧栏缩进之后显示指定文字,还可以使用img=src("/path_to_picture")来指定显示图标。

dashboardHeader(
  title = tagList(
    span(
      class = "logo-lg",
      HTML('<div style="font-family: Arial; font-size: 18px;">AdminLTE</div>')),
    text = HTML('<div style="font-family: Arial; font-size: 14px;"><b>ALT</b></div>')
  ))

层级

1. 整体层级

以下图为例,介绍页面的层级:

为了代码管理的方便,我把每个页面单独创建了一个文件,在app.R中在适当位置直接进行调用,比如该图片展示的页面:repository_tab就是其中一个页面,在app.R中调用:

ui <- dashboardPage(
  dashboardHeader(), # 设置导航栏
  dashboardSidebar(), # 设置侧栏
  dashboardBody(
    # 各种标题、正文等等的格式和字体的设置放在这里
    tabItems(
      first_tab,
      repository_tab # 页面放置位置
    )    
  ),
  title = "shinyDashboardPlus", # 调用的主题title
  footer = dashboardFooter( # 页面脚注
      left = "By XXX",
      right = "XXX Lab, 2023"
    )
  )

server <- function(input, output){} 

shinyApp(ui, server)

2. 单个页面的层级

box里面可以纳入很多组件,比如想要构建左侧为筛选条件,右侧为筛选得到的数据页面:

repository_tab.R的框架:

repository_tab <- tabItem(
  tabName = "repository",

  column(
    width = 12,
    align = "left",
    HTML('<div style="font-family: Arial; font-size: 22px;color: #7d7d73;">test Database</div>')
  ),# test Database 的小标题

  box( # box的设置
    sidebarPanel(),  # 放置box内左侧的选择框
    mainPanel (
      width = 8,
      tabsetPanel(
        tabPanel(),
        tabPanel() # 小页面
    )  # 放置box内右侧的数据框
    )
  )

选择框

1. 树形选择框

library(shinyWidgets)
treeInput(
  inputId = "ID2", 
  label = "Select cities:",
  choices = create_tree(cities), # 使用create_tree函数对cities数据构建树形数据
  returnValue = "text",
  closeDepth = 1
)

cities数据结构如下,每列为大类,列的内容为小类:

cities <- data.frame(
  continent = c("America", "America", "America", "Africa",
                "Africa", "Africa", "Africa", "Africa",
                "Europe", "Europe", "Europe", "Antarctica"),
  country = c("Canada", "Canada", "USA", "Tunisia", "Tunisia",
              "Tunisia", "Algeria", "Algeria", "Italy", "Germany", "Spain", NA),
  city = c("Trois-Rivières", "Québec", "San Francisco", "Tunis",
           "Monastir", "Sousse", "Alger", "Oran", "Rome", "Berlin", "Madrid", NA),
  stringsAsFactors = FALSE
)

2. 树形结构选择框2

代码:

shinyWidgets::virtualSelectInput(
          inputId = "search1",
          label = "Select:",
          choices = list(
            "Spring" = c("March", "April", "May"),
            "Summer" = c("June", "July", "August"),
            "Autumn" = c("September", "October", "November"),
            "Winter" = c("December", "January", "February")
          ),
          showValueAsTags = TRUE,
          search = TRUE,
          multiple = TRUE
        )

特别注意,这里的变量如果有空格,比如变量cancertype中存在Non-Small Cell Lung Cancer,需要额外把它转化为HTML:

choices = list(
            "All" = lapply(cancertype, HTML)
          )

MySQL数据库接入

建立数据库连接,可以直接在app.R中读入数据,也可以单独创建一个用于读取数据的脚本data_select.R:

library(tidyverse)
library(RMySQL)
# 创建连接
db_password <- Sys.getenv("MYSQL_PASSWORD")

con <- dbConnect(MySQL(),
                 user="root",
                 password=db_password,
                 dbname="testdb"
)

query.experiment <- "SELECT DISTINCT `sequencing.platform` FROM example;"
experiment <- dbGetQuery(con, query.experiment)$sequencing.platform

直接把experiment变量正常使用即可,比如多选框:

shinyWidgets::virtualSelectInput(
          inputId = "select_experiment",
          label = "Experimental Strategy :",
          choices = list(
            "Spring" = experiment
          ),
          showValueAsTags = TRUE,
          search = TRUE,
          multiple = TRUE
        )

在server中使用数据库的时候,可以在最后加上一行,来保障关闭网页数据库断连。

    # Close MySQL at last
    on.exit({
      dbDisconnect(con)
    })

小插件

增加空行:br()

安装依赖

跟随《Mastering Shiny》书中安装依赖包:

install.packages(c(
  "gapminder", "ggforce", "gh", "globals", "openintro", "profvis", 
  "RSQLite", "shiny", "shinycssloaders", "shinyFeedback", 
  "shinythemes", "testthat", "thematic", "tidyverse", "vroom", 
  "waiter", "xml2", "zeallot" 
))

参考资料

comments powered by Disqus