library(visNetwork)library(shiny)# 创建树形数据create_tree_data<-function(){nodes<- data.frame(id=1:10, label=paste0("品种", LETTERS[1:10]), title=paste0("品种", LETTERS[1:10],"<br>","点击查看详细信息"), group=sample(c("本地","引进","杂交"),10, replace=TRUE), value=1:10)edges<- data.frame(from=c(1,1,2,2,3,3,4,4,5), to=c(2,3,4,5,6,7,8,9,10), label=c("父本","母本","父本","母本","父本","母本","父本","母本","父本"))return(list(nodes=nodes, edges=edges, root=1))}ui<- fluidPage(titlePanel("品种谱系树形图 - 可自由拖拽"), sidebarLayout(sidebarPanel(width=3, selectInput("layout","布局方式:", choices=c("自由布局"="free","树形布局"="hierarchical","力导向布局"="force"), selected="free"), conditionalPanel(condition="input.layout == 'hierarchical'", selectInput("direction","树图方向:", choices=c("上下"="UD","左右"="LR"), selected="UD")), checkboxInput("physics","启用物理引擎", TRUE), checkboxInput("dragNodes","允许拖拽节点", TRUE), actionButton("reset","重置位置"), hr(), h5("操作提示:"), tags$ul(tags$li("鼠标拖动: 移动节点"), tags$li("鼠标滚轮: 缩放"), tags$li("双击空白处: 重置视图"), tags$li("Ctrl+拖拽: 框选多个节点"))), mainPanel(width=9, visNetworkOutput("tree_plot", height="700px"))), fluidRow(column(12, h4("节点详细信息"), uiOutput("variety_info"), verbatimTextOutput("tree_structure"))))server<- function(input, output, session){tree_data<- reactiveVal(create_tree_data())output$tree_plot<- renderVisNetwork({data<- tree_data()# 根据选择的布局方式配置vis_obj<- visNetwork(data$nodes, data$edges)%>% visOptions(highlightNearest=list(enabled=TRUE, degree=1, hover=TRUE), nodesIdSelection=TRUE, manipulation=list(enabled=TRUE, addNode=FALSE, addEdge=FALSE, editNode=FALSE, editEdge=FALSE, deleteNode=FALSE, deleteEdge=FALSE))%>% visInteraction(dragNodes=input$dragNodes, dragView=TRUE, zoomView=TRUE, navigationButtons=TRUE)%>% visPhysics(enabled=input$physics, stabilization=TRUE)%>% visEvents(select="function(nodes) { Shiny.onInputChange('selected_node', nodes.nodes[0]); }", dragEnd="function(params) { if(params.nodes.length > 0) { Shiny.onInputChange('node_moved', {node: params.nodes[0], timestamp: new Date().getTime()}); } }")# 根据布局选择应用不同的布局配置if(input$layout=="hierarchical"){vis_obj<- vis_obj %>% visHierarchicalLayout(direction=input$direction, sortMethod="directed", nodeSpacing=120, levelSeparation=200, shakeTowards="roots")}elseif(input$layout=="force"){vis_obj<- vis_obj %>% visLayout(randomSeed=123)%>% visIgraphLayout(layout="layout_with_kk",type="full")}else{# 自由布局vis_obj<- vis_obj %>% visLayout(improvedLayout=FALSE, randomSeed=NULL)%>% visPhysics(solver="repulsion", repulsion=list(nodeDistance=200, centralGravity=0.1, springLength=200, springConstant=0.05, damping=0.09))}vis_obj})# 重置视图observeEvent(input$reset,{# 重新生成数据,这样节点位置会重置tree_data(create_tree_data())})# 节点移动事件observeEvent(input$node_moved,{req(input$node_moved)message(paste("节点移动:", input$node_moved$node))})output$tree_structure<- renderPrint({data<- tree_data()cat("谱系树结构:\n")cat("===========\n")cat("根节点: 品种A (ID: 1)\n")cat("节点总数:", nrow(data$nodes),"\n")cat("边总数:", nrow(data$edges),"\n\n")# 构建树形文本edges_df<- data$edges# 按父节点分组edges_by_parent<- split(edges_df$to, edges_df$from)# 递归打印树结构print_tree<- function(node_id, prefix=""){node_label<- data$nodes$label[data$nodes$id==node_id]children<- edges_by_parent[[as.character(node_id)]]if(is.null(children)){cat(prefix,"└─ ", node_label," (ID: ", node_id,")\n", sep="")}else{if(node_id==1){cat(prefix, node_label," (ID: ", node_id,", 根节点)\n", sep="")}else{cat(prefix,"├─ ", node_label," (ID: ", node_id,")\n", sep="")}for(iinseq_along(children)){child<- children[i]is_last<- i==length(children)new_prefix<- ifelse(node_id==1,"", paste0(prefix,"│ "))child_prefix<- ifelse(is_last,"└─ ","├─ ")print_tree(child, paste0(new_prefix, child_prefix))}}}print_tree(1)})output$variety_info<- renderUI({req(input$selected_node)node_id<- as.numeric(input$selected_node)node_data<- tree_data()$nodes[tree_data()$nodes$id==node_id,]# 获取父节点和子节点信息edges<- tree_data()$edges# 父节点parents<- edges$from[edges$to==node_id]parent_labels<-if(length(parents)>0){sapply(parents, function(p)tree_data()$nodes$label[tree_data()$nodes$id==p])}else{"无"}# 子节点children<- edges$to[edges$from==node_id]child_labels<-if(length(children)>0){sapply(children, function(c)tree_data()$nodes$label[tree_data()$nodes$id==c])}else{"无"}tagList(h3(node_data$label), hr(), h4("基本信息:"), tags$table(class="table table-bordered", tags$tr(tags$td(strong("ID:")), tags$td(node_id)), tags$tr(tags$td(strong("类型:")), tags$td(node_data$group)), tags$tr(tags$td(strong("父本:")), tags$td(paste(parent_labels, collapse=", "))), tags$tr(tags$td(strong("子代:")), tags$td(paste(child_labels, collapse=", ")))), h4("品种特性:"), tags$ul(tags$li(paste("类别:", node_data$group)), tags$li("来源: 中国农科院"), tags$li("培育年份: 2018"), tags$li("平均产量: 4500 kg/ha"), tags$li("抗病性: 高抗白叶枯病")))})}shinyApp(ui=ui, server=server)谱系图展示品种信息
张小明
前端开发工程师
拒绝测试左移:当早期介入反而模糊质量责任的真相
在敏捷开发和DevOps浪潮席卷全球的今天,“测试左移”(shift-left testing)已成为软件测试从业者的热门词汇。它倡导在软件开发生命周期(SDLC)的早期阶段——如需求分析和设计环节——就引入测试活动,目的…
5个理由让你立即安装Markdown Viewer浏览器插件
5个理由让你立即安装Markdown Viewer浏览器插件 【免费下载链接】markdown-viewer Markdown Viewer / Browser Extension 项目地址: https://gitcode.com/gh_mirrors/ma/markdown-viewer Markdown Viewer是一款功能强大的浏览器扩展程序,能够直接在浏览器中优…
3分钟掌握Mermaid图表高清矢量图导出的终极方法
3分钟掌握Mermaid图表高清矢量图导出的终极方法 【免费下载链接】typora_plugin Typora plugin. feature enhancement tool | Typora 插件,功能增强工具 项目地址: https://gitcode.com/gh_mirrors/ty/typora_plugin 还在为技术文档中的图表导出质量发愁吗&a…
HTML5 Audio标签优雅播放IndexTTS 2.0生成结果
HTML5 Audio标签优雅播放IndexTTS 2.0生成结果 在短视频与虚拟人内容爆发式增长的今天,创作者们面临一个共同挑战:如何快速、精准地为画面配上富有情感且音色统一的语音?传统的配音方式依赖真人录制或通用TTS引擎,往往成本高、灵活…
Windows系统DLL文件修复终极指南:彻底解决应用程序依赖问题
Windows系统DLL文件修复终极指南:彻底解决应用程序依赖问题 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist DLL文件修复是Windows系统维护中的关键环…
Markdown浏览器插件快速配置与使用指南
Markdown浏览器插件快速配置与使用指南 【免费下载链接】markdown-viewer Markdown Viewer / Browser Extension 项目地址: https://gitcode.com/gh_mirrors/ma/markdown-viewer 还在为浏览Markdown文档而烦恼吗?这款功能强大的浏览器插件能让你直接在浏览器…