R Shiny弹窗无法生成K线图的问题解决请求
解决Shiny仪表盘K线图弹窗报错及绘图位置问题
问题原因
- 图表显示位置错误:
quantmod::candleChart默认直接输出到RStudio的绘图设备,不会返回可被ShinyrenderPlot识别的对象,导致图表无法渲染到弹窗中。 - "subscript out of bounds"错误:使用
read.table处理单股票字符串时会生成异常维度的数据框,索引操作触发维度错误;同时弹窗内直接传递无效的绘图对象给renderPlot,导致渲染失败。
解决方案
- 用
strsplit替代read.table处理股票列表,生成稳定的字符向量。 - 将
candleChart调用直接放在renderPlot内部,利用Shiny的绘图上下文完成弹窗内的图表渲染。 - 修正动态UI和事件绑定的命名空间逻辑,避免索引错误。
修改后的完整代码
library(quantmod) library(shiny) # 获取并整理股票数据 getSymbols(c("AAPL", "MSFT", "META", "ORCL", "TSLA", "GOOG"), from = "2020/01/01", to = Sys.Date(), periodicity = "daily") process_stock <- function(symbol) { df <- as.data.frame(get(symbol)) df$DATE <- index(get(symbol)) rownames(df) <- NULL names(df) <- c('OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED','DATE') df$SYMBOL <- symbol df[, c('SYMBOL','DATE','OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED')] } df_all <- do.call(rbind, lapply(c("AAPL", "MSFT", "META", "ORCL", "TSLA", "GOOG"), process_stock)) # 日期-股票映射数据 df_rep_date <- data.frame( RunDate = as.Date(c("2020-01-06", "2021-01-04", "2022-01-04")), ListStocks = c('AAPL,GOOG,TSLA', 'ORCL', 'META,MSFT'), stringsAsFactors = FALSE ) v_lst_sel_dates <- df_rep_date$RunDate # 生成股票表格数据 func_1symb_tab <- function(p_symb){ df_tmp_hist_dat <- df_all[df_all$SYMBOL == p_symb,c("DATE" ,"OPEN","HIGH","LOW" ,"CLOSE","VOLUME" )] df_tmp_hist_dat[1:5,] } # UI模块 simpUI <- function(id) { tagList( selectInput(NS(id, 'RunDate'), "Run Date", v_lst_sel_dates), textOutput(NS(id,'date_output')), textOutput(NS(id,'lst_symb_output')), uiOutput(NS(id,"stock_cards")) ) } # Server模块 simpServer <- function(id) { moduleServer(id, function(input, output, session) { ns <- session$ns output$date_output <- renderText(input$RunDate) # 获取当前选中日期对应的股票列表 v_lst_symbol <- reactive({ strsplit(df_rep_date[df_rep_date$RunDate == input$RunDate,]$ListStocks, ",")[[1]] }) output$lst_symb_output <- renderText(paste(v_lst_symbol(), collapse = ", ")) # 动态生成股票卡片(名称+表格+按钮) output$stock_cards <- renderUI({ lapply(v_lst_symbol(), function(v_symb){ fluidRow( h4(v_symb), tableOutput(ns(paste0(v_symb, '_table'))), actionButton(ns(paste0(v_symb, '_cndl_chart')), "查看K线图") ) }) }) # 为每个股票生成表格输出 observeEvent(v_lst_symbol(), { lapply(v_lst_symbol(), function(v_symb){ output[[paste0(v_symb, '_table')]] <- renderTable(func_1symb_tab(v_symb)) }) }) # 为每个按钮绑定弹窗事件 observeEvent(v_lst_symbol(), { lapply(v_lst_symbol(), function(v_symb){ btn_id <- paste0(v_symb, '_cndl_chart') observeEvent(input[[btn_id]], { showModal(modalDialog( title = paste(v_symb, "K线图"), size = "l", renderPlot({ # 直接在renderPlot内生成K线图 df_tmp_hist_dat <- df_all[df_all$SYMBOL == v_symb,c("DATE" ,"OPEN","HIGH","LOW" ,"CLOSE","VOLUME" )] v_df_dly_dat_xts <- xts(df_tmp_hist_dat[, -1], order.by = df_tmp_hist_dat[, 1]) candleChart(v_df_dly_dat_xts, name = v_symb, type = "auto", up.col = "green", dn.col = "red", theme = "white", TA = "addVo();addSMA(n=1, col='black');addSMA(n=7, col='gold');addSMA(n=14, col='brown');addMACD();addBBands();addRSI();addOBV();") }) )) }) }) }) }) } # 主程序 ui <- fluidPage(fluidRow(simpUI("par1"))) server <- function(input, output, session) { simpServer("par1") } shinyApp(ui = ui, server = server)
关键修改点
- 新增
process_stock函数简化股票数据整理逻辑,减少重复代码。 - 用
strsplit处理股票列表,避免read.table带来的维度问题。 - 将
candleChart直接放在renderPlot内部执行,利用Shiny的绘图上下文将图表渲染到弹窗。 - 拆分动态UI和事件绑定逻辑,代码结构更清晰,避免命名空间冲突。
内容的提问来源于stack exchange,提问作者VSR
相关产品推荐
相关产品推荐

