emacs在org模式画图
org模式画简单的图,可以用ditaa这种工具,小巧够用,可以满足大部分的需求。 配合emacs的picture-mode和artis-mode,可以实现文本画大部分的图。生成的 图形效果还不错。
但要画比较复杂的图,比如脑图,时序图等,ditaa就有点不够用了。就需要 plantuml这种更强大的工具。
plantuml
介绍
PlantUML 是基于 Java 语言的开源 UML(Unified Modeling Language)图形绘 制工具,因为其可以单独绘图,又可以嵌入 Markdown 以及 HTML,因而被不少 开发文档采用。PlantUML 可以看作是著名绘图工具 Graphviz 的封装。
PlantUML 支持的图形可简单分为三种类型
- 基本类:流程图、时序图、甘特图
- 工程类:类图、实体关系图、用例图、组件图
- 导图类:思维导图、组织结构导图
其他的支持类型,可以到官网上查看。
PlantUML 内置了 30 多种主题,可以通过 help themes 命令查看内置主题列表。 https://plantuml.com/zh/
安装
mac下先安装graphviz,是plantuml的依赖组件。
:: brew install graphviz
后从官网下载plantuml的jar包,放到emacs的插件目录下。
配置
emacs中,相关的配置
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(ditaa . t)
(python . t)
(shell . t)
(latex . t)
(plantuml . t)
(dot . t)
(lisp . t)
(org . t)
(java . t)))
(setq org-plantuml-jar-path
(expand-file-name "~/.emacs.d/plugins/plantuml-1.2023.8.jar"))
(setq org-confirm-babel-evaluate nil) ;picture 不提示
(add-hook 'org-babel-after-execute-hook 'bh/display-inline-images 'append)
(setq org-babel-results-keyword "results")
(defun bh/display-inline-images ()
(condition-case nil
(org-display-inline-images)
(error nil)))
(define-skeleton skel-org-block-plantuml
"Insert a org plantuml block, querying for filename."
"File (no extension): "
"#+begin_src plantuml :file " str ".png :cach yes :cmdline -charset UTF-8\n"
"title " @ - \n
"#+end_src\n")
(define-abbrev org-mode-abbrev-table "spuml" "" 'skel-org-block-plantuml)
语法
颜色,基本组件
- ->表示消息传递走向,默认箭头类型是单向直线,PlantUML手册中有很多样式可以通过代码自定义颜色和样式
- –>是单向虚直线箭头,一般是用来传递消息返回信息
- scale=2 设置图形大小
- 使用 as关键字重命名参与者
- 使用 title 增加标题
- legend 和 end legend 作为关键词,用于配置一个图例(legend). 支持可选 地使用left,right,center为这个图例指定对齐方式.
- caption显示在图片下方的标题
- together 有时候,默认布局并不完美,你可以使用 together 关键词将某些类进行分组: 布局引擎会尝试将它们捆绑在一起
- package 可以定义不同的样式,你可以通过以下的命令来设置默认样式 : skinparam packageStyle,或者对包使用对应的模板
- 如果需要调整布局,学会使用a -[hidden]d- c , A -[hidden]left- B 类似 的方式
- skinparam nodesep x will increase the horizontal margin
- skinparam ranksep x affects the vertical margin
时序图
-
常用对象
actor :: 实体对象,比如人、操作员等 participant:: 参与者,可以是类、服务名等 database :: 数据库 queue :: 队列,比如kafka队列就可以用这个表示 boundary :: 边界 control :: 控制 entity :: 实体 collections :: 集合
-
常用操作
- 使用activate和deactivate来激活、销毁生命周期
- destroy 表示一个参与者的生命线的终结
- 用 alt-else-end 表示分支
- 用 loop-end表示循环
- 使用 order 来自定义参与者的顺序,值越大的越靠后
- autonumber 自动对消息编号
- 使用 note left 或 note right 给消息添加注释,可以用 end note 添加多 行注释。
- 用 == 将图标分隔成多个逻辑步骤
- 使用… 表示延迟,并且可以给延迟添加注释;如….5min later….
思维导图
- 以@startmindmap开始,以@endmindmap结束。
- 用+或-新建分支,用符号数量表示层级,+表示分支向右边生长,-表示分支向 左边生长。也可以用*建立分支
- 使用:开始多行内容,;结束多行内容。目前如果要使用: ;只能用*新建分支
-
思维导图中定义颜色
@startmindmap <style> mindmapDiagram { .green { BackgroundColor lightgreen } .rose { BackgroundColor #FFBBCC } .your_style_name { BackgroundColor lightblue } } </style> * Colors ** Green <<green>> ** Rose <<rose>> ** Blue <<your_style_name>> @endmindmap
流程图
架构图
plantuml支持Archimate画架构图,也可以通过c4的插件,画c4风格的架构图。
Archimate是一种架构建模语言,顾名思义,是专业架构师(Archi)的好伴侣 (Mate)。
ArchiMate的建模概念元素虽然种类繁多,但究其性质而言不外乎结构元素 (Structure Element)和行为元素(Behavior Element)两种(也可称为静态 元素和动态元素),前者代表了各种结构化的实体(如参与者、应用组件以及数 据等),而后者则用来描述结构化元素所能执行的各种行为(如业务活动、应用 功能以及服务等)。此外,根据与行为元素之间的关系进一步划分,结构元素又 可被分为主动性结构元素(Active Structure Element)和被动性结构元素 (Passive Structure Element),前者表示发起动作的结构元素(例如参与者、 应用组件等),而后者则被用来描述行为元素的各种目标(例如业务数据、信息 数据等)。由此可见,ArchiMate所使用的描述方式与很多标准化的描述方式 (例如RDF语言)有着异曲同工之妙,基本采用“主动对象(主语)、行为(谓 语)、被动对象(宾语)+相互之间的关系”进行建模描述。
ArchiMate 3.0可以完全覆盖自身所在行业架构工作建模的方方面面,但它只有 59个元素 、13种关联关系(relationship) ,如此简洁的要素和关系却可以描 述如复杂的架构范围,堪称建模语言的翘楚。
示例
流程图
打开一个org文档,加入一个planduml代码块,代码需要以BEGIN_SRC开头 plantuml开头如下:
start
if (Graphviz installed?) then (yes)
:process all\ndiagrams;
else (no)
:process only
__sequence__ and __activity__ diagrams;
endif
stop
显示效果如下:

架构图
uml代码
title
<u>Simple</u> communication example
on <i>several</i> lines and using <back:cadetblue>creole tags</back>
end title
node "note " {
rectangle GO #lightgreen
rectangle STOP #red
rectangle WAIT #orange
!define Junction_Or circle #black
!define Junction_And circle #whitesmoke
Junction_And JunctionAnd
Junction_Or JunctionOr
archimate #Technology "VPN Server" as vpnServerA <<technology-device>>
GO -up-> JunctionOr
STOP -up-> JunctionOr
STOP -down-> JunctionAnd
WAIT -down-> JunctionAnd
}

package和颜色
title comtest
skinparam rectangle<<behavior>> {
roundCorner 25
}
sprite $bProcess jar:archimate/business-process
sprite $aService jar:archimate/application-service
sprite $aComponent jar:archimate/application-component
rectangle user #lightyellow;line.dotted;text:red {
rectangle "Handle claim" as HC <<$bProcess>><<behavior>> #Business
rectangle "Capture Information" as CI <<$bProcess>><<behavior>> #Business
rectangle "Notify\nAdditional Stakeholders" as NAS <<$bProcess>><<behavior>> #Business
rectangle "Validate" as V <<$bProcess>><<behavior>> #Business
rectangle "Investigate" as I <<$bProcess>><<behavior>> #Business
rectangle "Pay" as P <<$bProcess>><<behavior>> #Business
HC *-down- CI
HC *-down- NAS
HC *-down- V
HC *-down- I
HC *-down- P
V -right->> I
I -right->> P
}
package "severicenow" #lightgreen {
rectangle "Scanning" as scanning <<$aService>><<behavior>> #Application
rectangle "Customer admnistration" as customerAdministration <<$aService>><<behavior>> #Application
rectangle "Claims admnistration" as claimsAdministration <<$aService>><<behavior>> #Application
rectangle Printing <<$aService>><<behavior>> #Application
rectangle Payment <<$aService>><<behavior>> #Application
}
scanning -up-> CI
customerAdministration -up-> CI
claimsAdministration -up-> NAS
claimsAdministration -up-> I
Payment -up-> P
Printing -up-> V
Printing -up-> P
rectangle #lightgray;line:lightgray {
rectangle "Document\nManagement\nSystem" as DMS <<$aComponent>> #Application
rectangle "General\nCRM\nSystem" as CRM <<$aComponent>> #Application
rectangle "Home & Away\nPolicy\nAdministration" as HAPA <<$aComponent>> #Application
rectangle "Home & Away\nFinancial\nAdministration" as HFPA <<$aComponent>> #Application
}
DMS .up.|> scanning
CRM .up.|> customerAdministration
HAPA .up.|> claimsAdministration
HFPA .up.|> Payment
legend left
Example from the "Archisurance case study" (OpenGroup).
See
====
<$bProcess> :business process
====
<$aService> : application service
====
<$aComponent> : application component
endlegend

可以嵌套的元素
'left to right direction
top to bottom direction
rectangle "rc1" as rc1{
artifact artifact {
}
card card {
}
cloud cloud {
}
component component {
}
database database {
}
}
rectangle "rc2" as rc2 #lightgreen {
file file {
}
folder folder {
}
frame frame {
}
hexagon hexagon {
}
node node {
}
}
package "rc3" as rc3{
package package {
}
queue queue {
}
rectangle rectangle {
}
stack stack {
}
storage storage {
}
}
rc1 -[hidden]-> rc2
rc2-[hidden]->rc3

package 可以指定种类
scale 750 width
package foo1 <<Node>> {
class Class1
}
package foo2 <<Rectangle>> {
class Class2
}
package foo3 <<Folder>> {
class Class3
}
package foo4 <<Frame>> {
class Class4
}
package foo5 <<Cloud>> {
class Class5
}
package foo6 <<Database>> {
class Class6
}

plantuml支持ditaa
plantuml中,可以支持ditaa图。以@startditaa开始以@endditaa之间的内容, 会按ditaa的语法来处理。plantuml代码块里的代码如下:

生成的图片:

plantuml 画c4图
!include <C4/C4_Container>
AddElementTag("microService", $shape=EightSidedShape(), $bgColor="CornflowerBlue", $fontColor="white", $legendText="micro service\neight sided")
AddElementTag("storage", $shape=RoundedBoxShape(), $bgColor="lightSkyBlue", $fontColor="white")
SHOW_PERSON_OUTLINE()
Person(customer, Customer, "A customer")
System_Boundary(c1, "Customer Information") {
Container(app, "Customer Application", "Javascript, Angular", "Allows customers to manage their profile")
Container(customer_service, "Customer Service", "Java, Spring Boot", "The point of access for customer information", $tags = "microService")
Container(message_bus, "Message Bus", "RabbitMQ", "Transport for business events")
Container(reporting_service, "Reporting Service", "Ruby", "Creates normalised data for reporting purposes", $tags = "microService")
Container(audit_service, "Audit Service", "C#/.NET", "Provides organisation-wide auditing facilities", $tags = "microService")
ContainerDb(customer_db, "Customer Database", "Oracle 12c", "Stores customer information", $tags = "storage")
ContainerDb(reporting_db, "Reporting Database", "MySQL", "Stores a normalized version of all business data for ad hoc reporting purposes", $tags = "storage")
Container(audit_store, "Audit Store", "Event Store", "Stores information about events that have happened", $tags = "storage")
}
Rel_D(customer, app, "Uses", "HTTPS")
Rel_D(app, customer_service, "Updates customer information using", "async, JSON/HTTPS")
Rel_U(customer_service, app, "Sends events to", "WebSocket")
Rel_U(customer_service, message_bus, "Sends customer update events to")
Rel(customer_service, customer_db, "Stores data in", "JDBC")
Rel(message_bus, reporting_service, "Sends customer update events to")
Rel(message_bus, audit_service, "Sends customer update events to")
Rel(reporting_service, reporting_db, "Stores data in")
Rel(audit_service, audit_store, "Stores events in")
Lay_R(reporting_service, audit_service)
SHOW_LEGEND()
