Python语言提供的wordcloud词云功能,使文本数据的可视化,简单而美丽。但网上的大多数词云生成功能,多半没有可交互的GUI界面,使用起来稍觉不便。笔者结合网上的中文词云功能,以唐诗三百首,宋词三百首,宋诗三百首和元曲三百首为数据源,把展示的词云整合到Python语言的Tkinter GUI界面中,可以随时在唐诗宋词元曲间切换词云的数据来源,也可选择某一个作者的作品生成词云,为古诗词的文本分析,提供了方便的可视化工具。
如下是应用的界面,缺省显示唐诗三百首的词云:

切换作品,选择宋词三百首,点击生成词云:

从词云可以明显看出,唐诗和宋词的风格不一,主要的语词也不尽相同,但也有一定的相同之处,比如月,比如人,都是比较重要的词语。
也可以切换不同的作者,查看生成的词云。唐诗三百首,选李白:

李白喜欢月亮,经常邀月同饮;李白也自许甚高,所有我字也比较突出。
看看杜甫:
       
虽然也经常有月入诗,但已经没有李白那么突出了;而杜甫心忧天下,浑然忘我,词云中看不到我字。
上面演示了功能, 下面讲一下代码如何实现的。从网上找到唐诗三百首,宋词三百首,宋诗三百首及元曲三百首的文本,然后读入到Python的list中,list中的元素为自定义PoetryWork类:
class PoetryWork(object):
     def __init__(self, num, title, author, content):
         self.num = num
         self.title = title
         self.author = author
         self.content = content
     def get_num(self):
         return self.num
     
     def get_author(self):
         return self.author
     
     def get_title(self):
         return self.title
     
     def get_content(self):
         return self.content
如下为加载唐诗三百首的代码:
#加载唐诗三百首
 def load_tang_shi():
     tang_poet_list = {}
     num = ''
     title = ''
     author = ''
     content = []
     pattern = r'\d{3}'
     with open('唐诗三百首.txt', 'r', encoding= 'utf-8') as f:
         for line in f.readlines():
             if line:
                 if re.match(pattern, line):
                     #内容不空
                     if content:
                         tang_poetry_list.append(PoetryWork(num, title, author, content))
                     pos = line.index(':')
                     num = line[:3]
                     author = line[3:pos]
                     if author in tang_poet_list:
                         tang_poet_list[author] += 1
                     else:
                         tang_poet_list[author] = 1
                         
                     title = line[pos+1:]
                     content = []
                     #print('num:{},author:{},title:{}'.format(num, author, title))
                 else:
                     if line.strip():
                         content.append(line.strip())
                     
     tang_poetry_list.append(PoetryWork(num, title, author, content))    
     tang_poet_list = dict(sorted(tang_poet_list.items(),key=lambda x:x[1], reverse=True))
     poet_list = [x for x in tang_poet_list.keys()]  
     poet_list.insert(0,'all')
     
     return poet_list
在加载过程中对唐诗按作者进行了统计,最后按作品数量倒序输出返回作者列表,填充界面上作者的下拉框。
加载宋词宋诗和元曲的代码大体类似。
通常的词云展示都是用Matplotlib的控件方法,那样的化就没法整合到Tkinter的GUI界面中了,也不方便与用户进行交互。笔者参考网上代码,采用matplotlib的FigureCanvasTkAgg canvas画布功能,把生成的云图嵌入。
首先是声明matplotlib绘图的Figure和canvas画布对象:
curRow = 2
 fig = Figure(figsize=(5, 4), dpi=100)
 canvas = FigureCanvasTkAgg(fig, master=win)  # A tk.DrawingArea.
 canvas.get_tk_widget().pack(side=tkinter.BOTTOM, fill=tkinter.BOTH, expand=1)
然后在生成词云后,把词云嵌入进去:
def generateCloud(*args):
     """清除原有图表,生成新的图表"""
     global fig,canvas
     fig.clear()
    ax = fig.add_subplot(111)
     content = []
     cur_author = poetList.get()
     if workList.get() == '唐诗三百首':
         for work in tang_poetry_list:
             if cur_author == 'all' or cur_author == work.get_author():
                 content.extend(work.get_content())
     elif workList.get() == '宋诗三百首':
         for work in song_poetry_list:
             if cur_author == 'all' or cur_author == work.get_author():
                 content.extend(work.get_content())
     elif workList.get() == '宋词三百首':
         for work in song_poem_list:
             if cur_author == 'all' or cur_author == work.get_author():
                 content.extend(work.get_content())
     elif workList.get() == '元曲三百首':
         for work in yuan_verse_list:
             if cur_author == 'all' or cur_author == work.get_author():
                 content.extend(work.get_content())   
     else: #佛诗三百首
         for work in buddhist_poetry_list:
             if cur_author == 'all' or cur_author == work.get_author():
                 content.extend(work.get_content())  
                 
     res = jieba.lcut(" ".join(content))  # 中文分词
     text = " ".join(res)  # 用空格连接所有的词
     mask = np.array(Image.open("chinamap.png"))  # 指定词云图效果
     # 创建词云对象
     wc = WordCloud(width=800, height=600, mask=mask).generate(text)
     ax.imshow(wc) # 显示词云图
     ax.axis("off")
  
     wc.to_file("{}_{}_wordcloud.png".format(workList.get(), cur_author))  # 保存成图片
     
     canvas.draw()
重点是这两行代码:
   # 创建词云对象
     wc = WordCloud(width=800, height=600, mask=mask).generate(text)
     ax.imshow(wc) # 显示词云图
最后是canvas.draw().
要显示中文词云,还需要解决中文字体的问题。按网上的方法,可以在生成词云时,加入参数:font_path="simhei.ttf"。我这里使用的Miniconda3,加入字体路径报错,改成绝对路径还是报错。就采用了另一种方法,直接修改词云源码wordcloud.py里面的缺省字体设置:(全局搜wordcloud.py文件):
FONT_PATH = os.environ.get('FONT_PATH', os.path.join(FILE, 'DroidSansMono.ttf'))
将系统的缺省英文字体DroidSansMono.tff修改为别的中文字体即可。
我按照网上的建议先是改成simhei.ttf,乱码依旧。后来改成msyh.ttc微软雅黑字体,就好了。但是simhei.ttf这种字体在我的电脑上是正常安装了的,不知道为啥wordcloud找不着。
本文所有代码及资源文件都已上传github:GitHub - yangdanbo/PoetryCloud


















