文章目录
参数字典
自从做了热榜的词云之后,就越来越觉得词云的表达力真的很强,所以合计是不是可以为WordCloud做一个界面,来更加直观地操作。
既然以WordCloud为核心,那么界面的组件自然要和WordCloud的参数意义绑定,每个参数在界面中都对应一个提示组件,比如Lable,和一个输入组件,比如Entry。此外,这些参数都是可设的,那么在对应一个组件的同时,也应该对应一个StringVar。
简单起见,现在只挑选几个必备的参数,来演示这个逻辑的实现方法
参数 | 说明 | 适用组件 | 参数 |
---|---|---|---|
width | 词云宽度 | Spinbox | 100-2000, 10 |
height | 词云高度 | Spinbox | 100-2000, 10 |
scale | 缩放 | Spinbox | 0.5-10, 0.1 |
输入文本 | 路径对话框 | ||
输出词云 | 路径对话框 | ||
font_path | 字体文件路径 | 路径对话框 |
将这些参数封装成一个字典,内容如下
python
wcDct = {
"词云宽度" : {
"Ctrl": ttk.Spinbox,
"paras" : {"from_":100, "to":2000, "increment":10},
"default":800,
"call" : "width"},
"词云宽度" : {
"Ctrl": ttk.Spinbox,
"paras" : {"from_":100, "to":2000, "increment":10},
"default":450,
"call" : "height"},
"图像缩放" : {
"Ctrl": ttk.Spinbox,
"paras" : {"from_":0.5, "to":10, "increment":0.1},
"default":1,
"call" : "scale"},
"输入路径" : {"Ctrl": DialogButton},
"输出路径" : {"Ctrl": DialogButton},
"字体路径" : {"Ctrl": DialogButton, "call" : "font_path"},
}
其中,DialogButton是自定义的一个组件,主要包括一个按钮和一个输入框,点击按钮可以弹出文件对话框。
布局
有了这个字典,就可以据此来布局了
python
class DrawWords(ttk.Frame):
def __init__(self, master, **options):
super().__init__(master, **options)
self.pack()
self.words = None
self.initWidgets()
def initWidgets(self):
frm = ttk.Frame(self)
frm.pack(side=tk.LEFT, fill=tk.Y)
self.initPara(frm)
def setOneSpinBox(self, frm, key):
ttk.Label(frm, width=8, text=key).pack(side=tk.LEFT)
v = wcDct[key] # 组件参数
n = v["call"] # 调用名
self.spins[n] = v["ctrl"](frm, **v["paras"])
self.spins[n].set(v["default"])
self.spins[n].pack(side=tk.LEFT)
def setOneDiaButton(self, frmPara, key):
frm = ttk.Frame(frmPara)
frm.pack(side=tk.TOP, fill=tk.X)
ttk.Label(frm, width=8, text=key).pack(side=tk.LEFT)
v = wcDct[key]
n = v["call"] if 'call' in v else key
self.paths[n] = v["ctrl"](frm, **v['paras'])
self.paths[n].pack(side=tk.LEFT)
if 'default' in v:
self.paths[n].set(v['default'])
def initPara(self, frmPara):
self.spins = {}
frm = ttk.Frame(frmPara)
frm.pack(side=tk.TOP, fill=tk.X)
for key in ["词云宽度", "词云高度"]:
self.setOneSpinBox(frm, key)
self.paths = {}
for key in ["输入路径", "输出路径", "字体路径"]:
self.setOneDiaButton(frmPara, key)
frm = ttk.Frame(frmPara)
frm.pack(side=tk.TOP, fill=tk.X)
ttk.Button(frm, text="输出词云",
command=self.genWordCloud).pack(side=tk.LEFT)
在布局过程中,对SpinBox类型的变量和DialogButton型的变量分别做了不同的处理,并且为它们分别设计了全局变量字典self.spins和self.paths,以便于调用。
布局结果如下
其中,genWordCloud用于词云输出,考虑到分词的准确性,这里仍然采用jieba进行分词,并为分词专门设计一个函数,其最终实现如下。
python
def splitWords(self):
p = self.paths["输入路径"].get()
with open(p, encoding='utf8') as f:
text = f.read()
words = jieba.lcut(text)
self.words = [w for w in words if len(w)>1] # 取出长度大于1的词
def genWordCloud(self):
dct = {}
for key in ['width', 'height', 'font_path']:
if key in self.spins:
dct[key] = int(self.spins[key].get())
if key in self.paths:
dct[key] = self.paths[key].get()
cloud = WordCloud(**dct)
self.splitWords()
cloud.generate(" ".join(self.words))
p = self.paths["输出路径"].get()
if not (p.endswith('.png') or p.endswith('.svg')):
p = p+".png"
cloud.to_file(p)
测试结果
最后,按照惯例,做一下测试
python
if __name__ == "__main__":
root = tk.Tk()
DrawWords(root).pack(side=tk.TOP, fill=tk.BOTH)
root.mainloop()
效果如下