Example #1
0
class Clustering_Gui:
    """ Responsible for the display of all UI and input validity """
    def __init__(self, master):
        """" Initialization of all parameters of the class, buttons, labels """
        self.master = master
        master.title("K Means Clustering")
        master.minsize(750, 400)

        self.num_of_clusters = 0
        self.num_of_runs = 0
        self.cluster_ok = False
        self.runs_ok = False
        self.cluster = Clustering()

        self.file_path = StringVar()
        self.file_name_entered = Entry(master,
                                       width=100,
                                       textvariable=self.file_path)
        self.file_name_entered.grid(column=0, columnspan=3, row=0)
        self.file_path.trace('w', self.showPreProcessButton)

        self.button = Button(master,
                             text="Browse A File",
                             command=self.fileDialog)
        self.button.grid(column=3, row=0, pady=5)

        self.error_file_path = StringVar()
        self.error_file_label = Label(master,
                                      textvariable=self.error_file_path,
                                      foreground="red",
                                      width=40)
        self.error_file_label.grid(column=0, row=1, pady=10)

        self.num_cluster_label = Label(master, text="Number of clusters k")
        self.num_cluster_label.grid(column=0, row=2)

        self.num_cluster_text = StringVar()
        self.num_cluster_text.set(self.num_of_clusters)
        self.num_cluster_entry = Entry(master,
                                       width=15,
                                       textvariable=self.num_cluster_text)
        self.num_cluster_entry.grid(column=1, row=2)
        self.num_cluster_text.trace('w', self.validate_cluster)

        self.error_cluster_text = StringVar()
        self.error_cluster_label = Label(master,
                                         textvariable=self.error_cluster_text,
                                         foreground="red",
                                         width=40)
        self.error_cluster_label.grid(column=2, row=2)

        self.num_runs_label = Label(master, text="Number of runs")
        self.num_runs_label.grid(column=0, row=3)

        self.num_runs_text = StringVar()
        self.num_runs_text.set(self.num_of_runs)
        self.num_runs_entry = Entry(master,
                                    width=15,
                                    textvariable=self.num_runs_text)
        self.num_runs_entry.grid(column=1, row=3)
        self.num_runs_text.trace('w', self.validate_runs)

        self.error_run_text = StringVar()
        self.error_run_label = Label(master,
                                     textvariable=self.error_run_text,
                                     foreground="red",
                                     width=40)
        self.error_run_label.grid(column=2, row=3)

        self.prep_button = Button(master,
                                  text="Pre-process",
                                  command=self.call_preprocess,
                                  state='disabled')
        self.prep_button.grid(column=0, row=4, pady=20)
        self.cluster_button = Button(master,
                                     text="Cluster",
                                     state='disabled',
                                     command=self.call_cluster)
        self.cluster_button.grid(column=1, row=4, pady=20)

        self.scatter_pic_label = Label(master)
        self.scatter_pic_label.grid(column=0, row=5, pady=10)

        self.world_map_label = Label(master)
        self.world_map_label.grid(column=1, row=5, pady=10)

    def validate_runs(self, *args):
        """" Validate the input variable - number of runs - and display messages to the user accordingly """
        if (self.num_runs_text.get()):
            try:
                self.num_of_runs = int(self.num_runs_text.get())
                if self.num_of_runs <= 0:
                    self.error_run_text.set('Enter a number greater than zero')
                    self.runs_ok = False
                    self.cluster_button.config(state='disabled')
                elif self.num_of_runs >= 50:
                    self.error_run_text.set('Enter a number smaller than 50')
                    self.runs_ok = False
                    self.cluster_button.config(state='disabled')
                else:
                    self.error_run_text.set('')
                    self.runs_ok = True
                    self.showClusterButton()
            except ValueError:  # problem
                self.runs_ok = False
                self.error_run_text.set('Enter only numbers')
                self.cluster_button.config(state='disabled')

        else:  # empty
            self.runs_ok = False
            self.error_run_text.set('')
            self.cluster_button.config(state='disabled')

    def validate_cluster(self, *args):
        """" Validate the input variable - number of clusters - and display messages to the user accordingly """
        if self.num_cluster_text.get():
            try:
                self.num_of_clusters = int(self.num_cluster_text.get())
                if self.num_of_clusters <= 1:
                    self.cluster_ok = False
                    self.error_cluster_text.set(
                        'Enter a number greater than 1')
                    self.cluster_button.config(state='disabled')
                elif self.num_of_clusters > 164:
                    self.cluster_ok = False
                    self.error_cluster_text.set(
                        'Enter a number smaller than 165')
                    self.cluster_button.config(state='disabled')
                else:
                    self.cluster_ok = True
                    self.error_cluster_text.set('')
                    self.showClusterButton()

            except ValueError:
                self.cluster_ok = False
                self.error_cluster_text.set('Enter only numbers')
                self.cluster_button.config(state='disabled')
        else:
            self.cluster_ok = False
            self.error_cluster_text.set('')
            self.cluster_button.config(state='disabled')

    def fileDialog(self):
        """" Input the data file into a variable and save its path in the file path field. """
        file_path = filedialog.askopenfilename(initialdir="/",
                                               title="Select A File",
                                               filetype=(("xlsx files",
                                                          "*.xlsx"),
                                                         ("all files", "*.*")))
        self.file_path.set(file_path)
        self.showPreProcessButton()

    def showClusterButton(self, *args):
        """" Checking whether to enable "clustering" button-
            The number of runs and the number of threads entered by the user is correct"""
        if self.runs_ok & self.cluster_ok:
            self.cluster_button.config(state='normal')
        else:
            self.cluster_button.config(state='disabled')

    def showPreProcessButton(self, *args):
        """" Checking whether to enable "pre-processing" button-
            The file path contains an existing and empty file"""
        self.prep_button.config(state='disabled')

        if self.file_path.get():
            try:
                if os.stat(self.file_path.get()).st_size == 0:
                    self.error_file_path.set('This file is empty')
                else:
                    self.error_file_path.set('')
                    self.prep_button.config(state='normal')
            except FileNotFoundError:
                self.error_file_path.set('File not found')
            except Exception:
                self.error_file_path.set('Please choose a different file')

    def call_preprocess(self, *args):
        """" call preProcess function in Cluster class.
         Operates a popup if the data cleaning process was successful or not """
        if (self.cluster.preProcess(self.file_path.get())):
            messagebox.showinfo("K Means Clustering",
                                "Pre-Processing completed successfully!")
        else:
            messagebox.showerror(
                "K Means Clustering",
                "There was a problem running the Pre-Processing")

    def call_cluster(self, *args):
        """" call clustering function in Cluster class.
         Operates a popup if the data clustering was successful or not """
        if (self.cluster.clustering(self.num_of_clusters, self.num_of_runs)):
            # show plots
            scatterImg = PIL.Image.open("scatter.png").resize(
                (350, 300), PIL.Image.ANTIALIAS)
            self.scatter_pic = ImageTk.PhotoImage(scatterImg)
            self.scatter_pic_label.config(image=self.scatter_pic)

            worldImg = PIL.Image.open("map.png").resize((350, 300),
                                                        PIL.Image.ANTIALIAS)
            self.world_pic = ImageTk.PhotoImage(worldImg)
            self.world_map_label.config(image=self.world_pic)

            response = messagebox.askquestion(
                "K Means Clustering ",
                "Clustering completed successfully! Would you like to exit the program?",
                icon='warning')

            if response == "yes":
                self.master.destroy()

        else:
            messagebox.showerror("K Means Clustering",
                                 "There was a problem running the clustering")