Introduction

Propose of pcrPlateInput shiny control is to view runs as a PCR plate like any conventional real-time PCR software:

library(shinyMolBio)
library(tidyverse)
library(RDML)
# load RDML file
rdml <- RDML$new(system.file("/extdata/stepone_std.rdml", package = "RDML"))
# create Shiny control
pcrPlateInput(inputId = "firstLook", # Shiny input ID
              label = "Example", # optional plate label 
              plateDescription = rdml$AsTable(), # plate description (wells content)
              interactive = FALSE
) 
1 2 3 4 5 6 7 8 9 10 11 12
A NTC_RNase P NTC_RNase P NTC_RNase P pop1_RNase P pop1_RNase P pop1_RNase P pop2_RNase P pop2_RNase P
B pop2_RNase P STD_RNase P_10000.0 STD_RNase P_10000.0 STD_RNase P_10000.0 STD_RNase P_5000.0 STD_RNase P_5000.0 STD_RNase P_5000.0 STD_RNase P_2500.0
C STD_RNase P_2500.0 STD_RNase P_2500.0 STD_RNase P_1250.0 STD_RNase P_1250.0 STD_RNase P_1250.0 STD_RNase P_625.0 STD_RNase P_625.0 STD_RNase P_625.0
D
E
F
G
H

PCR plate is fully customizable and can be interactive - selectable wells.

PCR Plate Customization

There are several parameters that can be changed:

  • wells selection
  • plate visualization by CSS
  • wells labels and marks

Wells Selection

Some PCR plate wells can be marked as selected by selection = c(TO_SELECT) (TO_SELECT can be position or react.id)

pcrPlateInput(inputId = "testPlateSel", 
              label = "Selection",
              plateDescription = rdml$AsTable(),
              selection = 1:12, # optional preselected wells
              interactive = FALSE
) 
1 2 3 4 5 6 7 8 9 10 11 12
A NTC_RNase P NTC_RNase P NTC_RNase P pop1_RNase P pop1_RNase P pop1_RNase P pop2_RNase P pop2_RNase P
B pop2_RNase P STD_RNase P_10000.0 STD_RNase P_10000.0 STD_RNase P_10000.0 STD_RNase P_5000.0 STD_RNase P_5000.0 STD_RNase P_5000.0 STD_RNase P_2500.0
C STD_RNase P_2500.0 STD_RNase P_2500.0 STD_RNase P_1250.0 STD_RNase P_1250.0 STD_RNase P_1250.0 STD_RNase P_625.0 STD_RNase P_625.0 STD_RNase P_625.0
D
E
F
G
H

Wells Highlighting

Some PCR plate wells can be marked as highlighted by highlightning = c(TO_HIGHLIGHT) (TO_HIGHLIGHT can be position or react.id)

pcrPlateInput(inputId = "testPlateHl", 
              label = "Highlight",
              plateDescription = rdml$AsTable(),
              selection = 1:12, # optional preselected wells
              highlighting = 3:6, # optional highlighted wells
              interactive = FALSE
) 
1 2 3 4 5 6 7 8 9 10 11 12
A NTC_RNase P NTC_RNase P NTC_RNase P pop1_RNase P pop1_RNase P pop1_RNase P pop2_RNase P pop2_RNase P
B pop2_RNase P STD_RNase P_10000.0 STD_RNase P_10000.0 STD_RNase P_10000.0 STD_RNase P_5000.0 STD_RNase P_5000.0 STD_RNase P_5000.0 STD_RNase P_2500.0
C STD_RNase P_2500.0 STD_RNase P_2500.0 STD_RNase P_1250.0 STD_RNase P_1250.0 STD_RNase P_1250.0 STD_RNase P_625.0 STD_RNase P_625.0 STD_RNase P_625.0
D
E
F
G
H

CSS

All visualion of PCR plate can be modified by custom CSS. This CSS can be loaded as file or as text and can be global or plate instance specific.

Description of the default CSS file:

/* Global plate style */
  table.pcr-plate-tbl{
    width: 100%;
    border-collapse: separate;
    border-spacing: 1px;
  }
/* Global plate style */
  table.pcr-plate-tbl td, table.pcr-plate-tbl th{
    width: 4rem;
    height: 2rem;
    border: 2px solid #ccc;
    text-align: center;
  }
/* Plate well style */
  table.pcr-plate-tbl td{
    max-width: 4rem;
    word-wrap: break-word;
  }
/* Plate header */
  table.pcr-plate-tbl th{
    border-color: white;
    color: white;
  }
/* Plate header even column*/
  table.pcr-plate-tbl thead th:nth-child(even){
    background-color: #3CA9E8;
  }
/* Plate header odd column */
  table.pcr-plate-tbl thead th:nth-child(odd){
    background-color: #178ACC;
  }
/* Plate header odd row */
  th.odd-row{
    background-color: #3CA9E8;
  }
/* Plate header even row */
  th.even-row{
    background-color: #178ACC;
  }
/* Selected well */
  td.selected-well{
    border: 2px solid black !important;
  }
/* Toggle all sign */
  th.toggle-all {
    background: transparent !important;
    position: relative;
  }
/* Toggle all sign */
  th.toggle-all:after {
    content: "";
    position: absolute;
    bottom: 0;
    right: 0;
    width: 0;
    height: 0;
    display: block;
    border-left: 1em solid transparent;
    border-bottom: 1em solid transparent;
    border-bottom: 1em solid grey;
  }

So if you want to set your own style of plate you can do it by:

  • providing your CSS file cssFile = YOUR_FILE
  • setting CSS file to NULL and providing CSS as text cssFile = "", cssText = YOUR_TEXT
  • extend default file with your text cssText = YOUR_TEXT

Note that inside Shiny app CSS loads as singleton (once for all plates) but in Rmd file it loads for every plate but works globally too - you can prevent such loading by setting cssFile = ""

To make CSS rule only for current plate add #{{id}} before CSS rule. For example if you want set selection border to red and well color to light green

pcrPlateInput(inputId = "customCSS", # Shiny input ID
              label = "Custom CSS: red selection and light green plate background",
              plateDescription = rdml$AsTable(),
              selection = c("A01", "C08"), # optional preselected wells
              cssFile = "",
              cssText = "#{{id}} td.selected-well{border: 2px solid red !important;}
                        #{{id}} table.pcr-plate-tbl td{background-color: #ebffe0;}
              ",
              interactive = FALSE
) 
1 2 3 4 5 6 7 8 9 10 11 12
A NTC_RNase P NTC_RNase P NTC_RNase P pop1_RNase P pop1_RNase P pop1_RNase P pop2_RNase P pop2_RNase P
B pop2_RNase P STD_RNase P_10000.0 STD_RNase P_10000.0 STD_RNase P_10000.0 STD_RNase P_5000.0 STD_RNase P_5000.0 STD_RNase P_5000.0 STD_RNase P_2500.0
C STD_RNase P_2500.0 STD_RNase P_2500.0 STD_RNase P_1250.0 STD_RNase P_1250.0 STD_RNase P_1250.0 STD_RNase P_625.0 STD_RNase P_625.0 STD_RNase P_625.0
D
E
F
G
H

Wells Labels and Marks

Well label, on hover text and other marks can be setted by templates. To do this pcrPlateInput use whisker package - so you can provide column names of plate description as shortcuts inside templates {{COLUMN}} (or {{{COLUMN}}} - without escaping basic HTML characters).

  • wellLabelTemplate = "{{sample}}" - text inside well (label)
  • onHoverWellTextTemplate = "{{position}}\u000A{{sample}}\u000A{{target}}" - on hover text
  • wellClassTemplate = NULL - specific class for current well
  • wellStyleTemplate = NULL - specific CSS style for current well

For example we want to show sample name, Cq and Cq < 30 or Cq > 35 with colored circle as label; target name and dye as on hover text; mark sample type with border color by class; set different background color for each sample name. Also legend will be added by legend = YOUR_HTML_LEGEND.

Note: if your plate description contains several rows with the same position - only the first will be used! So for example to print all targets for one well combine them to the new column like this tbl %>% group_by(position) %>% mutate(targets = paste(target, collapse = ", ")).

library(shiny) # to use tags
## 
## Attaching package: 'shiny'
## The following object is masked from 'package:shinyMolBio':
## 
##     runExample
plateDescription <- rdml$AsTable(cq = round(data$cq)) %>%
  group_by(fdata.name) %>%
  mutate(sampleType = sample.type,  # whisker does not support dots!
         dyeId = target.dyeId,   # whisker does not support dots!
         mark = {
           if (cq < 30) "<span class='filled-circle1'></span>"
           else if (cq > 35) "<span class='filled-circle2'></span>"
           else ""
           })

uniqSamples <- unique(plateDescription$sample)
sampleColors <- topo.colors(length(uniqSamples), alpha = 0.3)
names(sampleColors) <- uniqSamples
plateDescription <- plateDescription %>% 
  group_by(sample) %>% 
  mutate(color = sampleColors[sample] %>%
           col2rgb() %>%
           .[,1] %>% c(0.2) %>% 
           paste(collapse = ","))

pcrPlateInput("customLabel", "Custom labels and marks",
              plateDescription,
              wellLabelTemplate = "{{{mark}}}{{sample}} Cq={{cq}}",
              onHoverWellTextTemplate = "{{target}}: {{dyeId}}",
              wellClassTemplate = "{{sampleType}}",
              wellStyleTemplate = "background-color:rgba({{color}});",
              cssFile = "",
              cssText = "#{{id}} td.selected-well{border: 2px solid red !important;}
               #{{id}} .ntc{border: 3px solid Plum;}
               #{{id}} .unkn{border: 3px solid Salmon;}
               #{{id}} .pos{border: 3px solid PaleGreen ;}
               #{{id}} .neg{border: 3px solid LightGrey ;}
               #{{id}} .std{border: 3px solid DeepSkyBlue ;}
               #{{id}} .filled-circle1 {padding: 2px 11px;
                  border-radius: 100%; background-color: Maroon;}
               #{{id}} .filled-circle2 {padding: 2px 11px;
                  border-radius: 100%; background-color: Orange;}",
              legend =
                tags$div(
                  tags$span(class = "filled-circle1"), "Cq < 30",
                  tags$br(),
                  tags$span(class = "filled-circle2"), "Cq > 35",
                  tags$br(),
                  tags$span(
                    tags$span(class = "ntc", "NTC"),
                    tags$span(class = "unkn", "Unknown"),
                    tags$span(class = "pos", "Positive"),
                    tags$span(class = "neg", "Negative"),
                    tags$span(class = "std", "Standard")
                  )
                ),
              interactive = FALSE
)
1 2 3 4 5 6 7 8 9 10 11 12
A NTC_RNase P Cq=40 NTC_RNase P Cq=40 NTC_RNase P Cq=40 pop1_RNase P Cq=29 pop1_RNase P Cq=29 pop1_RNase P Cq=29 pop2_RNase P Cq=28 pop2_RNase P Cq=28
B pop2_RNase P Cq=28 STD_RNase P_10000.0 Cq=27 STD_RNase P_10000.0 Cq=27 STD_RNase P_10000.0 Cq=27 STD_RNase P_5000.0 Cq=28 STD_RNase P_5000.0 Cq=28 STD_RNase P_5000.0 Cq=28 STD_RNase P_2500.0 Cq=29
C STD_RNase P_2500.0 Cq=29 STD_RNase P_2500.0 Cq=29 STD_RNase P_1250.0 Cq=30 STD_RNase P_1250.0 Cq=30 STD_RNase P_1250.0 Cq=30 STD_RNase P_625.0 Cq=31 STD_RNase P_625.0 Cq=31 STD_RNase P_625.0 Cq=31
D
E
F
G
H
Cq < 30
Cq > 35
NTC Unknown Positive Negative Standard

Interactive PCR Plate

Run shinyMolBio::runExample("pcrPlateInput") to see interactive PCR plate in action. This demo Shiny App allows you to open PCR plate via RDML package and view in two independent plates: one with default style and second with custom. You can click on wells and see which of them are selected after that. Available click targets are:

  • Click-Well - toggle selection of the well
  • Dbl-Click-Well - select only this well (deselect other)
  • Ctrl-Click-Well - select group of wells defined by wellGroupTemplate
  • Click-TopLeftCorner - toggle selection of the total plate
  • Click-ColumnHeader - toggle selection of the column
  • Dbl-Click-ColumnHeader - select only this column (deselect other)
  • Click-RowHeader - toggle selection of the row
  • Dbl-Click-RowHeader - select only this row (deselect other)
  • Hover - highlights well

Also this app shows action of updatePcrPlateInput function. Clicking on Select Random Well button selects well on the second plate.