3
\$\begingroup\$

Module: https://amueller.github.io/word_cloud/

I really enjoyed the python WordCloud module, so I wanted to give it a GUI so I could share it with family and friends. I also wanted a platform where one could generate their own WordClouds within their own pictures, without word limitations (like many of the online wordcloud programs).

The GUI provides a number of options to modify the WordCloud, and instructions are presented via a tooltip. I've also added some additional advanced options (things I think might be a bit less intuitive, but are still there if the user wants to modify them).

I'm basically looking for feedback on my code structure, and specifically how I implement modifications of the various arguments. This is my first time making a window within a window in Tkinter, and I had to use various globals to try and get it to work properly (which I know is a big no no).

#main.py
import tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
import os
from wordcloud import WordCloud,STOPWORDS
from collections import Counter
from PIL import Image as img
from tkinter import filedialog
from tkinter import *
from collections import Counter
import traceback

root = tk.Tk()
root.title('Word Cloud Generator')
root.geometry('600x600')

class CreateToolTip(object):
    """
    create a tooltip for a given widget
    """
    def __init__(self, widget, text='widget info'):
        self.waittime = 100     #miliseconds
        self.wraplength = 280   #pixels
        self.widget = widget
        self.text = text
        self.widget.bind("<Enter>", self.enter)
        self.widget.bind("<Leave>", self.leave)
        self.widget.bind("<ButtonPress>", self.leave)
        self.id = None
        self.tw = None

    def enter(self, event=None):
        self.schedule()

    def leave(self, event=None):
        self.unschedule()
        self.hidetip()

    def schedule(self):
        self.unschedule()
        self.id = self.widget.after(self.waittime, self.showtip)

    def unschedule(self):
        id = self.id
        self.id = None
        if id:
            self.widget.after_cancel(id)

    def showtip(self, event=None):
        x = y = 0
        x, y, cx, cy = self.widget.bbox("insert")
        x += self.widget.winfo_rootx() + 25
        y += self.widget.winfo_rooty() + 20
        # creates a toplevel window
        self.tw = tk.Toplevel(self.widget)
        # Leaves only the label and removes the app window
        self.tw.wm_overrideredirect(True)
        self.tw.wm_geometry("+%d+%d" % (x, y))
        label = tk.Label(self.tw, text=self.text, justify='left',
                       background="#ffffff", relief='solid', borderwidth=1,
                       wraplength = self.wraplength)
        label.pack(ipadx=1)

    def hidetip(self):
        tw = self.tw
        self.tw= None
        if tw:
            tw.destroy()

label0=tk.Label(root, text="Hover over line for instructions")
label0.grid(row=0)
label1=tk.Label(root, text="Load File")
label1.grid(row=1)
label1_tooltip=CreateToolTip(label1,'Load a text file containing your words')
label2=tk.Label(root, text="Words")
label2.grid(row=2)
label2_tooltip=CreateToolTip(label2,'Type in the words you want in your wordcloud, the more words, the better the word cloud will look. \n Words are case-sensitive (i.e. The is different form the)\n Separate words with space')
label3=tk.Label(root, text="Frequency of word")
label3.grid(row=3)
label3=CreateToolTip(label3,'This signifies the size of the above words. The more frequent a word, the larger it will be.\n Type in a number that represents the size of the word \n'
'The larger the number, the larger the word. I.E. Family Love Health in order of size, would be 200 100 50')
label4=tk.Label(root, text="Background Color")
label4.grid(row=4)
label4=CreateToolTip(label4,'Background Color of the Word Cloud, Default is white')
label5=tk.Label(root, text="Wordcloud Width")
label5.grid(row=5)
label5=CreateToolTip(label5,'The higher this number, the better the resolution of the words within your word cloud. Default is 400')
label6=tk.Label(root, text="Wordcloud Height")
label6.grid(row=6)
label6=CreateToolTip(label6,'Same as the width. Default is 400')
label7=tk.Label(root, text="Window Width")
label7.grid(row=7)
label7=CreateToolTip(label7,'Size of the WordCloud picture. \nThe higher the number, the larger the figure. Default is 5')
label8=tk.Label(root, text="Window Height")
label8.grid(row=8)
label8=CreateToolTip(label8,'Same as width. Default is 5')
label9=tk.Label(root, text="Words not to use")
label9.grid(row=9)
label9=CreateToolTip(label9,'A list of words to ignore. \nSeparate multiple words with space. I.E. Hello Goodbye.\nWords are case-sensitive (i.e. The is different form the)')
tk.Label(root, text="Image Options").grid(row=10)
label10=tk.Label(root, text="Load Image")
label10.grid(row=11)
label10=CreateToolTip(label10,'Image to insert the wordcloud into. \n Use photos that have a white background, and black filling where you want the words to be in.')
label11=tk.Label(root, text="Image Outline Thickness")
label11.grid(row=12)
label11=CreateToolTip(label11,'This defines the outline of the image. Default is 3')
label12=tk.Label(root, text="Image Outline Color")
label12.grid(row=13)
label12=CreateToolTip(label12,'The color of the outline. Default is black')


load_file=tk.Entry(root)
load_file.grid(row=1, column=1,sticky=W)
word_list=tk.Entry(root)
word_list.grid(row=2, column=1,ipadx=100,sticky=W+E)
multiplyer=tk.Entry(root)
multiplyer.grid(row=3, column=1,ipadx=100,sticky=W+E)
background_color_input=tk.Entry(root)
background_color_input.grid(row=4,column=1,sticky=W)
width_input=tk.Entry(root)
width_input.grid(row=5,column=1,sticky=W)
height_input=tk.Entry(root)
height_input.grid(row=6,column=1,sticky=W)
window_width_input=tk.Entry(root)
window_width_input.grid(row=7,column=1,sticky=W)
window_height_input=tk.Entry(root)
window_height_input.grid(row=8,column=1,sticky=W)
stopwords_input=tk.Entry(root)
stopwords_input.grid(row=9,column=1,sticky=W)
image_input=tk.Entry(root)
image_input.grid(row=11,column=1,sticky=W)
contour_width_input=tk.Entry(root)
contour_width_input.grid(row=12,column=1,sticky=W)
contour_color_input=tk.Entry(root)
contour_color_input.grid(row=13,column=1,sticky=W)

text=()
text_directory=()
image=()
image_directory=()


def input_file():
    fullpath = filedialog.askopenfilename(parent=root, title='Choose a file')
    global text_directory
    global text
    text_directory=os.path.dirname(fullpath)
    text= os.path.basename(fullpath)
    label2=Label(root,text=fullpath).grid(row=1,column=1,sticky=W)

def image_file():
    fullpath = filedialog.askopenfilename(parent=root, title='Choose a file')
    global image
    global image_directory
    image_directory=os.path.dirname(fullpath)
    image= os.path.basename(fullpath)
    label2=Label(root,text=fullpath).grid(row=11,column=1,sticky=W)

new_top_flag=False
new_top=None

def run():
    print('Program is Running')
    try:
        global new_top
        stopwords = set(STOPWORDS)
        width_value,height_value,background_colors,contour_color,contour_width,stopwords,window_width,window_height = defaults(stopwords)
        if new_top_flag != False:
            max_words,min_font_size,max_font_size,mode,relative_scaling=new_top.values()
        else:
            max_words,min_font_size,max_font_size,mode,relative_scaling=200,4,None,'RGB','auto'
        if word_list.get() != '':
            inputed_words=[]
            words_typed=word_list.get()
            multiplyer_value=multiplyer.get()
            for words,number in zip(words_typed.split(),multiplyer_value.split()):
                inputed_words+=((words+',')*int(number)).split(',')
            word_could_dict=Counter(inputed_words)
            if image == ():
                wc= WordCloud(width=width_value, height=height_value,background_color=background_colors,stopwords=stopwords,max_words=max_words,min_font_size=min_font_size,max_font_size=max_font_size,mode=mode,relative_scaling=relative_scaling).generate_from_frequencies(word_could_dict)
            else:
                os.chdir(image_directory)
                new_mask=np.array(img.open(image))
                wc= WordCloud(width=width_value, height=height_value,background_color=background_colors,stopwords=stopwords,mask=new_mask,contour_color=contour_color,contour_width=contour_width,max_words=max_words,min_font_size=min_font_size,max_font_size=max_font_size,mode=mode,relative_scaling=relative_scaling).generate_from_frequencies(word_could_dict)
            wc.to_file('savefile.png')
            plt.figure(figsize = (window_width,window_height))
            plt.imshow(wc, interpolation="bilinear")
            plt.tight_layout(pad=0)
            plt.axis("off")
            plt.show()
        else:
            os.chdir(text_directory)
            text_file = (open(text,encoding="utf8" ).read()).lower()
            if image ==():
                wc= WordCloud(width=width_value, height=height_value,background_color=background_colors,stopwords=stopwords,max_words=max_words,min_font_size=min_font_size,max_font_size=max_font_size,mode=mode,relative_scaling=relative_scaling).generate(text_file)
            else:
                os.chdir(image_directory)
                new_mask=np.array(img.open(image))
                wc= WordCloud(width=width_value, height=height_value,background_color=background_colors,stopwords=stopwords,mask=new_mask,contour_color=contour_color,contour_width=contour_width,max_words=max_words,min_font_size=min_font_size,max_font_size=max_font_size,mode=mode,relative_scaling=relative_scaling).generate(text_file)
            wc.to_file('savefile.png')
            plt.figure(figsize = (window_width,window_height))
            plt.imshow(wc, interpolation="bilinear")
            plt.tight_layout(pad=0)
            plt.axis("off")
            plt.show()
        new_top=None
    except:
        print('Error Running Program. Report below error report\n')
        traceback.print_exc()

def defaults(stopwords):
    if width_input.get() == '':
        width_value=400
    else:
        width_value=int(width_input.get())
    if height_input.get() == '':
        height_value=400
    else:
        height_value=int(height_input.get())
    if stopwords_input.get() != '':
        for values in (stopwords_input.get()).split():
            stopwords.add(values)
    if background_color_input.get() == '':
        background_colors='white'
    else:
        background_colors=background_color_input.get()
    if contour_color_input.get() == '':
        contour_color='black'
    else:
        contour_color=contour_color_input.get()
    if contour_width_input.get() == '':
        contour_width=3
    else:
        contour_width=int(contour_width_input.get())
    if window_width_input.get() == '':
        window_width = 5
    else:
        window_width=int(window_width_input.get())
    if window_height_input.get() == '':
        window_height=5
    else:
        window_height=int(window_height_input.get())
    return width_value,height_value,background_colors,contour_color,contour_width,stopwords,window_width,window_height

def new_window():
    global new_top
    global new_top_flag
    from advanced import newTopLevel
    new_top = newTopLevel(root)
    new_top_flag=True
    newWindow = new_top.newWindow


tk.Button(root,text='browse',command=input_file).grid(row=1,column=2)
tk.Button(root,text='browse',command=image_file).grid(row=11,column=2)
tk.Button(root,text='Advanced Settings',command=new_window).grid(row=34,column=1)
tk.Button(root,text='run',command=run).grid(row=14,column=1,sticky=W)


root.mainloop()
#advanced.py
from tkinter import *
import tkinter as tk

class CreateToolTip(object):
    """
    create a tooltip for a given widget
    """
    def __init__(self, widget, text='widget info'):
        self.waittime = 100     #miliseconds
        self.wraplength = 280   #pixels
        self.widget = widget
        self.text = text
        self.widget.bind("<Enter>", self.enter)
        self.widget.bind("<Leave>", self.leave)
        self.widget.bind("<ButtonPress>", self.leave)
        self.id = None
        self.tw = None

    def enter(self, event=None):
        self.schedule()

    def leave(self, event=None):
        self.unschedule()
        self.hidetip()

    def schedule(self):
        self.unschedule()
        self.id = self.widget.after(self.waittime, self.showtip)

    def unschedule(self):
        id = self.id
        self.id = None
        if id:
            self.widget.after_cancel(id)

    def showtip(self, event=None):
        x = y = 0
        x, y, cx, cy = self.widget.bbox("insert")
        x += self.widget.winfo_rootx() + 25
        y += self.widget.winfo_rooty() + 20
        # creates a toplevel window
        self.tw = tk.Toplevel(self.widget)
        # Leaves only the label and removes the app window
        self.tw.wm_overrideredirect(True)
        self.tw.wm_geometry("+%d+%d" % (x, y))
        label = tk.Label(self.tw, text=self.text, justify='left',
                       background="#ffffff", relief='solid', borderwidth=1,
                       wraplength = self.wraplength)
        label.pack(ipadx=1)

    def hidetip(self):
        tw = self.tw
        self.tw= None
        if tw:
            tw.destroy()

max_words=0
min_font_size=0
max_font_size=0
mode=0
relative_scaling=0

class newTopLevel(object):
    def __init__(self, root):
        self.newWindow = Toplevel(root)
        self.newWindow.title("Advanced")
        self.newWindow.geometry("400x400")
        label1=tk.Label(self.newWindow, text="Max words")
        label1.grid(row=0)
        label1_tooltip=CreateToolTip(label1,'The maximum number of words. Default is 200')
        label2=tk.Label(self.newWindow, text="Min Font Size")
        label2.grid(row=1)
        label2_tooltip=CreateToolTip(label2,'The smallest font size to use. Word Cloud will not generate any words smaller than this \n Default is 4')
        label3=tk.Label(self.newWindow, text="Max Font Size")
        label3.grid(row=2)
        label3_tooltip=CreateToolTip(label3,'The maximum font size for the largest word')
        label4=tk.Label(self.newWindow, text="Mode")
        label4.grid(row=3)
        label4_tooltip=CreateToolTip(label4,'If a transparent background is desired, type in RGBA, and change the background to None')
        label5=tk.Label(self.newWindow, text="Relative Scaling")
        label5.grid(row=4)
        label5_tooltip=CreateToolTip(label5,'A scale of 0, only word ranks are considered. A scale of 1,the more frequent word will be 2x the size as before.\n A scale of 0.5 uses word frequencies and rank. Defaulse is 0.5')

        self.max_word_input=tk.Entry(self.newWindow)
        self.max_word_input.grid(row=0,column=1)
        self.min_font_input=tk.Entry(self.newWindow)
        self.min_font_input.grid(row=1,column=1)
        self.max_font_input=tk.Entry(self.newWindow)
        self.max_font_input.grid(row=2,column=1)
        self.mode_input=tk.Entry(self.newWindow)
        self.mode_input.grid(row=3,column=1)
        self.scaling_input=tk.Entry(self.newWindow)
        self.scaling_input.grid(row=4,column=1)

        tk.Button(self.newWindow,text='Apply Settings',command=self.input_values).grid(row=7,column=1)
        tk.Button(self.newWindow,text='Restore Default',command=self.input_values).grid(row=7,column=2)

    def input_values(self):
        global max_words
        global min_font_size
        global max_font_size
        global mode
        global relative_scaling
        if self.max_word_input.get() == '':
            max_words=200
        else:
            max_words=int(self.max_word_input.get())
        if self.min_font_input.get() == '':
            min_font_size=4
        else:
            min_font_size=int(self.min_font_input.get())
        if self.max_font_input.get() == '':
            max_font_size=None
        else:
            max_font_size=int(self.max_font_input.get())
        if self.mode_input.get() == '':
            mode='RGB'
        else:
            mode=self.mode_input.get()
        if self.scaling_input.get() == '':
            relative_scaling='auto'
        else:
            relative_scaling=float(self.scaling_input.get())
    def values(self):
        return max_words,min_font_size,max_font_size,mode,relative_scaling
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.