tbtools-cli 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. tbtools_cli/__init__.py +18 -0
  2. tbtools_cli/auto_commands.py +1149 -0
  3. tbtools_cli/cli.py +528 -0
  4. tbtools_cli/cli_load.py +352 -0
  5. tbtools_cli/cli_rpc.py +303 -0
  6. tbtools_cli/cli_tools_registry.py +93 -0
  7. tbtools_cli/cli_top.py +1160 -0
  8. tbtools_cli/command_metadata.json +4606 -0
  9. tbtools_cli/command_spec.py +382 -0
  10. tbtools_cli/config.example.toml +19 -0
  11. tbtools_cli/config.py +43 -0
  12. tbtools_cli/core.py +508 -0
  13. tbtools_cli/errors.py +38 -0
  14. tbtools_cli/presets.py +100 -0
  15. tbtools_cli/scenarios.py +92 -0
  16. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/AdmixtureCli.java +91 -0
  17. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/AmazingMetaCli.java +78 -0
  18. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/AssemblyRecommandCli.java +47 -0
  19. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BamIndexCli.java +31 -0
  20. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BamSortCli.java +43 -0
  21. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BamStateCli.java +49 -0
  22. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BarPlotterCli.java +18 -0
  23. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BarplotCli.java +168 -0
  24. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BatchVizMotifsCli.java +47 -0
  25. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BlastXmlConvertCli.java +45 -0
  26. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BlatExecutorCli.java +66 -0
  27. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CalcRepeatCli.java +62 -0
  28. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CalculateSimilarityCli.java +34 -0
  29. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CddMotifCli.java +46 -0
  30. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CircleGeneViewerCli.java +81 -0
  31. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CircosCli.java +76 -0
  32. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/ColorSchemeCli.java +29 -0
  33. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CtgGroupCli.java +29 -0
  34. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CubeHeatmapCli.java +70 -0
  35. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DeHistCli.java +46 -0
  36. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DegramdomCli.java +33 -0
  37. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DiffExpCli.java +44 -0
  38. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DistanceCli.java +53 -0
  39. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DualSynCli.java +147 -0
  40. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/EggnogCli.java +39 -0
  41. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/ExprCorrCli.java +27 -0
  42. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FastaMergerCli.java +36 -0
  43. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FastaTableConvertCli.java +38 -0
  44. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FileCleanerCli.java +31 -0
  45. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FileSplitCli.java +15 -0
  46. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FindBlockDualCli.java +71 -0
  47. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FindBlockMultipleCli.java +67 -0
  48. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FindPathCli.java +15 -0
  49. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GSEAWrapperCli.java +33 -0
  50. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GenBank2FastaCli.java +32 -0
  51. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GeneDensityCli.java +38 -0
  52. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GeneLocGffCli.java +140 -0
  53. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GeneStructureCli.java +90 -0
  54. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GenericCli.java +177 -0
  55. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GetENALinksCli.java +36 -0
  56. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GetSubNewickTreeCli.java +50 -0
  57. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GoEnrichCli.java +42 -0
  58. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GoLevelCli.java +59 -0
  59. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GroupedBarCli.java +95 -0
  60. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GsaDiagCli.java +45 -0
  61. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GxfFilterCli.java +39 -0
  62. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GxfSortCli.java +23 -0
  63. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/HclustCli.java +43 -0
  64. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/HeatmapCli.java +89 -0
  65. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/HmmerSuiteCli.java +47 -0
  66. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/JgblocksCli.java +65 -0
  67. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/KeggEnrichCli.java +31 -0
  68. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/LayoutHeatmapCli.java +90 -0
  69. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MCScanXCli.java +80 -0
  70. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MCScanXFastCli.java +41 -0
  71. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MSACli.java +46 -0
  72. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MakeMotifCli.java +39 -0
  73. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MarkerDesignCli.java +62 -0
  74. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MarkerToolsCli.java +57 -0
  75. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Mast2TabCli.java +27 -0
  76. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MastCli.java +54 -0
  77. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MastRunCli.java +39 -0
  78. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Meme2TabCli.java +26 -0
  79. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MemeCli.java +74 -0
  80. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MemeRunCli.java +40 -0
  81. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MgGxfCli.java +35 -0
  82. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MicroSynCli.java +118 -0
  83. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MirIdentifyCli.java +55 -0
  84. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MotifCli.java +75 -0
  85. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MotifPatternCli.java +67 -0
  86. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MotifShiftCli.java +30 -0
  87. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MountainPlotCli.java +44 -0
  88. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MultiSuperHeatCli.java +64 -0
  89. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/NeedlemanWunschCli.java +76 -0
  90. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PafGC.java +15 -0
  91. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PafVizCli.java +67 -0
  92. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PeakDistCli.java +58 -0
  93. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Pep2CodonCli.java +26 -0
  94. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PfamMotifCli.java +43 -0
  95. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PhyloTreeCli.java +92 -0
  96. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PileUpCli.java +55 -0
  97. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PlantCAREResultClassifyCli.java +36 -0
  98. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PubmedSearchCli.java +25 -0
  99. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QpcrCli.java +69 -0
  100. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QpcrDdctCli.java +28 -0
  101. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QpcrProcCli.java +28 -0
  102. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QuickGenomeDotCli.java +66 -0
  103. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QuickProteinAnnoCli.java +34 -0
  104. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QuickRunIQtreeCli.java +69 -0
  105. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QuickTrimALCli.java +67 -0
  106. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/RNAplotCli.java +129 -0
  107. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/RegionBedToGFF3Cli.java +43 -0
  108. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/RegionDepthCli.java +29 -0
  109. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SamBamCovCli.java +33 -0
  110. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SeqConverterCli.java +17 -0
  111. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SeqLenTrackCli.java +41 -0
  112. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SeveralSpeciesCli.java +132 -0
  113. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SimpleEnricherCli.java +44 -0
  114. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SimpleHmmscanCli.java +32 -0
  115. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SixFrameTranlaterCli.java +35 -0
  116. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/StructAnnoCompareCli.java +66 -0
  117. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SubmitSMARTCli.java +30 -0
  118. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SuperCircosCli.java +330 -0
  119. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TableColManipCli.java +56 -0
  120. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TableCollapseCli.java +33 -0
  121. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TargetScoreCli.java +47 -0
  122. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TauCalcCli.java +28 -0
  123. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TaxonomyBatchCli.java +72 -0
  124. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TreeCli.java +75 -0
  125. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TreeRootingCli.java +30 -0
  126. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TrimMSACli.java +33 -0
  127. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/UnrootedTreeCli.java +44 -0
  128. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/UpSetCli.java +90 -0
  129. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Venn5Cli.java +60 -0
  130. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Venn6Cli.java +65 -0
  131. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/ViolinCli.java +66 -0
  132. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/VisualizeCli.java +57 -0
  133. tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/VizGFACli.java +39 -0
  134. tbtools_cli-1.2.0.dist-info/METADATA +504 -0
  135. tbtools_cli-1.2.0.dist-info/RECORD +139 -0
  136. tbtools_cli-1.2.0.dist-info/WHEEL +5 -0
  137. tbtools_cli-1.2.0.dist-info/entry_points.txt +2 -0
  138. tbtools_cli-1.2.0.dist-info/licenses/LICENSE +21 -0
  139. tbtools_cli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,92 @@
1
+ """tbtools new 交互式向导 — 场景映射表
2
+ 把用户「我想做什么」(中文场景)映射到具体命令 + 参数收集提示。
3
+ """
4
+
5
+ # 场景表: 键 = 中文场景名, 值 = [分组, 命令名, 一句话说明, 参数收集器描述]
6
+ # 参数收集器: 每个元素 = (参数位置标签, 默认值或None, 可选标志help)
7
+ def _scen(name, group, cmd, desc, args):
8
+ return {
9
+ "scenario": name, "group": group, "cmd": cmd,
10
+ "desc": desc, "args": args, "usage": _usage(group, cmd),
11
+ }
12
+
13
+ def _usage(group, cmd):
14
+ import tbtools_cli.auto_commands as _ac
15
+ impl = getattr(_ac, f'_{cmd}_impl', None)
16
+ if impl and (impl.__doc__ or ''):
17
+ d = impl.__doc__.strip()
18
+ if ':' in d:
19
+ return d.split(':', 1)[1].strip()
20
+ # 从已注册命令找
21
+ from .cli_load import _groups # 批次B: _groups 移至 cli_load
22
+ g = _groups.get(group)
23
+ if g and cmd in g.commands:
24
+ h = g.commands[cmd].help or ''
25
+ return h.split('\n')[0][:80]
26
+ return f"{group} {cmd}"
27
+
28
+ # 常用场景(覆盖 80% 使用)——按用户「想做什么」组织
29
+ SCENARIOS = {
30
+ # ---- 序列 ----
31
+ "多序列比对可视化(MSA)": _scen("多序列比对可视化(MSA)", "seq", "msa",
32
+ "把比对好的多序列(如蛋白/CDS)画成可视化图", [("输入比对文件 .fa", None)]),
33
+ "蛋白序列 Logo(conserved motif)": _scen("蛋白序列 Logo", "seq", "logo",
34
+ "把多序列比对的保守位点画成序列 Logo", [("输入比对文件 .fa", None), ("输出 Logo 图片", "logo.svg")]),
35
+ "Motif 分布图(MEME 结果)": _scen("Motif 分布图", "seq", "motif",
36
+ "把 MEME 发现的 motif 沿基因画分布图", [("MEME xml", None), ("输出图", "motif.svg")]),
37
+ "基因结构图(外显子/UTR)": _scen("基因结构图", "seq", "structure",
38
+ "从 GFF 画基因外显子/内含子/U TR 结构图", [("GFF 文件", None), ("输出图", "structure.svg")]),
39
+ "序列统计(长度/GC/组成)": _scen("序列统计", "tool", "stat-fasta",
40
+ "统计 FASTA 每条序列长度/GC 含量/组成", [("FASTA 文件", None), ("输出表", "stat.xls")]),
41
+
42
+ # ---- 表达 ----
43
+ "火山图(差异表达)": _scen("火山图", "expr", "volcano",
44
+ "差异表达分析结果画火山图(log2FC vs -log10P)", [("DEG 表", None), ("输出图", "volcano.svg")]),
45
+ "热图(表达矩阵)": _scen("热图", "expr", "heatmap",
46
+ "基因×样本表达矩阵画热图(支持聚类)", [("表达矩阵 .tsv", None), ("输出图", "heatmap.svg")]),
47
+ "PCA 聚类(样本分群)": _scen("PCA 聚类", "expr", "pca",
48
+ "表达矩阵样本 PCA 降维分群", [("表达矩阵 .tsv", None), ("输出图", "pca.svg")]),
49
+ "层次聚类树(距离矩阵)": _scen("层次聚类树", "expr", "hclust",
50
+ "三列距离矩阵(GeneA GeneB dist)画聚类树", [("距离文件 GeneA GeneB dist", None), ("输出图", "hclust.svg")]),
51
+ "差异表达双直方图": _scen("差异表达双直方图", "expr", "dehist",
52
+ "上下调基因数画左右直方图", [("任意 ID\\t值 文件", None), ("输出图", "dehist.svg")]),
53
+
54
+ # ---- 树 ----
55
+ "系统发育树(ML 一步法)": _scen("系统发育树", "tree", "onesteptree",
56
+ "蛋白序列快速建 ML 树(muscle→trimal→IQ-TREE)", [("蛋白多序列 .fa", None), ("输出前缀", "tree")]),
57
+ "可视化已有树(Newick)": _scen("可视化树", "tree", "tree",
58
+ "画 Newick 树的图", [("Newick 树文件", None), ("输出图", "tree.svg")]),
59
+ "无根树可视化": _scen("无根树", "tree", "unrooted",
60
+ "把树画成无根(放射状)风格", [("Newick 树文件", None), ("输出图", "unrooted.svg")]),
61
+
62
+ # ---- 共线性/基因组 ----
63
+ "共线性 Dot-plot": _scen("共线性 Dot-plot", "syn", "dotplot",
64
+ "两基因组共线性点图", [("简化 GFF 1", None), ("简化 GFF 2", None), ("blast 结果", None), ("输出图", "dotplot.svg")]),
65
+ "MCScanX 共线性分析": _scen("MCScanX 共线性", "syn", "mcscanx",
66
+ "跑 MCScanX 找共线性区块", [("简化 GFF", None), ("blast 结果", None)]),
67
+ "环形共线性图(Circos)": _scen("环形共线性图", "syn", "circos",
68
+ "多染色体环形共线性图", [("chromLen", None), ("link 文件", None), ("genePos", None), ("输出图", "circos.svg")]),
69
+ "染色体基因定位图": _scen("染色体定位图", "tool", "genelocation",
70
+ "基因在染色体上的位置分布图", [("chromLen 文件", None), ("FeaturePos", None), ("输出图", "geneloc.svg")]),
71
+ "基因密度分布(染色体)": _scen("基因密度", "tool", "genedensity",
72
+ "染色体基因密度分布统计", [("GFF3", None), ("输出表", "density.tsv")]),
73
+
74
+ # ---- 集合 ----
75
+ "Venn 图(2-6 集合交集)": _scen("Venn 图", "sets", "venn",
76
+ "多个基因集合交集 Venn 图(2/3/4/5/6 集)", [("集合数目 2-6", "3"), ("集合文件前缀", None), ("输出图", "venn.svg")]),
77
+ "UpSet 图(交集可视化)": _scen("UpSet 图", "sets", "upset",
78
+ "多个集合交集用 UpSet 图展示", [("集合文件", None), ("输出图", "upset.svg")]),
79
+
80
+ # ---- 表操作 ----
81
+ "表格行列转换/合并/拆分": _scen("表格操作", "table", "tableMerge",
82
+ "表格合并/转置/筛选等(见 list tools)", []),
83
+ }
84
+
85
+ _categories = {
86
+ "序列/结构": ["多序列比对可视化(MSA)", "蛋白序列 Logo(conserved motif)", "Motif 分布图(MEME 结果)", "基因结构图(外显子/UTR)", "序列统计(长度/GC/组成)"],
87
+ "表达/统计": ["火山图(差异表达)", "热图(表达矩阵)", "PCA 聚类(样本分群)", "层次聚类树(距离矩阵)", "差异表达双直方图"],
88
+ "系统发育": ["系统发育树(ML 一步法)", "可视化已有树(Newick)", "无根树可视化"],
89
+ "共线性/基因组": ["共线性 Dot-plot", "MCScanX 共线性分析", "环形共线性图(Circos)", "染色体基因定位图", "基因密度分布(染色体)"],
90
+ "集合分析": ["Venn 图(2-6 集合交集)", "UpSet 图(交集可视化)"],
91
+ "表格处理": ["表格行列转换/合并/拆分"],
92
+ }
@@ -0,0 +1,91 @@
1
+ import jigplot.engine.JIGBasePanel;
2
+ import jigplot.engine.JIGSubPanel;
3
+
4
+ import java.io.File;
5
+ import java.lang.reflect.Method;
6
+
7
+ /**
8
+ * tbplot admixtureViz — ADMIXTURE Q 矩阵可视化 CLI(GUI 面板逆向接口,09/20)
9
+ *
10
+ * 用法: AdmixtureCli <q1.txt> <q2.txt> [<q3.txt>...] <out.svg> [--id <samples.txt>]
11
+ * [--group <group.txt>] [--sort Qraito|Lexical|None] [--width N] [--height N] [--interval N]
12
+ *
13
+ * 接口来源:反编译 AdmixtureVizGUIPanel(GUI 真实调用链):
14
+ * AdmixtureQmatViz aqv = new AdmixtureQmatViz();
15
+ * aqv.setSampleIDFile/setInGroupFile/setSortMode/setWidth/setHeight/setPanelInterval;
16
+ * JIGUtils.quickShow(aqv.process(fileArr));
17
+ * 规避:直调 process(File[])(返回 JIGSubPanel)→ JIGBasePanel.save2*。
18
+ *
19
+ * 输入:ADMIIX 输出的 Q 矩阵(每行=样本,每列=群体占比)+ 可选样本 ID 文件
20
+ * (旧格式每行 sampleId+数值;新 Q 文件无 ID 列时用 --id)与群体分组文件。
21
+ */
22
+ public class AdmixtureCli {
23
+ public static void main(String[] args) throws Exception {
24
+ java.util.ArrayList<String> pos = new java.util.ArrayList<String>();
25
+ String idFile = null, groupFile = null, sort = "Qraito";
26
+ int width = 800, height = 600, interval = 0;
27
+ for (int i = 0; i < args.length; i++) {
28
+ if (args[i].equals("--id") && i+1 < args.length) idFile = args[++i];
29
+ else if (args[i].equals("--group") && i+1 < args.length) groupFile = args[++i];
30
+ else if (args[i].equals("--sort") && i+1 < args.length) sort = args[++i];
31
+ else if (args[i].equals("--width") && i+1 < args.length) width = Integer.parseInt(args[++i]);
32
+ else if (args[i].equals("--height") && i+1 < args.length) height = Integer.parseInt(args[++i]);
33
+ else if (args[i].equals("--interval") && i+1 < args.length) interval = Integer.parseInt(args[++i]);
34
+ else pos.add(args[i]);
35
+ }
36
+ if (pos.size() < 2) {
37
+ System.err.println("用法: AdmixtureCli <q1.txt> <q2.txt> [<q3.txt>...] <out.svg> [--id samples.txt] [--group group.txt] [--sort Qraito|Lexical|None]");
38
+ System.exit(1);
39
+ }
40
+ String outPath = pos.remove(pos.size() - 1);
41
+ // 支持两种输入:直接 Q 文件 / *.lst 清单(每行一个 Q 文件路径,GUI 传统格式)
42
+ java.util.ArrayList<File> qList = new java.util.ArrayList<File>();
43
+ for (String fp : pos) {
44
+ File f = new File(fp);
45
+ if (f.getName().toLowerCase().endsWith(".lst")) {
46
+ java.io.BufferedReader br = new java.io.BufferedReader(new java.io.FileReader(f));
47
+ String line;
48
+ while ((line = br.readLine()) != null) {
49
+ String t = line.trim();
50
+ if (!t.isEmpty()) qList.add(new File(t));
51
+ }
52
+ br.close();
53
+ } else {
54
+ qList.add(f);
55
+ }
56
+ }
57
+ File[] qFiles = qList.toArray(new File[0]);
58
+
59
+ Object aqv = Class.forName("biocjava.bioDoer.JIGplotToolkit.PopulationGenetics.AdmixtureQmatViz")
60
+ .getDeclaredConstructor().newInstance();
61
+ Class<?> c = aqv.getClass();
62
+ if (idFile != null) c.getMethod("setSampleIDFile", File.class).invoke(aqv, new File(idFile));
63
+ if (groupFile != null) c.getMethod("setInGroupFile", File.class).invoke(aqv, new File(groupFile));
64
+ Class<?> sm = Class.forName("biocjava.bioDoer.JIGplotToolkit.PopulationGenetics.AdmixtureQmatViz$SortMode");
65
+ c.getMethod("setSortMode", sm).invoke(aqv, Enum.valueOf((Class)sm, sort));
66
+ c.getMethod("setWidth", int.class).invoke(aqv, width);
67
+ c.getMethod("setHeight", int.class).invoke(aqv, height);
68
+ c.getMethod("setPanelInterval", int.class).invoke(aqv, interval);
69
+ Object result = c.getMethod("process", File[].class).invoke(aqv, (Object) qFiles);
70
+ JIGSubPanel[] panels;
71
+ if (result instanceof JIGSubPanel[]) {
72
+ panels = (JIGSubPanel[]) result;
73
+ } else if (result instanceof JIGSubPanel) {
74
+ panels = new JIGSubPanel[]{ (JIGSubPanel) result };
75
+ } else {
76
+ System.err.println("❌ process 未返回 JIGSubPanel/JIGSubPanel[]");
77
+ System.exit(1);
78
+ panels = new JIGSubPanel[0];
79
+ }
80
+ JIGBasePanel base = new JIGBasePanel(width, height);
81
+ for (JIGSubPanel p : panels) base.addSubPanel(p);
82
+ System.err.println("[tbplot] " + panels.length + " 个 Q 面板");
83
+ File outf = new File(outPath);
84
+ String low = outPath.toLowerCase();
85
+ if (low.endsWith(".png")) base.save2PNG(outf);
86
+ else if (low.endsWith(".pdf")) base.save2PDF(outf);
87
+ else base.save2SVG(outf);
88
+ System.err.println("[tbplot] 已保存: " + outPath);
89
+ System.exit(0);
90
+ }
91
+ }
@@ -0,0 +1,78 @@
1
+ import biocjava.bioDoer.MEME.DrawMotifPattern.DrawAmazingMetaPlot;
2
+ import jigplot.engine.JIGBasePanel;
3
+
4
+ import java.awt.Component;
5
+ import java.awt.Window;
6
+ import java.io.File;
7
+
8
+ /**
9
+ * tbcli amazmeta — Amazing Meta Plot CLI(08/31 第七十八波,引擎 120)
10
+ *
11
+ * 用法: AmazingMetaCli <meme.xml> <newick.treefile> <out.svg|png|pdf> [seqLen.txt] [geneRename.txt]
12
+ * meme.xml: MEME 结果(必选)
13
+ * newick.treefile: 进化树(必选,控制基因顺序)
14
+ * seqLen.txt: 可选序列长度文件(gene\tlen)
15
+ * geneRename.txt: 可选基因重命名
16
+ *
17
+ * 引擎: DrawAmazingMetaPlot.plot() —— 组合 进化树+Motif模式+基因结构+蛋白域 到一张图
18
+ * (论文级组合图;plot() 内部 JFrame 显示 → Window 反射取 JIGBasePanel → save2SVG/PNG/PDF)
19
+ */
20
+ public class AmazingMetaCli {
21
+ public static void main(String[] args) throws Exception {
22
+ if (args.length < 3) {
23
+ System.err.println("用法: AmazingMetaCli <meme.xml> <newick.treefile> <out.svg|png|pdf> [seqLen.txt] [geneRename.txt]");
24
+ System.exit(1);
25
+ }
26
+ File memeFile = new File(args[0]);
27
+ File treeFile = new File(args[1]);
28
+ String out = args[2];
29
+ File seqLenFile = args.length > 3 ? new File(args[3]) : null;
30
+ File renameFile = args.length > 4 ? new File(args[4]) : null;
31
+
32
+ String newick = new String(java.nio.file.Files.readAllBytes(treeFile.toPath()), "UTF-8").trim();
33
+
34
+ DrawAmazingMetaPlot damp = new DrawAmazingMetaPlot();
35
+ damp.setMemeXmlFile(memeFile);
36
+ damp.setNewickTreeString(newick);
37
+ damp.setGradient(true);
38
+ damp.setTotalGraphWidth(2000);
39
+ damp.setTotalGraphHeight(2800);
40
+ damp.setTreeLayOut(biocjava.bioDoer.JIGplotToolkit.newickParser.PhyloTreeMan.TreeBranchTranForm.Cladogram);
41
+ if (seqLenFile != null && seqLenFile.exists()) damp.setInCoverMEMESeqLenFile(seqLenFile);
42
+ if (renameFile != null && renameFile.exists()) damp.setGeneRenameFile(renameFile);
43
+
44
+ damp.plot();
45
+
46
+ // plot() 内部 JFrame 显示 → Window.getWindows() 反射取 JIGBasePanel
47
+ JIGBasePanel panel = null;
48
+ for (int i = 0; i < 20 && panel == null; i++) {
49
+ for (Window w : Window.getWindows()) {
50
+ if (w == null || !w.isVisible()) continue;
51
+ JIGBasePanel p = findPanel(w);
52
+ if (p != null) { panel = p; break; }
53
+ }
54
+ if (panel == null) Thread.sleep(200);
55
+ }
56
+ if (panel == null) {
57
+ System.err.println("错误: 无法从 JFrame 提取 JIGBasePanel");
58
+ System.exit(1);
59
+ }
60
+ String low = out.toLowerCase();
61
+ if (low.endsWith(".png")) panel.save2PNG(new File(out));
62
+ else if (low.endsWith(".pdf")) panel.save2PDF(new File(out));
63
+ else panel.save2SVG(new File(out));
64
+ System.err.println("[tbplot] 已保存: " + out);
65
+ System.exit(0);
66
+ }
67
+
68
+ private static JIGBasePanel findPanel(Component c) {
69
+ if (c instanceof JIGBasePanel) return (JIGBasePanel) c;
70
+ if (c instanceof java.awt.Container) {
71
+ for (Component child : ((java.awt.Container) c).getComponents()) {
72
+ JIGBasePanel p = findPanel(child);
73
+ if (p != null) return p;
74
+ }
75
+ }
76
+ return null;
77
+ }
78
+ }
@@ -0,0 +1,47 @@
1
+ import biocjava.bioDoer.NGSDataAnalysis.SequencingDataRecommand.AssemblyGenomeDataSizeRecommand;
2
+ import biocjava.bioDoer.NGSDataAnalysis.SequencingDataRecommand.AssemblyGenomeDataSizeRecommand.AssemblyObject;
3
+ import biocjava.bioDoer.NGSDataAnalysis.SequencingDataRecommand.RecommendSize;
4
+
5
+ /**
6
+ * tbplot seqrecommend — 基因组组装测序量推荐 CLI(GUI 逆向 #43,09/21)
7
+ *
8
+ * 用法: AssemblyRecommandCli <genomeSize1n_bp> [--polyploid] [--het 0.01] [--level Minimum|Draft|Haplotyped_Resolved|Haplotyped_T2T]
9
+ * genomeSize1n_bp: 1n 基因组大小(bp,如 400000000)
10
+ * --polyploid: 多倍体
11
+ * --het: 杂合率(如 0.01 = 1%)
12
+ * --level: 组装目标(默认 Draft)
13
+ *
14
+ * 引擎: AssemblyGenomeDataSizeRecommand(GUI 逆向:AssemblyGenomeDataSizeRecommandGUIPanel $2
15
+ * → process(size, polyploid, het, object) → RecommendSize.getReportString();纯计算离线)
16
+ */
17
+ public class AssemblyRecommandCli {
18
+ public static void main(String[] args) throws Exception {
19
+ if (args.length < 1) {
20
+ System.err.println("用法: AssemblyRecommandCli <genomeSize1n_bp> [--polyploid] [--het 0.01] [--level Minimum|Draft|Haplotyped_Resolved|Haplotyped_T2T]");
21
+ System.exit(1);
22
+ }
23
+ double genomeSize1n = Double.parseDouble(args[0]);
24
+ boolean isPolyPoid = false;
25
+ double het = 0.01;
26
+ AssemblyObject obj = AssemblyObject.Draft;
27
+ for (int i = 1; i < args.length; i++) {
28
+ switch (args[i]) {
29
+ case "--polyploid": isPolyPoid = true; break;
30
+ case "--het": het = Double.parseDouble(args[++i]); break;
31
+ case "--level":
32
+ String lv = args[++i];
33
+ if (lv.equalsIgnoreCase("Minimum") || lv.equalsIgnoreCase("Mininum")) obj = AssemblyObject.Mininum;
34
+ else if (lv.equalsIgnoreCase("Haplotyped_Resolved")) obj = AssemblyObject.Haplotyped_Resolved;
35
+ else if (lv.equalsIgnoreCase("Haplotyped_T2T")) obj = AssemblyObject.Haplotyped_T2T;
36
+ else obj = AssemblyObject.Draft;
37
+ break;
38
+ default:
39
+ System.err.println("警告: 忽略未知参数 " + args[i]);
40
+ }
41
+ }
42
+ AssemblyGenomeDataSizeRecommand agdsr = new AssemblyGenomeDataSizeRecommand();
43
+ RecommendSize rs = agdsr.process(genomeSize1n, isPolyPoid, het, obj);
44
+ System.out.println(rs.getReportString());
45
+ System.exit(0);
46
+ }
47
+ }
@@ -0,0 +1,31 @@
1
+ import biocjava.bioIO.HTSData.SAMBAM.Utils.BAMIndexCreater;
2
+
3
+ import java.io.File;
4
+
5
+ /**
6
+ * tbcli bamindex — BAM 索引创建 CLI(08/31 第七十二波)
7
+ *
8
+ * 用法: BamIndexCli <in.sorted.bam> [out.bai]
9
+ * in.sorted.bam: 已排序 BAM
10
+ * out.bai: 输出 .bai(默认 <in>.bai)
11
+ *
12
+ * 引擎: BAMIndexCreater.setInSortedBamFile/setOutBaiFile + process()
13
+ * (main 硬编码演示 → setter+process)
14
+ */
15
+ public class BamIndexCli {
16
+ public static void main(String[] args) throws Exception {
17
+ if (args.length < 1) {
18
+ System.err.println("用法: BamIndexCli <in.sorted.bam> [out.bai]");
19
+ System.exit(1);
20
+ }
21
+ String inBam = args[0];
22
+ String outBai = args.length > 1 ? args[1] : inBam + ".bai";
23
+
24
+ BAMIndexCreater bic = new BAMIndexCreater();
25
+ bic.setInSortedBamFile(new File(inBam));
26
+ bic.setOutBaiFile(new File(outBai));
27
+ bic.process();
28
+ System.err.println("[tbplot] 已保存: " + outBai);
29
+ System.exit(0);
30
+ }
31
+ }
@@ -0,0 +1,43 @@
1
+ import biocjava.bioIO.HTSData.SAMBAM.Utils.SAMBAMSorter;
2
+ import htsjdk.samtools.SAMFileHeader;
3
+
4
+ import java.io.File;
5
+
6
+ /**
7
+ * tbcli bamsort — BAM 排序 CLI(08/31 第七十二波)
8
+ *
9
+ * 用法: BamSortCli <in.bam> <out.bam> [sortOrder] [tmpDir]
10
+ * sortOrder: coordinate|queryname|unsorted|duplicate(默认 coordinate)
11
+ * tmpDir: 临时目录(默认系统临时)
12
+ *
13
+ * 引擎: SAMBAMSorter.setInFile/setOutFile/setSo/setTmpDir + process()
14
+ * (main 硬编码演示 → setter+process)
15
+ */
16
+ public class BamSortCli {
17
+ public static void main(String[] args) throws Exception {
18
+ if (args.length < 2) {
19
+ System.err.println("用法: BamSortCli <in.bam> <out.bam> [sortOrder] [tmpDir]");
20
+ System.exit(1);
21
+ }
22
+ String soStr = args.length > 2 ? args[2] : "coordinate";
23
+ String tmpDir = args.length > 3 ? args[3] : null;
24
+
25
+ SAMFileHeader.SortOrder so;
26
+ switch (soStr.toLowerCase()) {
27
+ case "coordinate": so = SAMFileHeader.SortOrder.coordinate; break;
28
+ case "queryname": so = SAMFileHeader.SortOrder.queryname; break;
29
+ case "unsorted": so = SAMFileHeader.SortOrder.unsorted; break;
30
+ case "duplicate": so = SAMFileHeader.SortOrder.duplicate; break;
31
+ default: so = SAMFileHeader.SortOrder.coordinate; break;
32
+ }
33
+
34
+ SAMBAMSorter sbs = new SAMBAMSorter();
35
+ sbs.setInFile(new File(args[0]));
36
+ sbs.setOutFile(new File(args[1]));
37
+ sbs.setSo(so);
38
+ if (tmpDir != null) sbs.setTmpDir(new File(tmpDir));
39
+ sbs.process();
40
+ System.err.println("[tbplot] 已保存: " + args[1] + " (sort=" + soStr + ")");
41
+ System.exit(0);
42
+ }
43
+ }
@@ -0,0 +1,49 @@
1
+ import java.io.File;
2
+ import java.util.ArrayList;
3
+ import java.util.List;
4
+
5
+ /**
6
+ * tbplot bamstate — BAM 覆盖状态评估 CLI(08/29,第 57 引擎)
7
+ *
8
+ * 用法: BamStateCli <gff3> <out.tsv> <bam1> [bam2 ...] [--coverageThr X] [--depthThr X]
9
+ * gff3: 标准 GFF3(gene/mRNA/exon 特征)
10
+ * out.tsv: 每 BAM 的 coverage 比例 / depth / 总基因数 / 表达基因数
11
+ * bamN: 比对 BAM(需 samtools index 建立 .bai)
12
+ *
13
+ * ⚠️ BAM 参考染色体名必须与 GFF3 seqid 匹配(HiC_scaffold_* 等)
14
+ * 实测:GRAS RNA-seq bam_subset + arrb21 GFF3(HiC_scaffold)验证通过
15
+ */
16
+ public class BamStateCli {
17
+ public static void main(String[] args) throws Exception {
18
+ if (args.length < 3) {
19
+ System.err.println("用法: BamStateCli <gff3> <out.tsv> <bam1> [bam2 ...] [--coverageThr X] [--depthThr X]");
20
+ System.exit(1);
21
+ }
22
+ // 解析选项(-- 开头),其余为 bam
23
+ ArrayList<String> bams = new ArrayList<>();
24
+ double covThr = 0.5, depthThr = 3.0;
25
+ boolean gffDone = false;
26
+ String gff = args[0];
27
+ String out = args[1];
28
+ for (int i = 2; i < args.length; i++) {
29
+ if (args[i].equals("--coverageThr") && i+1 < args.length) covThr = Double.parseDouble(args[++i]);
30
+ else if (args[i].equals("--depthThr") && i+1 < args.length) depthThr = Double.parseDouble(args[++i]);
31
+ else bams.add(args[i]);
32
+ }
33
+ if (bams.isEmpty()) { System.err.println("至少需要一个 BAM"); System.exit(1); }
34
+ ArrayList<File> bamFiles = new ArrayList<>();
35
+ for (String b : bams) bamFiles.add(new File(b));
36
+ Object o = Class.forName("biocjava.bioDoer.GenomeAnnotation.BAMStateAssessor")
37
+ .getDeclaredConstructor().newInstance();
38
+ Class<?> c = o.getClass();
39
+ c.getMethod("setBamFiles", List.class).invoke(o, bamFiles);
40
+ c.getMethod("setGffFile", File.class).invoke(o, new File(gff));
41
+ c.getMethod("setCoverageThreshold", double.class).invoke(o, covThr);
42
+ c.getMethod("setDepthThreshold", double.class).invoke(o, depthThr);
43
+ c.getMethod("setOutputFile", File.class).invoke(o, new File(out));
44
+ c.getMethod("setThreads", int.class).invoke(o, 4);
45
+ c.getMethod("process").invoke(o);
46
+ System.err.println("[tbplot] BAM 状态评估完成: " + out);
47
+ System.exit(0);
48
+ }
49
+ }
@@ -0,0 +1,18 @@
1
+ import biocjava.bioDoer.bar_plotter;
2
+
3
+ /**
4
+ * tbcli barplotter 桥 — 合成共线性柱状图 CLI(08/31 第六十六波)
5
+ *
6
+ * 用法: BarPlotterCli -g <gff> -s <synteny> -c <ctl> -o <out.png>
7
+ * gff: chr\tgene\tend(简化 GFF)
8
+ * synteny: MCScanX 式 collinearity(# Alignment 分段,行=基因1\t基因2\t...)
9
+ * ctl: 4 行 = xdim / ydim / xchr列表(逗号分隔) / ychr列表(逗号分隔)
10
+ *
11
+ * ⚠️ bar_plotter.main 是死代码(打印 -4)→ 真入口是 main1(main1≠main 规律)
12
+ */
13
+ public class BarPlotterCli {
14
+ public static void main(String[] args) {
15
+ bar_plotter.main1(args);
16
+ System.exit(0);
17
+ }
18
+ }
@@ -0,0 +1,168 @@
1
+ import biocjava.bioDoer.JIGplotToolkit.EnrichmentAnalysisGraph.Barplot;
2
+ import jigplot.engine.JIGBasePanel;
3
+
4
+ import java.awt.Window;
5
+ import java.io.File;
6
+ import java.lang.reflect.Field;
7
+
8
+ /**
9
+ * Barplot CLI — 富集柱状图(-log10 P-value 横向柱状图)
10
+ *
11
+ * 用法:
12
+ * java -cp JAR:tbplot BarplotCli <enrichment.xls> <outFile> <termCol> <pvalCol> [classCol] [maxTerms] [xlab] [ylab] [mode]
13
+ *
14
+ * 参数:
15
+ * enrichment.xls — 富集结果表(TSV 带表头)
16
+ * outFile — 输出文件 (.svg/.png/.pdf)
17
+ * termCol — Term 列名(如 GO_Name / Pathway)
18
+ * pvalCol — P-value 列名(如 P_value)
19
+ * classCol — 分 class 列名(可选,如 Class)
20
+ * maxTerms — 最多显示 Term 数(默认 50)
21
+ * xlab — X 轴标签(默认 "-log10(P-value)")
22
+ * ylab — Y 轴标签(默认 "GO Term")
23
+ * mode — 图模式: Normal|TextOnLeft|BarOnLeft(默认 Normal)
24
+ *
25
+ * 数据格式(TSV,首行表头):
26
+ * GO_Name\tP_value\tClass
27
+ * photosynthesis\t0.0001\tBP
28
+ * ...
29
+ */
30
+ public class BarplotCli {
31
+
32
+ public static void main(String[] args) throws Exception {
33
+ if (args.length < 4) {
34
+ System.err.println("用法: BarplotCli <enrichment.xls> <outFile> <termCol> <pvalCol> [classCol] [maxTerms] [xlab] [ylab] [mode]");
35
+ System.exit(1);
36
+ }
37
+
38
+ String inFile = args[0];
39
+ String outFile = args[1];
40
+ String termCol = args[2];
41
+ String pvalCol = args[3];
42
+ String classCol = args.length > 4 ? args[4] : null;
43
+ int maxTerms = args.length > 5 ? Integer.parseInt(args[5]) : 50;
44
+ String xlab = args.length > 6 ? args[6] : "-log10(P-value)";
45
+ String ylab = args.length > 7 ? args[7] : "GO Term";
46
+ String modeStr = args.length > 8 ? args[8] : "Normal";
47
+
48
+ Barplot.GraphMode mode;
49
+ switch (modeStr.trim().toUpperCase()) {
50
+ case "TEXTONLEFT": mode = Barplot.GraphMode.TextOnLeft; break;
51
+ case "BARONLEFT": mode = Barplot.GraphMode.BarOnLeft; break;
52
+ default: mode = Barplot.GraphMode.Normal; break;
53
+ }
54
+
55
+ // 记录当前窗口数
56
+ int windowCountBefore = Window.getWindows().length;
57
+
58
+ // 构建 Barplot
59
+ Barplot bp = new Barplot();
60
+ bp.setInTabFile(new File(inFile));
61
+ bp.setTermColName(termCol);
62
+ bp.setpValueColName(pvalCol);
63
+ if (classCol != null && !classCol.equals("-")) {
64
+ bp.setClassColName(classCol);
65
+ }
66
+ bp.setMaxTermToShow(maxTerms);
67
+ bp.setXlab(xlab);
68
+ bp.setYlab(ylab);
69
+ bp.setGraphMode(mode);
70
+
71
+ // 调用 generate()(会弹 JFrame,但 xvfb 下不阻塞)
72
+ bp.generate();
73
+
74
+ // 等待 Swing 完成渲染
75
+ Thread.sleep(500);
76
+
77
+ // 遍历所有窗口找 JIGBasePanel
78
+ JIGBasePanel targetPanel = null;
79
+ Window[] windows = Window.getWindows();
80
+ for (Window w : windows) {
81
+ // 搜索组件树找 JIGBasePanel
82
+ targetPanel = findJIGBasePanel(w);
83
+ if (targetPanel != null) break;
84
+ }
85
+
86
+ if (targetPanel == null) {
87
+ // 反射从 Barplot 实例抓 jigPanel 字段(如果存在)
88
+ System.err.println("窗口遍历未找到 JIGBasePanel,尝试反射...");
89
+ // Barplot 内部 jigPanel 是局部变量,无法反射
90
+ // 但 JScrollPane 包含 JIGBasePanel
91
+ for (Window w : windows) {
92
+ if (w instanceof javax.swing.JFrame) {
93
+ java.awt.Container content = ((javax.swing.JFrame) w).getContentPane();
94
+ targetPanel = findJIGBasePanelDeep(content);
95
+ if (targetPanel != null) break;
96
+ }
97
+ }
98
+ }
99
+
100
+ if (targetPanel != null) {
101
+ // 保存
102
+ if (outFile.toLowerCase().endsWith(".svg")) {
103
+ targetPanel.save2SVG(new java.io.File(outFile));
104
+ } else if (outFile.toLowerCase().endsWith(".png")) {
105
+ targetPanel.save2PNG(new java.io.File(outFile));
106
+ } else if (outFile.toLowerCase().endsWith(".pdf")) {
107
+ targetPanel.save2PDF(new java.io.File(outFile));
108
+ } else {
109
+ targetPanel.save2SVG(new java.io.File(outFile + ".svg"));
110
+ }
111
+ System.err.println("Barplot saved: " + outFile);
112
+
113
+ // 关闭窗口
114
+ for (Window w : windows) {
115
+ if (w instanceof java.awt.Frame) {
116
+ ((java.awt.Frame) w).dispose();
117
+ }
118
+ }
119
+ } else {
120
+ System.err.println("ERROR: 未找到 JIGBasePanel,无法保存。");
121
+ System.err.println("Windows count: " + windows.length);
122
+ for (Window w : windows) {
123
+ System.err.println(" Window: " + w.getClass().getName() + " visible=" + w.isVisible());
124
+ }
125
+ System.exit(2);
126
+ }
127
+ }
128
+
129
+ static JIGBasePanel findJIGBasePanel(java.awt.Container c) {
130
+ for (int i = 0; i < c.getComponentCount(); i++) {
131
+ java.awt.Component comp = c.getComponent(i);
132
+ if (comp instanceof JIGBasePanel) {
133
+ return (JIGBasePanel) comp;
134
+ }
135
+ if (comp instanceof java.awt.Container) {
136
+ JIGBasePanel found = findJIGBasePanel((java.awt.Container) comp);
137
+ if (found != null) return found;
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+
143
+ static JIGBasePanel findJIGBasePanelDeep(java.awt.Container c) {
144
+ // 搜索 JScrollPane -> JViewport -> JIGBasePanel
145
+ for (int i = 0; i < c.getComponentCount(); i++) {
146
+ java.awt.Component comp = c.getComponent(i);
147
+ if (comp instanceof JIGBasePanel) {
148
+ return (JIGBasePanel) comp;
149
+ }
150
+ if (comp instanceof javax.swing.JScrollPane) {
151
+ javax.swing.JScrollPane jsp = (javax.swing.JScrollPane) comp;
152
+ java.awt.Component view = jsp.getViewport().getView();
153
+ if (view instanceof JIGBasePanel) {
154
+ return (JIGBasePanel) view;
155
+ }
156
+ if (view instanceof java.awt.Container) {
157
+ JIGBasePanel found = findJIGBasePanelDeep((java.awt.Container) view);
158
+ if (found != null) return found;
159
+ }
160
+ }
161
+ if (comp instanceof java.awt.Container) {
162
+ JIGBasePanel found = findJIGBasePanelDeep((java.awt.Container) comp);
163
+ if (found != null) return found;
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ }