class MainApplication(tk.Frame): """ The main frame of the GUI This class is the main component of the GUI. Its main purpose is managing one plot and displaying useful information related to it. It contains several buttons, entries and a PlotData widget used to display data. Attributes ---------- BTN_HEIGHT : 30 The height value that is used for all the buttons inside this class BTN_WIDTH : 90 The width value that is used for all the buttons inside this class parent : The parent window (or widget) plot : Plot The plot managed by this class plot_data : PlotData The widget that displays data related to the plot new_plot_btn : Button The "New plot" button that creates the plot managed by this class x_label : Label A lebel which contains the text "X: " x_entry : Entry An entry in which you can type the x coordinate value for a new point y_label : Label A lebel which contains the text "Y: " x_entry : Entry An entry in which you can type the y coordinate value for a new point add_point_btn : Button The "Add point" button, which adds a point to the plot only if the values inside x_entry and y_entry are numbers add_multiple_points_check : Checkbutton The "Add multiple points" check button, which lets you add points on the plot by clicking inside the coordinate system only if it is activated check_state : BooleanVar Stores the state of add_multiple_points_check check button. update_line_btn : Button The "Update line" button, which stretches the best fitting line to match the plot's coordinate system size Methods ------- create_plot_data() Returns a PlotData widget which will store the points inside the plot and the alpha and beta coefficients of the best fitting line. reset_plot_data() Resets the data inside the plot_data widget (alpha=None, beta=None, all points are deleted). create_new_plot() Assigns a new Plot instance to the plot attribute. remove_closed_plot() Disconnects the on_click callback from the plot and assigns "None" to the plot attribute. new_plot_clb() A callback assigned to the new_plot_btn ("New plot") button It checks for already existing but closed plots, creates a new one and resets the data inside plot_data widget. create_new_plot_btn() Returns a new new_plot_btn ("New plot") button. create_x_label() Returns the "X: " label. create_x_entry() Returns the entry used to get the x coordinate value for the point added by the add_point_btn ("Add point") button. create_y_label() Returns the "Y: " label. create_y_entry() Returns the entry used to get the y coordinate value for the point added by the add_point_btn ("Add point") button. add_point_clb() A callback assigned to the add_point_btn ("Add point") button First of all, it checks if the values of x_entry and y_entry are numbers. If those values are numbers it adds a point to the plot and plot_data, otherwise prints a message in the console. create_add_point_btn() Returns a new add_point_btn ("Add point") button. add_multiple_points_clb() A callback assigned to the add_multiple_points_check ("Add multiple points") check button. It updates check_state everytime the check button changes its state. create_add_multiple_points_check() Returns a new add_multiple_points_check ("Add multiple points") check button. update_line_clb() A callback assigned to the update_line_btn ("Update line") button. It stretches the best fitting line to match the plot's coordinate system size. create_update_line_btn() Returns a new update_line_btn ("Update line") button. is_number(s) Returns True if the "s" string is a float number, False otherwise. """ BTN_HEIGHT = 30 BTN_WIDTH = 90 def __init__(self, parent, **args): """ Parameters ---------- parent : The parent window (or widget) **args : A list of options used by the LabelFrame widget (bg, height, width, text, etc.), which are passed to the LabelFrame __init__ function. """ super().__init__(parent, args) self.parent = parent self.plot = None self.plot_data = self.create_plot_data() self.new_plot_btn = self.create_new_plot_btn() self.x_label = self.create_x_label() self.x_entry = self.create_x_entry() self.y_label = self.create_y_label() self.y_entry = self.create_y_entry() self.add_point_btn = self.create_add_point_btn() self.add_multiple_points_check, self.check_state = self.create_add_multiple_points_check() self.update_line_btn = self.create_update_line_btn() def create_plot_data(self): plot_data = PlotData(self, height=PlotData.HEIGHT, width=PlotData.WIDTH, bg="white") plot_data["text"] = "Plot data: " plot_data.place(x=10, y=165) return plot_data def reset_plot_data(self): self.plot_data.reset() def create_new_plot(self): self.plot = Plot(self) self.plot.update_check_state(self.check_state.get()) def remove_closed_plot(self): if self.plot is not None and not plt.fignum_exists(self.plot.fig.number): self.plot.disconnect_on_click_clb() self.plot = None def new_plot_clb(self): self.remove_closed_plot() self.create_new_plot() self.reset_plot_data() def create_new_plot_btn(self): new_plot_btn = Button(self, text="New plot", command=self.new_plot_clb, padx=5) new_plot_btn.place(x=10, y=10, height=MainApplication.BTN_HEIGHT, width=MainApplication.BTN_WIDTH) return new_plot_btn def create_x_label(self): x_label = Label(self, text="X: ", bg="white") x_label.place(x=110, y=50, height=MainApplication.BTN_HEIGHT) return x_label def create_x_entry(self): x_entry = Entry(self, width=10) x_entry.place(x=130, y=50, height=MainApplication.BTN_HEIGHT) return x_entry def create_y_label(self): y_label = Label(self, text="Y: ", bg="white") y_label.place(x=200, y=50, height=MainApplication.BTN_HEIGHT) return y_label def create_y_entry(self): y_entry = Entry(self, width=10) y_entry.place(x=220, y=50, height=MainApplication.BTN_HEIGHT) return y_entry def add_point_clb(self): strx = self.x_entry.get() stry = self.y_entry.get() self.x_entry.delete(0, len(strx)) self.y_entry.delete(0, len(stry)) if not self.is_number(strx) or not self.is_number(stry): print("Please type valid numbers") else: x = float(strx) y = float(stry) self.plot.add_point(x, y) self.plot_data.add_point(x, y) def create_add_point_btn(self): add_point_btn = Button(self, text="Add point", command=self.add_point_clb, padx=5) add_point_btn.place(x=10, y=50, height=MainApplication.BTN_HEIGHT, width=MainApplication.BTN_WIDTH) return add_point_btn def add_multiple_points_clb(self): self.plot.update_check_state(self.check_state.get()) def create_add_multiple_points_check(self): check_state = BooleanVar() add_multiple_points_check = Checkbutton(self, text="Add multiple points", bg="white", var=check_state, onvalue=1, offvalue=0, command=self.add_multiple_points_clb) add_multiple_points_check.place(x=10, y=90) return add_multiple_points_check, check_state def update_line_clb(self): if len(self.plot is not None): self.plot.update_best_fitting_line() def create_update_line_btn(self): update_line_btn = Button(self, text="Update line", command=self.update_line_clb) update_line_btn.place(x=10, y=125, height=MainApplication.BTN_HEIGHT, width=MainApplication.BTN_WIDTH) return update_line_btn @staticmethod def is_number(s): try: float(s) return True except ValueError: return False