Shiny平台构建与R包开发(一)——ui布局

本节为Shiny平台构建与R包开发教程的第一小节。

Getting Started

初识Shiny时,了解其工作机理非常重要。下面的案例展示了一个最简单的Shiny APP的工作机理:

#DO NOT include any non-English characters in Shiny script
library(shiny)
ui <- fluidPage("Hello World!")
server <- function(input, output){}
shinyApp(ui, server)

其运行方式和其他R语言代码没有太大的差别。直接让光标定位在脚本文件中,单击Run或按快捷键Ctrl+Enter将代码逐行运行。 ui变量定义了Shiny APP的ui设计,server函数定义了Shiny APP的服务器行为。input参数代表用户向服务器中输入的数据,output代表服务器向用户展示的内容。

当运行shinyApp函数时,Shiny调用图形用户界面(GUI),打开网页。只要网页不被关闭,shinyApp就一直处于运行状态,用户也就无法在Rstudio的命令行窗口运行其他命令。

不要马上急着关闭弹出的GUI。点击“Open in Browser”按钮,按下“(Fn)+F12”按钮,进入浏览器调试模式。如果您了解过HTML+CSS+JS的基本知识,请耐心阅读Box1部分内容,以深刻地理解Shiny是如何生成HTML网页的。这对后续ui排版及CSS设计的深入理解有很好地辅助作用。

Box1 Shiny生成HTML网页的内在机制
进入浏览器调试模式后,打开<head>标签,可以找到<link>引入了bootstrap的css文件,表明Shiny的CSS是基于bootstrap开发的。展开<body>标签,发现类名为container-fluid的一个<div>标签。这表明Shiny中fluidPage函数的功能是向网页中插入一个<div class="container-fluid"></div>。而这一标签的具体样式显然在CSS文件中已被定义了。

退出GUI界面,回到R终端,运行print(ui),同样也可以看到ui变量本质上就是个<div>。再来看一个更复杂的例子。

ui <- fluidPage(
 titlePanel("title Panel"),
 sidebarLayout(
   sidebarPanel("sidebar Panel"),
   mainPanel("main Panel")    
 )
)
server <- function(input, output){}
shinyApp(ui, server)

先运行print(ui),发现在fluidPage函数构建的<div class="container-fluid">的基础上,titlePanel函数在内部插入了<h2>标签;siderbarLayout函数插入了<div class="row">标签;sidebarPanelmainPanel函数分别在row标签的div区块中分别插入<div class="col-sm-4"><div class="col-sm-8" role="main">。除此之外,很重要的一点是titlePanel还在网页的<head>标签中插入了<title>标签,即<title>title Panel</title>。因此,对应网页的标签也自动变成了title Panel了。
总之,ui变量在R中存储的仅仅是字符串,但其内部字符的语法遵从HTML语言,在网页端根据HTML语法翻译成对应元素。此后的ui设计案例也可以用同样的方法查看每个函数的作用。

元素布局

最常用的网页内元素布局是将页面划分为不同的“行”和“列”。以Bing为例,若想要“复刻”一个Bing的首页,一种方法是:将网页划分为6行,每一行又可以分为不同数量的列。每一“行”是一个<div class="row">,每一列是嵌套在“行”<div>里面的<div class="column">。因此,在Bing首页中,第一行需要设置6个“列”<div>,第一个<div>放Microsoft的logo以及文字“Microsoft Bing”,一直到最后一个<div>放一个功能选项按钮。第二行只需要一个“列”<div>,并且是空白的。最后一行也只有一个“列”<div>,放置许多版权信息和链接。
在这里插入图片描述图1 ui布局—以Bing为例
这一布局落实到Shiny中就变成:

ui <- fluidPage(
  fluidRow(column(),column(),column(),column(),column(),column()),
  fluidRow(column()),
  fluidRow(column()),
  fluidRow(column()),
  fluidRow(column(),column(),column(),column()),
  fluidRow(column())
)

其中每个column函数填上对应的HTML元素,在R中以遵循HTML语法的字符串形式表示。但问题是,每个“列”<div>的宽度怎么定义?column函数中有width参数,其取值在0~12间,其中12代表与电脑显示屏的宽度相等。若取值为4,相当于这个column产生的区域为电脑显示屏宽度的4/12=1/3。

关于更多的ui页面内布局方式,可以参考以下博客:
https://blog.csdn.net/weixin_27744023/article/details/112742221

之前演示的sidebarLayout在这篇博客中定义为“左右布局”,同样也可以用width参数控制side panel和main panel的宽度;这篇博客中涉及的“分页布局”在实际Shiny APP中也应用较多。如果您还不知道Shiny中server变量定义的语法,您可以将这篇博客的案例代码中将与ui布局无关的函数全部去掉,替换成普通字符串,以理解每个案例的运作机理。

页面布局

无论是bibliometrix包还是Expmeasure包,其Shiny GUI页面顶部都有导航栏(navigation bar)。但fluidPage函数并不能实现这一功能。

R Shiny中最常用的两种页面布局函数是fluidPage(静态页面)和navbarPage(导航页面)。下面的案例非常典型,介绍了如何搭配使用这两个函数构造整个Shiny数据分析平台的ui。

ui.overall <- navbarPage("expmeasures",
                         tabPanel("Home", ui.home),
                         tabPanel("Data", ui.data),
                         
                         navbarMenu("Trend",
                                    tabPanel("2D Plot", ui.trend.2D_Plot),
                                    tabPanel("3D Plot", ui.trend.3D_Plot),
                                    tabPanel("VioPlot", ui.trend.VioPlot)
                         ),
                         
                         navbarMenu("Error",
                                    tabPanel("Outlier", ui.error.Outlier),
                                    tabPanel("Leverage", ui.error.Leverage),
                                    tabPanel("Influence", ui.error.Influence)
                         ),
                         
                         navbarMenu("Significance",
                                    tabPanel("K-function", ui.significance.k),
                                    tabPanel("GLMs", ui.significance.GLMs),
                                    tabPanel("Residue", ui.significance.residue),
                                    tabPanel("Jackknife", ui.significance.jackknife)
                         ),
                         
                         navbarMenu("Explantory",
                                    tabPanel("ANOVA", ui.explantory.ANOVA),
                                    tabPanel("Muti Comp", ui.explantory.muticomp)
                         ),
                         
                         navbarMenu("Prediction",
                                    tabPanel("Trend", ui.prediction.trend),
                                    tabPanel("Spline", ui.prediction.spline),
                                    tabPanel("Inverse", ui.prediction.inverse),
                                    tabPanel("Simp Mov", ui.prediction.simmov),
                                    tabPanel("Krigging", ui.prediction.krigging)
                         ),
                         
                         navbarMenu("Uncertainty",
                                    tabPanel("TNE", ui.uncertainty.TNE),
                                    tabPanel("T/E Simu", ui.uncertainty.TNEsimu)
                         ),
                         
                         tabPanel("Quit")
) 

您可以运行expmeasure函数,观察Expmeasure包导航栏结构与上述代码的对应关系。 navbarPage函数的第一个参数在导航栏的左侧显示; tabPanel函数构造了导航栏中的一个模块,鼠标点击模块时会跳转到对应页面(对应页面的ui被tabPanel函数的第二个参数指定); navbarMenu函数同样也构造导航栏中的一个模块,但鼠标点击该模块时会出现下拉菜单,显示若干子模块。每个子模块又由tabPanel函数构造,tabPanel函数的第二个参数定义了对应页面的ui。

那么,所谓对应页面的ui又应该怎么定义?这里以Expmeasure的HOME页面为例:

ui.home <- fluidPage(
  sidebarPanel(
    fluidRow(
      column(12, align = "center", imageOutput('igem_icon', height = "250px"))
    ),
    fluidRow(
      column(12, align = "center", h1("Expmeasure: A tool for part characterization"))
    ),
    fluidRow(
      column(12, align = "center", h2("Developed by ZJU-China, 2021"))
    ),
  width = 12)
)

可见,每个对应页面的ui就是一个fluidPage。至于在fluidPage中要填什么元素,就是“元素布局”应该要考虑的事情。这里HOME页面只放一个Side Bar,让它的宽度占满整个电脑屏幕(width=12)。在Side Bar中放置三行,第一行放一个图片,第二行放一行h1标题文字,第三行放一行h2标题文字。column函数的第一个参数就是width参数,注意这里width=12就不是占满整个电脑屏幕,而是占满整个Side Bar的宽度,因为fluidRow被嵌套在了sidebarPanel里面(如果学过HTML,我们知道嵌在里面的一个div的长宽不会超过它外面的div)。

至此,您已经通过本教程了解到如何设计网页布局,并能设计导航页面和静态页面,在静态页面中插入标题、文字。但您可能马上就有欲望在页面中插入图片,因为这会使网页更好看。

插入图片

事实上,上述代码块已经初步展示了在Shiny页面中插入图片的语法,下面展示完整代码:

ui <- fluidPage(
  sidebarPanel(
    fluidRow(
      column(12, align = "center", imageOutput('igem_icon', height = "250px"))
    ),
    fluidRow(
      column(12, align = "center", h1("Expmeasure: A tool for part characterization"))
    ),
    fluidRow(
      column(12, align = "center", h2("Developed by ZJU-China, 2021"))
    ),
  width = 12)
)

server <- function(input, output){
output$igem_icon <- renderImage({
  list(src = './expmeasure/R/logo.png')
}	, deleteFile = FALSE)
}

shinyApp(ui, server)

上述代码的ui部分中,核心部分是imageOutput函数。该函数的第一个参数指定了图像输出区域的编号。这里编号为"igem_icon"。在server变量中,由"output$igem_icon"识别相应编号,并利用renderImage函数在该区域输出本地路径为’./expmeasure/R/logo.png’的图片。

关于数据输出(包括table、plot、text等)的详细机理将在第四节介绍,图片输出只是服务器输出数据的一种形式。了解图片输出方式后,其他的数据输出形式十分类似。例如输出table只需将 imageOutput renderImage函数组合替换成 tableOutput renderTable即可。

下一篇: Shiny平台构建与R包开发(二)——数据输入

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HaoranWu_ZJU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值