Shiny应用基础(3):页面布局

来源:互联网 发布:软件质量保证书范本 编辑:程序博客网 时间:2024/06/07 15:40

页面布局考虑HTML元素在浏览器窗口中的展示位置和顺序。一般情况下,一个shiny程序只需要一个展示窗口,其布局应该很简单随意。然而,shiny把这部分设计得相当复杂,提供了很多相关的函数。这些函数都是在shinyApp的ui参数内使用的,为了方便本文暂把它们称为“UI函数”。

在上一节的内容中我们知道,通过tags列表的设置我们完全可以解决HTML页面元素产生和页面布局的问题,为什么shiny还要提供辣么多的UI函数呢?为说明这一点,请比较下面两个shiny“程序”所产生的HTML代码:

  ## 程序1:  shinyApp(      ui = div(class="container"),      server = function(input, output, session) {}  )  ## 程序2:  shinyApp(      ui = fixedPage(),      server = function(input, output, session) {}  )

运行上面两个“程序”,分别查看页面的源代码,发现两者的 body 内具有相同的代码。但不同的是程序2的 head 部分多了下面几行代码:

  <meta name="viewport" content="width=device-width, initial-scale=1" />  <link href="shared/bootstrap/css/bootstrap.min.css" rel="stylesheet" />  <script src="shared/bootstrap/js/bootstrap.min.js"></script>  <script src="shared/bootstrap/shim/html5shiv.min.js"></script>  <script src="shared/bootstrap/shim/respond.min.js"></script>

这就是关键。 shiny的UI函数不仅产生了相应的HTML元素,还引入、规定或解决了HTML元素的依赖关系(HTML dependency) 。所以嘛,有些UI函数你可以视而不见,但有些你还不得不了解和使用。

1 Bootstrap框架

前面的例子已经看到,虽然我们可以使用div函数对HTML页面进行手工分块布局,但fixPage函数做的工作更多。很显然,Shiny页面布局引入了Bootstrap框架。有关Bootstrap介绍的网络资源非常多,这里就不详细介绍了。如果真的要好好应用shiny,我觉得先了解了解Bootstrap是很有必要的。

要理解shiny布局页面的原理并不难,其实就是bootstrapPage函数和上一篇文章讲过的tags标签。这些函数在shiny源代码包的bootstrap.R和bootstrap-layout.R中定义,很容易找到。简单起见,这里我们只讲用法。

1.1 bootstrapPage函数

shiny的源代码中有一个基本的bootstrapPage()函数,HTML的依赖性(CSS,js)由它确定:

  bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {      if (!is.null(responsive)) {          shinyDeprecated("The 'responsive' argument is no longer used with Bootstrap 3.")      }      ## required head tags for boostrap      importBootstrap <- function() {          list(              htmlDependency("bootstrap", "3.3.1",                             c(                                 href = "shared/bootstrap",                                 file = system.file("www/shared/bootstrap", package = "shiny")                             ),                             script = c(                                 "js/bootstrap.min.js",                                 ## These shims are necessary for IE 8 compatibility                                 "shim/html5shiv.min.js",                                 "shim/respond.min.js"                             ),                             stylesheet = if (is.null(theme)) "css/bootstrap.min.css",                             meta = list(viewport = "width=device-width, initial-scale=1")              )          )      }      attachDependencies(          tagList(              if (!is.null(title)) tags$head(tags$title(title)),              if (!is.null(theme)) {                  tags$head(tags$link(rel="stylesheet", type="text/css", href = theme))              },              ## remainder of tags passed to the function              list(...)          ),          importBootstrap()      )  }


bootstrapPage函数是可以直接调用的,前提是:你得熟悉HTML/CSS和bootstrap,否则建议用fixedPage或fluidPage。这两个函数其实也引用了bootstrapPage函数,调用这些函数就表明我们将在整个页面中应用bootstrap框架。如果你故意避开这些函数不用,那很可能在程序运行中会发生一些抓破脑袋也想不明白的意外情况。


1.2 fixedPage和fluidPage函数

Bootstrap有两种基本页面布局,即固定宽度布局和流式布局,其CSS样式分别由 container 类和 container-fluid 类设置。通常情况下,在固定宽度布局中页面内容的宽度通过固定像素设置;而流式布局中的页面内容则通过屏幕宽度的百分比设置。在Shiny中,页面布局函数 fixedpage() 产生具有 container 类属性的块,而 fluidPage() 则产生 container-fluid 块。

两个函数的调用方法都是一样的:

  fixedPage(..., title = NULL, responsive = NULL, theme = NULL)  fluidPage(..., title = NULL, responsive = NULL, theme = NULL)

  • … 参数:数量不限的页面内容,逗号分隔
  • title 参数:页面标题
  • theme参数:自定义页面的CSS主题样式文件。如果不设置自定义样式,则采用shiny包安装目录下www/shared/bootstrap/css子目录中的样式表文件。如果有自定义的样式表,则需要在应用程序的主目录下建立www目录,并把css文件放到该目录下(可以再设置子目录)。
  • responsive参数:现行shiny版本已弃用该参数,因Bootstrap 3的媒体查询功能已升级。

以上参数没有一个是强制要求设置的。如果不设置任何参数,fixedPage的作用就是产生下面代码:

  <div class="container"></div>

而fluidPage则获得:

  <div class="container-fluid"></div>

title和theme产生的内容放置到HTML代码的 head 标签内,你可以自己尝试设置这些参数,通过查看HTML源代码检查它们的作用。


1.3 navbarPage

navbarPage() 函数用于产生具有顶部导航条页面,而导航条下方页面可以设为固定宽度或流式布局。

  navbarPage(title, ..., id = NULL, header = NULL, footer = NULL,             inverse = FALSE, collapsable = FALSE, fluid = TRUE, responsive = TRUE,             theme = NULL)

  • header:所有标签页顶部均要显示的标题内容,其外部容器为div.row
  • footer:所有标签页底部均要显示的脚注内容,其外部容器为div.row
  • inverse:bootstrap黑底白字风格的顶部导航条
  • collapsable:当屏幕显示宽度小于940px时是否自动折叠导航条使所有的标签均位于导航条内。该参数对于触屏设备很有用。
  • …:所有的其他参数都被当成标签页,产生对应的导航列表项目和内容块,但只有使用tabPanel函数(后面将介绍)设置的内容可以正确显示和切换。

这是一个非常奇怪的函数,试图包揽解决一大堆问题,设计思路很不清。如果你对bootstrap navbar的要求较高,你会发现它可以设置功能少得可怜,只能进行页面内的导航而不是网站的导航。


1.4 已弃用的页面设置函数

  • basicPage:已弃用,现在用fluidPage代替
  • pageWithSidebar:已弃用,现使用fluidPage和sidebarLayout产生相同页面

2 页面布局函数

通过xxxPage函数引入bootstrap框架文件后,页面的内容布局可以灵活设置。如果你熟悉HTML/CSS,下面的绝大多数函数可用可不用。但不管怎么样,这些都应放在xxxPage函数内,因为它们需要bootstrap,除非你手工引物相应的css和js文件。


2.1 窗口与页面标题

有两个函数:headerPanel和titlePanel。它们的用法是一样的:

  headerPanel(title, windowTitle = title)  titlePanel(title, windowTitle = title)

  • title 设置内容标题
  • windowTitle 设置浏览器窗口标题,默认为内容标题

这两个函数的细微差别可以通过查看它们的函数源代码看出来。headerPanel的源代码是:

  headerPanel <- function(title, windowTitle=title) {      tagList(          tags$head(tags$title(windowTitle)),          div(class="col-sm-12",              h1(title)          )      )  }

而titlePanel的源代码为:

  titlePanel <- function(title, windowTitle=title) {      tagList(          tags$head(tags$title(windowTitle)),          h2(title)      )  }

  • 内容标题的标签类型不一样
  • 父容器不一样,headerPanel产生额外的div容器

总体感觉上面两个函数的灵活性太差,产生的标题千篇一律。如果你也认为是这样,可以考虑用tags直接设置,比如:

  shinyApp(      ui = fixedPage(          tags$head(              tags$title('窗口标题'),              tags$style(                  rel = 'stylesheet',                  '.title-panel {background: #ABCDEF} ',                  '.title-panel h2 {text-align:center; color: #FF0000}'              )          ),          div(              class='col-md-12 title-panel',              h2('页面标题')          )      ),      server = function(input, output, session) {}  )


2.2 标签页与导航

shiny应用程序往往只需要一个浏览器窗口,因此它绝大多数(可能是全部)“导航”都是指页面内部的标签页切换,包括前面介绍了navbarPage函数所产生的顶部导航条。

2.2.1 tabPanel

  tabPanel(title, ..., value = NULL, icon = NULL)

设置标签页的基本函数,但不是一个简单的函数。除了设置标签页内的内容(… 参数)外还用于设置导航标签,title、value和icon是用于导航标签设置的参数。它需要在navbarPage、tabsetPanel、navbarMenu等函数内部使用,否则没有太大的意义。

2.2.2 navbarPage、tabsetPanel、navlistPanel

navbarPage函数已经看过了。看一下后两个函数的参数:

  tabsetPanel(..., id = NULL, selected = NULL, type = c("tabs", "pills"),              position = c("above", "below", "left", "right"))  navlistPanel(..., id = NULL, selected = NULL, well = TRUE, fluid = TRUE, widths = c(4, 8))

为什么把navbarPage放在这里,你看看下面代码的效果就理解了:

shinyApp(    ui = navbarPage(        title="导航条风格",        tabPanel("One", icon=icon("home")),        tabPanel("Two"),        navbarMenu(            title = "Three",            tabPanel("Four"),            tabPanel("Five")        ),        navlistPanel(            title = "导航面板风格", widths = c(3, 9),            tabPanel("One", icon=icon("home")),            tabPanel("Two"),            navbarMenu(                title = "Three",                tabPanel("Four"),                tabPanel("Five")            )        ),        h4("标签页风格"),        tabsetPanel(            type = "pills",            tabPanel("One", icon=icon("home")),            tabPanel("Two"),            navbarMenu(                title = "Three",                tabPanel("Four"),                tabPanel("Five")            )        ),        tags$head(            tags$style(".tab-content .tab-content {border: 1px solid gray; min-height:200px;}")        )    ),    server = function(session, input, output) {    })

运行后得到的页面如下:


2.2.3 navbarMenu函数

用于产生下拉式导航标签,上面的例子已经看到它的使用方法:

  navbarMenu(title, ..., icon = NULL)


2.3 行列设置

2.3.1 fixedRow和fluidRow

两者的代码是完全一样的,都是产生bootstrap的div.row类容器:

  fixedRow <- function(...) {      div(class = "row", ...)  }  fluidRow <- function(...) {      div(class = "row", ...)  }

2.3.2 column

产生bootstrap的div.col-sm-n类容器:

  column(width, ..., offset = 0)

  • width:设置col-sm-n中的n值,参数没有默认值,必需设置,取值为1-12间的整数,了解bootstrap媒体查询的应该知道。
  • offset:额外设置col-sm-offset-n中的n值,参数表示该列和前一列的间距,数值的单位意义和width一样,默认值为0。


2.4 特殊块

这些块的特殊之处只是预设了容器和样式,如果觉得内容太多你可以完全把它们忽略。

2.4.1 absolutePanel和fixedPanel

  absolutePanel(..., top = NULL, left = NULL, right = NULL, bottom = NULL,                width = NULL, height = NULL, draggable = FALSE, fixed = FALSE,                cursor = c("auto", "move", "default", "inherit"))  fixedPanel(..., top = NULL, left = NULL, right = NULL, bottom = NULL,             width = NULL, height = NULL, draggable = FALSE,             cursor = c("move", "default", "inherit"))

获取position属性分别为absolute和fixed的div容器,位置参数设置初始位置,draggable参数设置是否可以用鼠标拖动。

shinyApp(    ui = fixedPage(        fixedPanel(            top = 50, right=50, width=200, draggable = TRUE, style="padding: 20px; border: 1px solid red;",            "可以移动的框框1"        ),        absolutePanel(            top = 150, right=150, width=200, draggable = TRUE, style="padding: 20px; border: 1px solid red;",            "可以移动的框框2"        )    ),    server = function(session, input, output) {    })

2.4.2 conditionalPanel

条件显示区块。含condition条件参数,通过JS判断该条件以聚定显示或隐藏该区块。用户不用关心JS代码的编写。由于涉及到数据交互,我把它放到以后的文章介绍。

2.4.3 inputPanel

inputPanel <- function(...) {  div(class = "shiny-input-panel",    flowLayout(...)  )}

外部容器为div.shiny-input-panel;内部容器为div.shiny-flow-layout;各项内容均使用独立div容器。

2.4.4 sidebarPanel

sidebarPanel <- function(..., width = 4) {    div(class=paste0("col-sm-", width),        tags$form(class="well",                  ...        )    )}

外部容器为div,只接收宽度width参数;内部容器为form,接收其他所有内容和参数。

2.4.5 mainPanel

  mainPanel <- function(..., width = 8) {      div(class=paste0("col-sm-", width),          ...      )  }

只产生一个div容器,事实上和column函数差不多。

2.4.6 wellPanel

  wellPanel <- function(...) {      div(class="well", ...)  }

产生一个预设样式的块,有点像代码块,但字体不特殊设置。


3 预设布局模板


3.1 两列模板:sidebarLayout

  sidebarLayout(sidebarPanel, mainPanel, position = c("left", "right"),                fluid = TRUE)

  • 产生外部容器为div.row
  • 只能设置两栏,左右排列
  • 虽然参数为sidebarPanel和mainPanel,实际上可以使用其他方法设置div块
  • fluid参数已废


3.2 垂直分割模板:splitLayout

  splitLayout(..., cellWidths = NULL, cellArgs = list())

  • 外部容器为div.shiny-split-layout
  • 每项内容使用独立div块,cellWidths用于设置每个子块的宽度,如不设置则按内容数量进行等分父容器


3.3 流式(自动折行)模板:flowLayout

  • 外部容器为div.shiny-flow-layout
  • 每项内容使用独立div块,宽度固定为220px
  • 按屏幕显示区宽度自动折行


3.4 垂直排列模板:verticalLayout

这种模式将出现在该函数的每项内容(参数)都单独放在一行,即垂直排列。

运行下面程序,调整浏览器窗口大小,你会发现不同布局模式的变化:

  shinyApp(      ui = fixedPage(          tags$style(              ".container div {border: 1px solid gray; min-height:30px;}",              "h4 {color:red; margin-top: 20px;}"          ),          h4("两栏模板"),          sidebarLayout(              sidebarPanel("side bar panel"),              mainPanel("main panel")          ),          h4("垂直分割模板"),          splitLayout("aaaa", "bbbb", "cccc", "dddd"),          h4("垂直排列模板"),          verticalLayout("aaaa", "bbbb", "cccc", "dddd"),          h4("流式(自动折行)模板"),          flowLayout("aaaa", "bbbb", "cccc", "dddd")      ),      server = function(session, input, output) {      }  )

     



Author: ZGUANG@LZU

Created: 2015-08-04 二 19:58

Emacs 24.5.1 (Org mode 8.2.10)

1 0
原创粉丝点击