我制作了一个闪亮的应用程序,其中有人上传文件,计算了一些比率,并且可以使用滑块来设置阈值的格式。我为此使用DT::formatStyle
,它工作得非常好。据我了解这个函数,它创建一个回调来处理条件格式。
然后,我想使用DT
中的按钮扩展名导出数据。我想在导出为pdf或打印时保留格式。事实证明这不起作用:数据导出时没有任何格式。我试图设置exportOptions(list(stripHtml = FALSE))
,但它仍然不起作用。
令我感到惊讶的是,即使我直接从Firefox打印(如文件/打印...;我只尝试使用Firefox,而应用程序只能在Firefox中运行),颜色会被删除,但字体重量保持不变。我怀疑我可能不得不调整CSS,但我不知道该怎么做。
我想有办法按原样制作pdf和/或印刷品,这与我在浏览器中看到的最接近。 以下是一个例子:
library(shiny)
library(DT)
library(dplyr)
data("starwars")
ui <- fluidPage(title = "Ratios",
sidebarLayout(
sidebarPanel(width = 2,
actionButton("button", "Go"), # Emulates data loading
sliderInput("seuil_j", "Threshold J",
min = 0, max = 80, value = 35, step = 0.5)),
mainPanel(
fluidRow(column(width = 12,
DT::dataTableOutput("ratios"))))
)
)
server <- function(input, output, session) {
donnees_ratios <- reactive({
req(input$button)
set.seed(14)
starwars %>%
select(1:10) %>% # DataTables is not happy with list columns
mutate(signe = sample(c(1, -1), replace = TRUE, size = nrow(.)),
ratio_j = signe * mass / height) %>%
select(name, mass, height, signe, ratio_j, everything())
})
output$ratios <- DT::renderDataTable({
donnees_ratios() %>%
creer_DT() %>%
formatter_DT(input)
})
}
creer_DT <- function(donnees) {
datatable(donnees,
rownames = FALSE,
class = 'cell-border stripe compact hover',
extensions = c("Buttons"),
options = list(
dom = 'Blfrtip',
buttons = list(
list(extend = "pdf",
exportOptions = list(stripHtml = FALSE,
columns = ':visible'),
orientation = 'landscape'),
list(extend = "print",
exportOptions = list(stripHtml = FALSE,
columns = ':visible')),
"excel", "csv", "colvis"),
language = list(
decimal = ",",
thousands = " " # small unbreakable space
)
)
)
}
formatter_DT <- function(table, input) {
table %>%
formatPercentage(columns = c("ratio_j"),
digits = 1L, dec.mark = ",", mark = " ") %>%
formatRound(columns = c("height", "mass"),
digits = 1L, dec.mark = ",", mark = " ") %>%
format_seuil("ratio_j", input$seuil_j)
}
format_seuil <- function(table, column, seuil) {
# Threshold for the aboslute value, and different coloring if higher or lower
formatStyle(table, column,
fontWeight = styleInterval(
c(-seuil / 100, seuil / 100), c("bold", "normal", "bold")),
color = styleInterval(
c(-seuil / 100, seuil / 100), c("red", "black", "orange")
))
}
shinyApp(ui, server)
我可以导出为pdf或打印,但显示被修改。我也可以使用rmarkdown
和knitr
生成一个pdf,但这将是工作的两倍,感觉我错过了使用按钮扩展的东西。
我希望这很清楚,谢谢你的帮助!
弗洛里安
答案 0 :(得分:1)
PDF
and print
buttons have very different behaviors.
print
button behaviorWhen you click the print
button, you use the user agent (in this use case, the browser) to render the HTML
document as a paged document (PDF). There's a W3C standard named CSS Paged Media that defines how CSS rules are applied for paged media.
Theses CSS rules are enclosed in CSS @media print
at-rule.
There's a comprehensive guide about CSS Paged Media here: print-css.rocks.
Dealing with CSS Paged Media is not straightforward:
wkhtmltopdf
, weasyprint
, XML Prince
...) are used to generate PDF with CSS Paged Media. Using one of these user agents is quite easy since pandoc 2.0
: they can replace a LaTeX
engine. HTML
file, browsers do not apply @media print
by default (they apply @media screen
at-rule). So, it can be hard to figure out @media print
rules. The only mean I know to track theses rules is to use the Chrome Developer Tools (open the menu, select More tools
and Rendering
. In the Rendering
panel, you can emulate a paged media selecting print
).Since you want to use a browser to generate a styled PDF
, I think CSS paged media rules is an impracticable way. Moreover, using a headless user agent with a dynamic HTML document as a Shiny App is extremely complex. So, my advise is to forget the print
button.
PDF
button behaviorDataTables
library relies on pdfmake
JavaScript library to generate a PDF file. You can apply custom styles passing a JavaScript function to the customize
option of the pdfHtml5
button. This function customizes the document object sent to the pdfmake
API.
In order to understand the structure of the JSON
document object passed by DataTables
to pdfmake
, you can output it to the browser console:
library(shiny)
library(DT)
library(dplyr)
data("starwars")
ui <- fluidPage(title = "Ratios",
sidebarLayout(
sidebarPanel(width = 2,
actionButton("button", "Go"), # Emulates data loading
sliderInput("seuil_j", "Threshold J",
min = 0, max = 80, value = 35, step = 0.5)),
mainPanel(
fluidRow(column(width = 12,
DT::dataTableOutput("ratios"))))
)
)
server <- function(input, output, session) {
donnees_ratios <- reactive({
req(input$button)
set.seed(14)
starwars %>%
select(1:10) %>% # DataTables is not happy with list columns
mutate(signe = sample(c(1, -1), replace = TRUE, size = nrow(.)),
ratio_j = signe * mass / height) %>%
select(name, mass, height, signe, ratio_j, everything())
})
output$ratios <- DT::renderDataTable({
donnees_ratios() %>%
creer_DT() %>%
formatter_DT(input)
})
}
creer_DT <- function(donnees) {
datatable(donnees,
rownames = FALSE,
class = 'cell-border stripe compact hover',
extensions = c("Buttons"),
options = list(
dom = 'Blfrtip',
buttons = list(
list(extend = "pdf",
exportOptions = list(stripHtml = FALSE,
columns = ':visible'),
orientation = 'landscape',
customize = JS("function(doc){console.dir(doc);}")),
list(extend = "print",
exportOptions = list(stripHtml = FALSE,
columns = ':visible')),
"excel", "csv", "colvis"),
language = list(
decimal = ",",
thousands = " " # small unbreakable space
)
)
)
}
formatter_DT <- function(table, input) {
table %>%
formatPercentage(columns = c("ratio_j"),
digits = 1L, dec.mark = ",", mark = " ") %>%
formatRound(columns = c("height", "mass"),
digits = 1L, dec.mark = ",", mark = " ") %>%
format_seuil("ratio_j", input$seuil_j)
}
format_seuil <- function(table, column, seuil) {
# Threshold for the aboslute value, and different coloring if higher or lower
formatStyle(table, column,
fontWeight = styleInterval(
c(-seuil / 100, seuil / 100), c("bold", "normal", "bold")),
color = styleInterval(
c(-seuil / 100, seuil / 100), c("red", "black", "orange")
))
}
shinyApp(ui, server)
You can modify a default style. Here's one example changing the font color of the tableHeader
style:
customize = JS("function(doc){doc.styles.tableHeader.color='yellow';}"))
For further customization, you have to write your own JavaScript function. Here's an example to format the fifth column with percent:
customize = JS("function(doc){doc.content[1].table.body.forEach(function(el,idx){if(idx>0){el[4].text=String((parseFloat(el[4].text)*100).toFixed(1))+'%'}})}"))