1

I´ve been working on a bhaskara equation solver with Interface() on . I´ve watched some tutorials and I´ve split the Pages into classes. The basic idea of this application is the following:

It starts at StartPage, where the user clicks on the select values button and this opens up PageOne. At this page the user should type the desired values and click the select values button (I´m sorry, I couldn't find a way to save all the values at one, if you know how to do this too, please let me know). After the user saves the values, he goes back to StartPage and clicks the Calculate button. If all values are OK, it shows the result variable.

import tkinter as tk
from tkinter import ttk, END
import math

LARGE_FONT =("Verdana", 12)

def calculate():
    global value_a
    global value_b
    global value_c
    value_list = [value_a, value_b, value_c]
    if "" in value_list:
        return False
    else:
        delta = (int(value_b.get())**2) - 4*int(value_a.get())*int(value_c.get())

        if delta >= 0:
            delta_root = math.sqrt(delta)
            bhask_pos = int(-value_b.get()) + (delta_root)/2*int(value_a.get())
            bhask_neg = int(-value_b.get()) - (delta_root)/2*int(value_a.get())
            global result
            result = "The equation", int(value_a.get()), "x² +", int(value_b.get()), "x +", int(value_c.get()), "has the results ", int(bhask_pos), "and ", int(bhask_neg)
        else:
            pass
        return True


class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self,*args, **kwargs)
        #self.geometry("720x360")
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_columnconfigure(0, weight=1)
        container.grid_rowconfigure(0, weight=1)

        self.frames = {}

        for F in (StartPage, PageOne):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()


class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        button = ttk.Button(self, text="Insert values", command=lambda: controller.show_frame(PageOne))
        button.pack(side="top", padx=10, pady=20, expand=False)
        canvas = tk.Canvas(self, width=400, height=200, bg="#C0C0C0", bd="10")
        canvas.pack(side="bottom", padx=10, pady=20, expand=False)
        if calculate() == False:
            canvas.create_text(30, 30, text="Error. Check if you selected all values")
        elif calculate() == True:
            canvas.create_text(30, 30, text=result)
        else:
            pass
        calculation_button = ttk.Button(self, text="Calculate", command=calculate)
        calculation_button.pack()


class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        def get_entry_data_a():
            global value_1
            value_1 = int(value_a.get())
            entry_a.delete(0, END)
            print(value_1)#just for debugging

        def get_entry_data_b():
            global value_2
            value_2 = int(value_b.get())
            entry_b.delete(0, END)
            print(value_2)

        def get_entry_data_c():
            global value_3
            value_3 = int(value_c.get())
            entry_c.delete(0, END)
            print(value_3)

        def event_data_a(event):
            value_1 = int(value_a.get())
            entry_a.delete(0, END)
            print(value_1)

        def event_data_b(event):
            value_2 = int(value_b.get())
            entry_b.delete(0, END)
            print(value_2)

        def event_data_c(event):
            value_3 = int(value_c.get())
            entry_c.delete(0, END)
            print(value_3)

        text_a = tk.Label(self, text="value from a:", padx=10, pady=10)
        text_a.grid(row=1, column=1)
        text_b = tk.Label(self, text="value from b:", padx=10, pady=10)
        text_b.grid(row=2, column=1)
        text_c = tk.Label(self, text="value from c", padx=10, pady=10)
        text_c.grid(row=3, column=1)

        value_a = tk.IntVar()
        entry_a = tk.Entry(self, textvariable=value_a)
        entry_a.grid(row=1, column=2)
        entry_a.delete(0, END)
        button_a = ttk.Button(self, text="Save value", command=get_entry_data_a)
        button_a.grid(row=1, column=3, padx=10, pady=10)

        value_b = tk.IntVar()
        entry_b = tk.Entry(self, textvariable=value_b)
        entry_b.grid(row=2, column=2)
        entry_b.delete(0, END)
        button_b = ttk.Button(self, text="Save value", command=get_entry_data_b)
        button_b.grid(row=2, column=3, padx=10, pady=10)

        value_c = tk.IntVar()
        entry_c = tk.Entry(self, textvariable=value_c)
        entry_c.grid(row=3, column=2)
        entry_c.delete(0, END)
        button_c = ttk.Button(self, text="Save value", command=get_entry_data_c)
        button_c.grid(row=3, column=3,padx=10, pady=10)

        entry_a.bind("<Return>", event_data_a)
        entry_b.bind("<Return>", event_data_b)
        entry_c.bind("<Return>", event_data_c)

        back_button = ttk.Button(self, text="Return to Start Page", command=lambda:controller.show_frame(StartPage))
        back_button.grid(row=5, column=2, padx=20, pady=20)


app = App()
app.mainloop()

The results should appear in the canvas, as well as a message error if the user does not select a value. My actual problem is that the canvas object is created in StartPage, so I need to define the function before the StartPage class (because I need the boolean result returned from the function calculate() to create the text in the canvas), but the actual values that the user chooses comes only at the end of the code. How is it possible to use those values? Where should I define the function calculate() in this case?

This is the error message:

line 10, in calculate
    value_list = [value_a, value_b, value_c]
NameError: name 'value_a' is not defined
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Where/what's the "select values" button? – martineau Feb 09 '19 at 01:26
  • 1
    See answers to the question [How to access variables from different classes in tkinter?](https://stackoverflow.com/questions/33646605/how-to-access-variables-from-different-classes-in-tkinter) for some hints. – martineau Feb 09 '19 at 01:52

1 Answers1

0

To get the globals working properly, just make sure to give them initial values before their first used. The easiest way to do this is to assign them default values at the module level. For example:

value_a = ""
value_b = ""
value_c = ""

def calculate():
    global value_a
    global value_b
    global value_c

    # ...

That should get your code working. But while Python supports globals, they're almost never the right choice. Look at the logic of your application and think of ways to use local scope more. One possibility is to set value_a, value_b, and value_c as instance variable in your App class, since it's accessible as controller in both frames.

For example:

def calculate(value_a, value_b, value_c):
    value_list = [value_a, value_b, value_c]
    # ...

class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self,*args, **kwargs)

        self.value_a = ""
        self.value_b = ""
        self.value_c = ""

        # ...

class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        # ...

        if calculate(controller.value_a, controller.value_b, controller.value_c):
            # ...

class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        def get_entry_data_a():
            value_1 = int(controller.value_a.get())
            entry_a.delete(0, END)
            print(value_1)#just for debugging

        # ...

        controller.value_a = tk.IntVar()
Nikolas Stevenson-Molnar
  • 4,235
  • 1
  • 22
  • 31