shiny【1】——基本框架和步骤
介绍如何使用shiny创建网页。
ui构建HTML交互,server来render输出和对输入作出反馈。
-
新建一个shiny项目,在app.R中设定用户函数和开发者函数,并输出可视化结果
-
增加UI控制
-
fluidPage():设定基本的可视化的页面的框架
-
selectInput():输入控制,用于用户和app进行交互的
-
verbatimTextOutput():输出控制,显示代码,告诉Shiny在哪里放置渲染输出
-
tableOutput():输出控制,显示表格
- 增加习惯
我们将通过在服务器函数中定义输出来实现它们。
- render{Type}:renderPrint和renderTable,用来产生特定的输出(比如文字,表格,图片等等),renderPrint通常和verbatimTextOutput()配对使用,以显示带有固定宽度(逐字)文本的统计摘要,renderTable通常和tableOutput()配合使用,来展示表格中的输入数据。
根据我的使用习惯记录一下基本步骤。新建一个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"
))