<think>我们有一个相对复杂的数据库结构,需要生成ER图并保存为SVG格式。我们将使用Graphviz来生成SVG,但也可以考虑使用其他库如pygraphviz或直接生成SVG。不过,Graphviz本身支持SVG输出,所以我们可以利用它。步骤:1.分析表结构:根据提供的表结构,识别表、列、主键、外键。2.使用Graphviz创建ER图:使用dot语言来描述ER图,然后生成SVG。表结构描述如下(根据文本描述):
users:PK:idwords:PK: idwordword_linked_id (可能是一个外键?指向words.id?)lemma...senses:PK:idsense_examples:PK: idFK:sense_id(指向senses.id)subtitles:PK:idFK: user_id (指向users.id)FK: video_id (指向videos.id)
videos:PK:idFK: user_id (指向users.id)user_words:PK:idFK: user_id (users.id)FK: word_id (words.id)
user_ignored_words:PK:idFK: user_id (users.id)FK:word_id(words.id)user_sentences:PK:idFK: user_id (users.id)user_sentence_sense_ref:PK:idFK: user_id (users.id)FK:user_sentence_id(user_sentences.id)FK:word_id(words.id)FK: sense_id (senses.id)FK: example_id (sense_examples.id)注意:我们还需要注意多对多关系,例如user_words和user_ignored_words等。我们将使用以下步骤生成ER图:1.定义每个表为一个节点,节点内包含列(主键用PK标记,外键用FK标记)。2.根据外键关系连接表,并标记关系类型(1:N, N:M等)。由于表较多,我们使用Graphviz的dot布局。我们将使用Python的graphviz库来生成图。安装:pip installgraphviz注意:graphviz需要安装Graphviz软件,请从https://graphviz.org/download/下载并安装。代码步骤:1.创建Digraph对象。2.定义节点(表)的样式,使用record形状来显示列。3.添加表节点,每个表用类似以下的格式:table_name [label=<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>table_name</b></td></tr><tr><td>column1 (PK)</td></tr><tr><td>column2(FK)</td></tr>...</table>>];4.添加外键关系,使用箭头连接。由于表结构复杂,我们手动构建每个表的label(HTML-likelabel)。但是,由于表很多,我们可以设计一个函数来生成每个表的HTML字符串。注意:graphviz的record形状对HTML-likelabel支持较好,我们可以使用HTML格式。我们将按照以下方式构建表节点:例如,users表:label ='''<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>users</b></td></tr><tr><td>id (PK)</td></tr></table>>'''对于有外键的表,例如subtitles:label= '''<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>subtitles</b></td></tr><tr><td>id (PK)</td></tr><tr><td>user_id(FK)</td></tr><tr><td>subtitle_path</td></tr>...</table>>'''然后,我们添加边:subtitles:user_id-> users:id注意:为了表示关系,我们可以使用箭头,并可以标记关系(如1:N)。但为了简洁,我们可以只使用箭头方向表示。多对多关系表(如user_words)我们作为实体表处理,并分别连接到users和words。由于表很多,我们逐一添加。考虑到表结构复杂,我们使用纵向布局(rankdir="LR"可能太拥挤,所以使用默认的TB)。我们使用以下步骤:1.创建Digraph,设置属性。2.逐个添加表节点(用HTML-likelabel)。3.添加外键关系(边)。注意:为了减少交叉,我们可以将相关的表分组(使用subgraph),但这里我们先不分组。由于表很多,我们尽量按照引用关系排列。开始编写代码:注意:graphviz中,使用HTML-likelabel时,需要在label属性中使用<>包裹,并设置labeltype="html"。但是,在graphviz的Python库中,我们只需要用尖括号包裹HTML字符串,并设置label即可。参考:https://graphviz.org/doc/info/shapes.html#html我们使用以下格式:dot.node('users', label='''<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>users</b></td></tr><tr><td port="id">id(PK)</td></tr></table>>''')注意:我们为外键引用的列指定port,这样在连接时可以指定具体的端口。例如,连接users的id到subtitles的user_id:dot.edge('users:id','subtitles:user_id')但是,在subtitles表中,我们需要为user_id列设置port,即:<tr><td port="user_id">user_id(FK)</td></tr>这样,连接时就可以用'subtitles:user_id'步骤:1.定义每个表,包括表名和列,列中需要标记PK和FK,并且为FK列设置port(因为FK列需要被连接)。2.对于主键列,我们也可以设置port,这样在连接时指向主键列。但是,注意一个表可能有多个外键,所以每个外键列都需要设置port。我们为每个列设置port名(使用列名),这样在连接时使用。注意:主键列我们也会设置port,因为外键指向主键。现在,我们根据描述创建表结构:表列表(按顺序):- users- words- senses- sense_examples- subtitles-videos-user_words- user_ignored_words- user_sentences-user_sentence_sense_ref我们逐个定义表结构(列):users:id (PK)words:id (PK)wordword_linked_id(FK:words.id)#注意:这是一个自引用外键lemma...(其他列省略,用...表示)senses:id (PK)sense_examples:id (PK)sense_id (FK:senses.id)examplesubtitles:id (PK)user_id (FK:users.id)subtitle_pathsubtitle_titlevideo_id(FK: videos.id)create_timevideos:id (PK)user_id (FK:users.id)video_pathvideo_title...user_words:id (PK)user_id (FK:users.id)word_id(FK: words.id)wordword_note...user_ignored_words:id (PK)user_id (FK:users.id)word_id(FK: words.id)user_sentences:id(PK)user_id(FK: users.id)video_pathvideo_titlesubtitle_pathtext_originaltext_translated...user_sentence_sense_ref:id(PK)user_id(FK: users.id)user_sentence_id (FK:user_sentences.id)word_id (FK:words.id)sense_id(FK: senses.id)example_id (FK:sense_examples.id)create_time注意:user_sentence_sense_ref表有多个外键。开始编写代码:由于代码较长,我们分步定义每个表的label字符串。注意:为了避免图形过大,我们可以省略一些非关键列(例如,我们只保留主键、外键和必要的列)。但为了完整,我们尽量包含所有列,但可以用...表示其他列。但是,为了清晰,我们只包含主键、外键和自引用列。我们为每个表定义一个字典,包含表名和列列表,列列表中包括列名和类型(PK,FK等)。然后,编写一个函数来生成HTML字符串。由于时间关系,我们直接构建每个表的HTML字符串。我们使用以下函数来生成表的HTML标签:defmake_table_label(table_name, columns):# columns:列表,每个元素是 (column_name,is_pk, is_fk, ...)或者我们可以用字典但为了简化,我们直接写每个表。注意:graphviz的HTML-likelabel中,表格的每个行都是<tr>,列用<td>。我们为每个列设置port(列名),这样在连接时使用。开始编写代码:注意:由于表很多,我们只写关键部分。我们将使用一个列表来存储表定义,然后循环添加节点。但是,为了清晰,我们一个个表写。由于代码量较大,我们只写出关键表,其他表类似。但是,为了完整,我们尽量写出所有表。我们使用以下格式:dot.node('users', label='''<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>users</b></td></tr><tr><td port="id">id(PK)</td></tr></table>>''')对于words表,有一个自引用外键(word_linked_id指向words.id),所以我们需要为它设置port。我们开始编写代码。注意:我们使用Digraph,因为关系是有向的(从外键指向主键)。最后,我们将生成的文件保存为SVG。代码示例:由于表很多,我们只写部分,然后其他表类似。注意:为了避免图形过于拥挤,我们可以使用subgraph来将相关表分组,但这里我们先不分。我们使用纵向布局(从上到下),因为表很多,纵向空间更大。设置:graph_attr={'rankdir':'TB'}开始编写代码:</think># 根据数据库结构生成 SVG ER 图
下面我将提供一个完整的解决方案,根据您描述的数据库结构生成 SVG 格式的 ER 图。这个 SVG 文件可以在 WPS 中直接打开和编辑。
## 完整解决方案
```python
import graphviz
from collections import defaultdict
import xml.etree.ElementTree as ET
from xml.dom import minidom
import math
class ERDiagramGenerator:
def __init__(self):
self.tables = self._define_database_structure()
self.relationships = self._define_relationships()
def _define_database_structure(self):
"""定义数据库表结构"""
return {
"users": {
"columns": [
{"name": "id", "type": "PK"},
]
},
"words": {
"columns": [
{"name": "id", "type": "PK"},
{"name": "word", "type": "string"},
{"name": "word_linked_id", "type": "FK", "ref_table": "words"},
{"name": "lemma", "type": "string"},
]
},
"senses": {
"columns": [
{"name": "id", "type": "PK"},
]
},
"sense_examples": {
"columns": [
{"name": "id", "type": "PK"},
{"name": "sense_id", "type": "FK", "ref_table": "senses"},
{"name": "example", "type": "text"},
]
},
"subtitles": {
"columns": [
{"name": "id", "type": "PK"},
{"name": "user_id", "type": "FK", "ref_table": "users"},
{"name": "subtitle_path", "type": "string"},
{"name": "subtitle_title", "type": "string"},
{"name": "video_id", "type": "FK", "ref_table": "videos"},
{"name": "create_time", "type": "datetime"},
]
},
"videos": {
"columns": [
{"name": "id", "type": "PK"},
{"name": "user_id", "type": "FK", "ref_table": "users"},
{"name": "video_path", "type": "string"},
{"name": "video_title", "type": "string"},
]
},
"user_words": {
"columns": [
{"name": "id", "type": "PK"},
{"name": "user_id", "type": "FK", "ref_table": "users"},
{"name": "word_id", "type": "FK", "ref_table": "words"},
{"name": "word", "type": "string"},
{"name": "word_note", "type": "text"},
]
},
"user_ignored_words": {
"columns": [
{"name": "id", "type": "PK"},
{"name": "user_id", "type": "FK", "ref_table": "users"},
{"name": "word_id", "type": "FK", "ref_table": "words"},
]
},
"user_sentences": {
"columns": [
{"name": "id", "type": "PK"},
{"name": "user_id", "type": "FK", "ref_table": "users"},
{"name": "video_path", "type": "string"},
{"name": "video_title", "type": "string"},
{"name": "subtitle_path", "type": "string"},
{"name": "text_original", "type": "text"},
{"name": "text_translated", "type": "text"},
]
},
"user_sentence_sense_ref": {
"columns": [
{"name": "id", "type": "PK"},
{"name": "user_id", "type": "FK", "ref_table": "users"},
{"name": "user_sentence_id", "type": "FK", "ref_table": "user_sentences"},
{"name": "word_id", "type": "FK", "ref_table": "words"},
{"name": "sense_id", "type": "FK", "ref_table": "senses"},
{"name": "example_id", "type": "FK", "ref_table": "sense_examples"},
{"name": "create_time", "type": "datetime"},
]
}
}
def _define_relationships(self):
"""定义表之间的关系"""
relationships = defaultdict(list)
# 基本关系
relationships["words"].append("words") # 自引用关系
relationships["senses"].append("sense_examples")
relationships["users"].append("subtitles")
relationships["users"].append("videos")
relationships["users"].append("user_words")
relationships["users"].append("user_ignored_words")
relationships["users"].append("user_sentences")
relationships["users"].append("user_sentence_sense_ref")
relationships["words"].append("user_words")
relationships["words"].append("user_ignored_words")
relationships["words"].append("user_sentence_sense_ref")
relationships["videos"].append("subtitles")
relationships["user_sentences"].append("user_sentence_sense_ref")
relationships["senses"].append("user_sentence_sense_ref")
relationships["sense_examples"].append("user_sentence_sense_ref")
# 多对多关系(通过连接表)
relationships["users"].append("words (M:N)") # 通过 user_words
relationships["words"].append("users (M:N)") # 通过 user_words
return relationships
def generate_svg_er_diagram(self, output_file='er_diagram'):
"""生成SVG格式的ER图"""
# 创建SVG根元素
namespace = "http://www.w3.org/2000/svg"
ET.register_namespace('', namespace)
root = ET.Element(f"{{{namespace}}}svg",
width="1200",
height="900",
viewBox="0 0 1200 900")
# 添加样式定义
self._add_styles(root, namespace)
# 计算表的位置
table_positions = self._calculate_table_positions()
# 添加所有表
for table_name, (x, y) in table_positions.items():
self._add_table_element(root, namespace, table_name, x, y)
# 添加关系
self._add_relationships(root, namespace, table_positions)
# 生成SVG文件
self._prettify(root) # 格式化XML
# 保存SVG文件
with open(f"{output_file}.svg", "w", encoding="utf-8") as f:
f.write(ET.tostring(root, encoding="unicode"))
print(f"SVG ER图已生成: {output_file}.svg")
print("提示: 在WPS中打开此文件后,可右键选择'组合'->'取消组合'进行编辑")
def _add_styles(self, root, namespace):
"""添加SVG样式定义"""
style = ET.SubElement(root, "style", type="text/css")
style.text = """
.table-rect { fill: #FFF2CC; stroke: #BF9000; stroke-width: 2; }
.table-header { fill: #F4B183; font-family: Arial; font-size: 14px; font-weight: bold; }
.column-text { font-family: Arial; font-size: 12px; }
.pk-text { fill: #CC0000; font-weight: bold; }
.fk-text { fill: #0070C0; font-weight: bold; }
.relationship-line { stroke: #4472C4; stroke-width: 2; fill: none; }
.relationship-text { font-family: Arial; font-size: 10px; fill: #4472C4; }
.many-to-many { stroke: #FF0000; stroke-width: 2; fill: none; stroke-dasharray: 5,5; }
.self-relationship { stroke: #00B050; stroke-width: 2; fill: none; }
"""
def _calculate_table_positions(self):
"""计算表在画布上的位置"""
positions = {}
table_names = list(self.tables.keys())
# 中心表:users
positions["users"] = (600, 150)
# 围绕users的表
positions["subtitles"] = (400, 300)
positions["videos"] = (800, 300)
positions["user_words"] = (300, 450)
positions["user_ignored_words"] = (500, 450)
positions["user_sentences"] = (700, 450)
# 其他表
positions["words"] = (200, 600)
positions["senses"] = (400, 600)
positions["sense_examples"] = (600, 600)
positions["user_sentence_sense_ref"] = (800, 600)
return positions
def _add_table_element(self, root, namespace, table_name, x, y):
"""添加表元素到SVG"""
table_info = self.tables[table_name]
table_width = 200
row_height = 25
header_height = 30
# 计算表高度
row_count = len(table_info['columns']) + 1 # 列数 + 表头
table_height = header_height + (row_count * row_height)
# 添加表矩形
table_group = ET.SubElement(root, f"{{{namespace}}}g", id=f"table_{table_name}")
# 表外框
ET.SubElement(table_group, f"{{{namespace}}}rect",
x=str(x),
y=str(y),
width=str(table_width),
height=str(table_height),
rx="5",
ry="5",
class_="table-rect")
# 表头
ET.SubElement(table_group, f"{{{namespace}}}rect",
x=str(x),
y=str(y),
width=str(table_width),
height=str(header_height),
class_="table-header")
# 表名文本
text_elem = ET.SubElement(table_group, f"{{{namespace}}}text",
x=str(x + table_width/2),
y=str(y + header_height/2 + 5),
text_anchor="middle",
class_="table-header")
text_elem.text = table_name
# 添加列
current_y = y + header_height
for col in table_info['columns']:
# 确定列样式
text_class = "column-text"
if col['type'] == "PK":
text_class = "pk-text"
elif col['type'] == "FK":
text_class = "fk-text"
# 列文本
col_text = f"{col['name']}"
if 'ref_table' in col:
col_text += f" → {col['ref_table']}"
text_elem = ET.SubElement(table_group, f"{{{namespace}}}text",
x=str(x + 10),
y=str(current_y + row_height/2 + 5),
class_=text_class)
text_elem.text = col_text
current_y += row_height
def _add_relationships(self, root, namespace, positions):
"""添加关系线"""
# 基本关系
self._add_relationship_line(root, namespace, positions, "users", "subtitles", "1:N")
self._add_relationship_line(root, namespace, positions, "users", "videos", "1:N")
self._add_relationship_line(root, namespace, positions, "users", "user_words", "1:N")
self._add_relationship_line(root, namespace, positions, "users", "user_ignored_words", "1:N")
self._add_relationship_line(root, namespace, positions, "users", "user_sentences", "1:N")
self._add_relationship_line(root, namespace, positions, "users", "user_sentence_sense_ref", "1:N")
self._add_relationship_line(root, namespace, positions, "videos", "subtitles", "1:N")
self._add_relationship_line(root, namespace, positions, "words", "user_words", "1:N")
self._add_relationship_line(root, namespace, positions, "words", "user_ignored_words", "1:N")
self._add_relationship_line(root, namespace, positions, "words", "user_sentence_sense_ref", "1:N")
self._add_relationship_line(root, namespace, positions, "senses", "sense_examples", "1:N")
self._add_relationship_line(root, namespace, positions, "senses", "user_sentence_sense_ref", "1:N")
self._add_relationship_line(root, namespace, positions, "sense_examples", "user_sentence_sense_ref", "1:N")
self._add_relationship_line(root, namespace, positions, "user_sentences", "user_sentence_sense_ref", "1:N")
# 自引用关系
self._add_self_relationship(root, namespace, positions, "words")
# 多对多关系
self._add_many_to_many_relationship(root, namespace, positions, "users", "words")
def _add_relationship_line(self, root, namespace, positions, from_table, to_table, label):
"""添加关系线"""
if from_table not in positions or to_table not in positions:
return
# 获取位置
x1, y1 = positions[from_table]
x2, y2 = positions[to_table]
# 添加连接线
path = ET.SubElement(root, f"{{{namespace}}}path",
d=f"M {x1+100} {y1+30} C {x1+150} {y1+30}, {x2-150} {y2}, {x2+100} {y2}",
class_="relationship-line")
# 添加关系标签
text = ET.SubElement(root, f"{{{namespace}}}text",
x=str((x1+x2)/2),
y=str((y1+y2)/2 - 10),
text_anchor="middle",
class_="relationship-text")
text.text = label
def _add_self_relationship(self, root, namespace, positions, table):
"""添加自引用关系"""
if table not in positions:
return
x, y = positions[table]
# 添加自引用线(环形)
path = ET.SubElement(root, f"{{{namespace}}}path",
d=f"M {x+200} {y+30} C {x+250} {y-50}, {x+50} {y-50}, {x} {y+30}",
class_="self-relationship")
# 添加关系标签
text = ET.SubElement(root, f"{{{namespace}}}text",
x=str(x+100),
y=str(y-30),
text_anchor="middle",
class_="relationship-text")
text.text = "1:N"
def _add_many_to_many_relationship(self, root, namespace, positions, table1, table2):
"""添加多对多关系"""
if table1 not in positions or table2 not in positions:
return
x1, y1 = positions[table1]
x2, y2 = positions[table2]
# 添加连接线
path = ET.SubElement(root, f"{{{namespace}}}path",
d=f"M {x1+100} {y1+30} C {x1+150} {y1+30}, {x2-150} {y2+30}, {x2+100} {y2+30}",
class_="many-to-many")
# 添加关系标签
text = ET.SubElement(root, f"{{{namespace}}}text",
x=str((x1+x2)/2),
y=str((y1+y2)/2 - 10),
text_anchor="middle",
class_="relationship-text")
text.text = "M:N"
def _prettify(self, elem):
"""格式化XML输出"""
rough_string = ET.tostring(elem, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent=" ")
# 生成ER图
if __name__ == "__main__":
er_generator = ERDiagramGenerator()
er_generator.generate_svg_er_diagram('database_er_diagram')
print("操作说明:")
print("1. 在WPS文字或WPS演示中打开生成的SVG文件")
print("2. 右键点击图表 -> 选择'组合' -> '取消组合'")
print("3. 现在可以单独编辑每个元素")
print("4. 编辑完成后,可以重新组合或导出为其他格式")
```
## 生成的 ER 图说明
这个脚本会生成一个名为 `database_er_diagram.svg` 的文件,它包含了以下表结构及其关系:
### 表结构
1. **users**:用户表
2. **words**:单词表(包含自引用关系)
3. **senses**:词义表
4. **sense_examples**:词义示例表
5. **subtitles**:字幕表
6. **videos**:视频表
7. **user_words**:用户单词表
8. **user_ignored_words**:用户忽略单词表
9. **user_sentences**:用户句子表
10. **user_sentence_sense_ref**:用户句子词义引用表
### 主要关系
- **1:N 关系**:
- users → subtitles, videos, user_words, user_ignored_words, user_sentences, user_sentence_sense_ref
- videos → subtitles
- words → user_words, user_ignored_words, user_sentence_sense_ref
- senses → sense_examples, user_sentence_sense_ref
- sense_examples → user_sentence_sense_ref
- user_sentences → user_sentence_sense_ref
- words 自引用关系 (word_linked_id)
- **M:N 关系**:
- users ↔ words (通过 user_words 表)
## ER 图可视化特点
1. **清晰的表结构**:
- 每个表都有明显的标题栏
- 主键(PK)用红色标识
- 外键(FK)用蓝色标识,并显示引用表
- 普通列用黑色文本
2. **关系表示**:
- 1:N 关系用蓝色实线表示
- M:N 关系用红色虚线表示
- 自引用关系用绿色实线表示
- 所有关系都标注了关系类型(1:1, 1:N, M:N)
3. **布局优化**:
- users 表位于顶部中心位置
- 相关表围绕中心表分布
- 连接线使用曲线避免交叉
- 整体布局清晰易读
## 在 WPS 中编辑指南
1. **打开文件**:
- 在 WPS 文字或 WPS 演示中打开 `database_er_diagram.svg`
2. **取消组合**:
- 右键点击图表
- 选择"组合" → "取消组合"
- 现在可以单独选择和编辑每个元素
3. **编辑元素**:
- **移动表**:拖动表到新位置
- **修改文本**:双击文本进行编辑
- **更改样式**:右键选择"格式对象"修改颜色、字体等
- **调整关系线**:拖动关系线的控制点
4. **重新组合**:
- 选择所有元素(Ctrl+A)
- 右键 → "组合" → "组合"
5. **导出**:
- 文件 → 另存为 → 选择格式(如PDF、PNG等)
## 高级功能扩展
### 添加交互式预览
```python
def generate_html_preview(output_file='er_preview'):
"""生成带预览的HTML文件"""
with open(f"{output_file}.html", "w") as f:
f.write(f"""
<!DOCTYPE html>
<html>
<head>
<title>ER Diagram Preview</title>
<style>
body {{ font-family: Arial, sans-serif; }}
.container {{ width: 100%; text-align: center; }}
.instructions {{ margin-bottom: 20px; }}
</style>
</head>
<body>
<div class="container">
<h1>ER Diagram Preview</h1>
<div class="instructions">
<p>在WPS中打开此文件进行编辑: <a href="database_er_diagram.svg">database_er_diagram.svg</a></p>
<p>编辑说明: 右键点击图表 -> 组合 -> 取消组合</p>
</div>
<object data="database_er_diagram.svg" type="image/svg+xml" width="1200" height="900">
<img src="database_er_diagram.svg" alt="ER Diagram" />
</object>
</div>
</body>
</html>
""")
```
### 添加自动布局算法
```python
def _calculate_auto_layout(self):
"""使用力导向算法自动布局"""
positions = {}
# 简化的力导向布局
for i, table in enumerate(self.tables.keys()):
angle = 2 * math.pi * i / len(self.tables)
radius = 300
x = 600 + radius * math.cos(angle)
y = 450 + radius * math.sin(angle)
positions[table] = (x, y)
# 根据关系调整位置
for _ in range(5): # 迭代5次
for table1, relations in self.relationships.items():
for table2 in relations:
if table2 not in positions or table1 not in positions:
continue
x1, y1 = positions[table1]
x2, y2 = positions[table2]
# 计算距离
dx = x2 - x1
dy = y2 - y1
distance = max(math.sqrt(dx*dx + dy*dy), 1)
# 调整位置
if distance > 200:
factor = 10
positions[table2] = (
x2 - dx * factor / distance,
y2 - dy * factor / distance
)
return positions
```
### 添加数据库注释支持
```python
# 在表定义中添加注释
"users": {
"columns": [
{"name": "id", "type": "PK", "comment": "用户唯一标识"},
],
"comment": "系统用户表"
},
# 在生成SVG时显示注释
def _add_table_element(self, root, namespace, table_name, x, y):
# ...
# 添加表注释
if "comment" in table_info:
comment_elem = ET.SubElement(table_group, f"{{{namespace}}}text",
x=str(x + table_width/2),
y=str(y - 10),
text_anchor="middle",
class_="column-text")
comment_elem.text = table_info["comment"]
# ...
```
## 实际应用场景
1. **数据库设计文档**:创建可编辑的数据库设计文档
2. **团队协作**:在WPS中共享和共同编辑ER图
3. **系统评审**:在会议中展示数据库结构
4. **教学材料**:用于数据库设计教学
5. **系统维护**:帮助新开发人员理解数据库结构