I thought I know the fundamentals of python, but this problem seems to prove that wrong.
Problem is, when I pass a class to a function, that function will not recognize the class that I passed, but instead just recognize that parent class.
This is the class.
from tkinter import *
from one_sample_t_test_dialog import One_T_Test_Dialog
from about_us_dialog import About_Us_Dialog
class Gui(Frame):
def __init__(self, master):
Frame.__init__(self, master, background="white")
self._master = master
# Main Window
frame = Frame(master, width = 800, height = 600)
self._master.title("Statistics Program")
# Menus
menu = Menu(master)
master.config(menu=menu)
# --Tests
test_menu = Menu(menu)
menu.add_cascade(label = "Tests", menu = test_menu)
# ----T-Tests
t_test_menu = Menu(test_menu)
test_menu.add_cascade(label = "T-Tests", menu = t_test_menu)
t_test_menu.add_command(label="One Sample t-test", command = self.one_sample_t_test)
t_test_menu.add_command(label="Two Sample t-test", command = self.two_sample_t_test)
t_test_menu.add_command(label="Paired t-test", command = self.about_us)
# --Help
help_menu = Menu(menu)
menu.add_cascade(label = "Help", menu = help_menu)
help_menu.add_command(label="About Us", command = self.about_us)
# Toolbar
# --t-test
toolbar = Frame(master)
l = Label(toolbar, text="Mean Comparison:")
l.pack(side=LEFT, padx = 5, pady = 5)
b=Button(toolbar, text = "One Sample t-test", command=self.one_sample_t_test)
b.pack(side=LEFT)
b=Button(toolbar, text = "Two Sample t-test", command=self.two_sample_t_test)
b.pack(side=LEFT)
b=Button(toolbar, text = "Paired t-test", command=self.two_sample_t_test)
b.pack(side=LEFT)
# --anova
l=Label(toolbar, text="ANOVA:")
l.pack(side=LEFT, padx = 5, pady = 5)
b=Button(toolbar, text = "One Way Anova", command=self.two_sample_t_test)
b.pack(side=LEFT)
# --Multiple-comparison Tests
toolbar_02 = Frame(master)
l=Label(toolbar_02, text="Multiple Mean Comparison:")
l.pack(side=LEFT, padx = 5, pady = 5)
b=Button(toolbar_02, text = "Tukey", command=self.two_sample_t_test)
b.pack(side=LEFT)
b=Button(toolbar_02, text = "Bonferroni", command=self.two_sample_t_test)
b.pack(side=LEFT)
toolbar.pack(fill=BOTH)
toolbar_02.pack(fill=BOTH)
# Spreadsheet.
self.canvas = canvas = Canvas(self._master)
self.canvas_frame = canvas_frame = Frame(canvas)
# Scrollbars
vbar=Scrollbar(self._master,orient=VERTICAL, command=self.canvas.yview)
hbar=Scrollbar(self._master,orient=HORIZONTAL, command=self.canvas.xview)
# Further configuration
canvas.configure(yscrollcommand=vbar.set, xscrollcommand=hbar.set)
# Initialize scrollbars
vbar.pack(side=RIGHT,fill=Y)
hbar.pack(side=BOTTOM,fill=X)
canvas.pack(side=LEFT, expand=True, fill="both")
canvas.create_window((4,4), window=canvas_frame, anchor="nw")
canvas_frame.bind("<Configure>", self.OnFrameConfigure)
self.grid()
#canvas_frame.pack()
self._master.geometry("800x600+50+50")
#self.pack(fill=BOTH, expand=1)
def get_master(self):
return self._master
def OnFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def about_us(self):
d = About_Us_Dialog(self._master)
root.wait_window(d.parent)
def grid(self):
"""
Make the grid here.
"""
grid_frame = self.canvas_frame
self.entry = []
for i in range(40):
self.entry.append([])
for i in range(len(self.entry)):
for j in range(80):
self.entry[i].append(Entry(grid_frame, width=10))
self.entry[i][j].grid(row=j, column=i)
# grid_frame.pack(padx=2, pady=2)
def one_sample_t_test(self):
d = One_T_Test_Dialog(self)
value = self._master.wait_window(d.parent)
# Check if an error occured.
result = None # We will store the result here.
if value is None:
return
else:
# perform the t-test here.
pass
# If we made it at this point, there's no error and
# the result have been acquired. We can now display
# the result.
def two_sample_t_test(self):
# Testing Ground
#print(self.get_variables())
#print(self.get_values(3))
pass
def get_variables(self):
"""
This method will return a dictionary of variable names and their corresponding
index, that is located in index zero of the double array. For instance,
self.entry[3][0] is a variable name, so is self.entry[5][0], and so on.
"""
variable_name_dict = {}
for i in range(len(self.entry)):
temp = self.entry[i][0].get()
if temp is not "":
variable_name_dict[i] = temp
return variable_name_dict
def get_values(self, variable_index):
"""
This method will return a list of values that is located under the variable.
Use this in conjunction with get_variables().
"""
values = []
if self.entry[variable_index][0] is not "": # Make sure that it's not empty.
for v in self.entry[variable_index]:
if v.get() is not "":
values.append(v.get())
# Since the first cell is in the column is always a variable name, we can
# pop the first element.
values.pop(0)
return values
root = Tk()
app = Gui(root)
root.mainloop()
This is the other class, with a method being called by the class above. the class above pass itself as an argument.
from tkinter import *
from tkinter import messagebox
import dialog
class One_T_Test_Dialog(dialog.Dialog):
def body(self, gui):
master = gui.get_master()
# Entry Labels.
Label(master, text="Mean:").grid(row=0)
Label(master, text="Standard Deviation:").grid(row=1)
Label(master, text="Sample Size:").grid(row=2)
Label(master, text="Sample Size:").grid(row=3)
Label(master, text="Test Value:").grid(row=4)
# Data entry class members.
# The for loop initialize the list as an entry list.
num_of_entry = 5
self.entry = [] #entry list
for i in range(num_of_entry):
self.entry.append(Entry(master))
# Data entry location initialization.
for i in range(num_of_entry):
self.entry[i].grid(row=i,column=1)
# Or, the user can just select a mean from the drop down list and
# enteryt the test value.
Label(master, text="Select Values Instead:").grid(column = 0, row=5)
self.dropdown_val = StringVar(master)
# initial value
self.dropdown_val.set('Select a values.')
choices = ['red', 'green', 'blue', 'yellow','white', 'magenta']
option = OptionMenu(master, self.dropdown_val, *choices).grid(column = 1, row=5)
button = Button(master, text="check value slected").grid(column=1, row=6)
# Further initialization.
# --At the Test Value, or null hypothesis, we want to have a default
# value. Assuming this is a 151/252 level course, the default value
# is always 0.
self.entry[4].insert(0, "0")
return self.entry[0] # initial focus
def apply(self):
# Collect the data first.
data_list = []
for e in self.entry:
data_list.append(e.get())
# Validate
for d in data_list:
# Make sure it's not empty.
# Make sure the value is float.
empty_flag = False
not_float_flag = False
if len(d) == 0:
empty_flag = True
if empty_flag is False:
try:
float(d)
except ValueError:
not_float_flag = True
if empty_flag is True or not_float_flag is True:
# Report an input error.
if empty_flag is True and not_float_flag is False:
messagebox.showerror("INPUT ERROR", "There's an empty input box.")
elif not_float_flag is True and empty_flag is False:
messagebox.showerror("INPUT ERROR", "Check your input. Make sure its a number.")
elif not_float_flag is True and empty_flag is True:
messagebox.showerror("INPUT ERROR", "There's an empty input box and non-numerical input.")
return None
# If everything went well, convert the validated data.
for i in range(len(data_list)):
data_list[i] = float(data_list[i])
return data_list
The problem is the line
master = gui.get_master()
in the second class gives an error because
AttributeError: 'Frame' object has no attribute 'get_master'
Frame being the parent of the class Gui.
Related
I have a question. I have this code:
import tkinter as tk
class new_f:
def __init__(self,root,num):
self.new_frame=tk.Frame(root,width=100,height=100,bg='white',bd=3,relief=tk.GROOVE)
self.new_frame.pack(side=tk.LEFT,fill=tk.X,expand=True)
self.num=num
def add_label(self,t):
self.l1=tk.Label(self.new_frame,bg='white',text=t)
self.l1.pack()
def return_instance(self):
return self.num
class Main_win:
def __init__(self,root):
self.root=root
self.bind_number=0
self.current_index=0
self.instance_list=[]
self.b1=tk.Button(self.root,text='Add Frame',command=self.add_frame_win)
self.b1.pack(side=tk.BOTTOM)
self.b2=tk.Button(self.root,text='Add text',command=self.add_text_frame)
self.b2.pack(side=tk.BOTTOM)
def return_instance_num(self,num,*args):
self.current_index=num
def add_frame_win(self):
new_in=new_f(self.root,self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>',lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index=new_in.return_instance()
self.bind_number+=1
def add_text_frame(self):
instance=self.instance_list[self.current_index]
instance.add_label('Hello World')
root=tk.Tk()
ob=Main_win(root)
root.mainloop()
What I a trying to achieve is that I want to detect on which frame was the left mouse-button clicked so as to make that Frame active and add the labels to that particular Frame. However, I am stuck on how would I go about writing the code. I need a new class Because I don't know how many frames will the user need.
This is a short example of the code I will be implementing later. So my question is:
How will I go to detect which frame was picked so as to make it active to add the labels?
In this approach I have label l1 bound to Button-1
This was achieved by passing self to new_f instead of root
and binding self.l1 to Button-1
import tkinter as tk
class new_f:
def __init__(self, prog, num):
self.prog = prog
self.new_frame = tk.Frame(prog.root, width = 100, height = 100, bg = 'white', bd = 3, relief = tk.GROOVE)
self.new_frame.pack(side = tk.LEFT, fill = tk.X, expand = True)
self.num = num
def add_label(self, t):
self.l1 = tk.Label(self.new_frame, bg = 'white', text = t)
self.l1.pack()
# binding button-1 press to label
self.l1.bind("<Button-1>", lambda evnt: self.prog.return_instance_num(self.return_instance()))
def return_instance(self):
return self.num
class Main_win:
def __init__(self, root):
self.root = root
self.bind_number = 0
self.current_index = 0
self.instance_list = []
self.b1 = tk.Button(self.root, text = 'Add Frame', command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(self.root, text = 'Add text', command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def return_instance_num(self, num, *args):
self.current_index = num
def add_frame_win(self):
# note passing self not root
new_in = new_f(self, self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>', lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index = new_in.return_instance()
self.bind_number = self.bind_number + 1
def add_text_frame(self):
instance = self.instance_list[self.current_index]
instance.add_label('Hello World')
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
Here is an alternative method that uses dictionaries to store l1 and new_frame objects as keys and new_f instances as values.
This method can be used for other tkinter objects (Entry, Listbox, Text, Canvas)
import tkinter as tk
class new_f:
def __init__(self, parent):
self.parent = parent
self.frame = tk.Frame(
parent.root, width = 100, height = 100,
bg = "white", bd = 3, relief = tk.GROOVE)
self.frame.pack(
side = tk.LEFT, fill = tk.X, expand = True)
self.frame.bind("<Button-1>", parent.get_current_frame)
def add_label(self, t):
self.label = tk.Label(self.frame, bg = "white", text = t)
self.label.pack(fill = tk.BOTH, expand = True)
# bind button-1 to label, set instance_label and current to self
self.label.bind("<Button-1>", self.parent.get_current_label)
self.parent.instance_label[self.label] = self.parent.current = self
class Main_win:
instance_label = dict() # This method can be expanded for other objects
instance_frame = dict() # that you may want to create in frames
def __init__(self, root):
self.root = root
self.b1 = tk.Button(
self.root, text = "Add Frame", command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(
self.root, text = "Add text", command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def get_current_label(self, ev):
self.current = self.instance_label[ev.widget]
def get_current_frame(self, ev):
self.current = self.instance_frame[ev.widget]
def add_frame_win(self):
# note passing self not root
self.new_in = new_f(self)
self.instance_frame[self.new_in.frame] = self.current = self.new_in
def add_text_frame(self):
# Change message with entry tool?
self.current.add_label("Hello World")
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
I'm wondering if I got my if else statement wrong or if its a tkinter issue. I want it so that if a 0 is left in any or all boxes, it gives an error message. But after the error message is closed, it opens a random blank window. This is my code. The specific area is the if else statement within the function valueget()
import tkinter as tk
def mainwindow():
mainwindow = tk.Tk()
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
tk.Label(mainwindow, text = 'Enter a', font = ('verdana'), bg='#aaf0d1').grid(row=0)
tk.Label(mainwindow, text = 'Enter b', font = ('verdana'), bg='#aaf0d1').grid(row=1)
tk.Label(mainwindow, text = 'Enter c', font = ('verdana'), bg='#aaf0d1').grid(row=2)
getA = tk.IntVar()
aBox = tk.Entry(mainwindow, textvariable = getA, width=3, bg='#aaf0d1')
aBox.grid(row=0, column=1)
aBox.config(highlightbackground='#aaf0d1')
getB = tk.IntVar()
bBox = tk.Entry(mainwindow, textvariable = getB, width=3, bg='#aaf0d1')
bBox.grid(row=1, column=1)
bBox.config(highlightbackground='#aaf0d1')
getC = tk.IntVar()
cBox = tk.Entry(mainwindow, textvariable = getC, width=3, bg='#aaf0d1')
cBox.grid(row=2, column=1)
cBox.config(highlightbackground='#aaf0d1')
button = tk.Button(mainwindow, text='Obtain roots', command = lambda: valueget(), font = ('verdana'), highlightbackground='#aaf0d1')
button.grid(row=4)
button.config(bg='#aaf0d1')
def valueget():
readA = getA.get()
readB = getB.get()
readC = getC.get()
intA = int(readA)
intB = int(readB)
intC = int(readC)
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
messagewindow = tk.Tk()
messagewindow.geometry('290x50')
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
if readA == 0 or readB==0 or readC==0 or (readA==0 and readB==0 and readC==0):
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
messagewindow.mainloop()
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
mainwindow.mainloop()
def startup():
startpage = tk.Tk()
startpage.title('Solver')
photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage, image = photo, command = lambda: [startpage.destroy(), mainwindow()])
coverbutton.pack()
coverbutton.configure(highlightbackground='#aaf0d1')
startpage.mainloop()
startup()
Here's a basic idea of what I would do:
import tkinter as tk
from tkinter import messagebox
def mainwindow(root):
# Creates a toplevel window
mainwindow = tk.Toplevel()
mainwindow.protocol("WM_DELETE_WINDOW", root.destroy) # This overrides the "X" being clicked to also destroy the root window.
root.withdraw() # "Hides" the root window, leaving it (and mainloop) running in the background.
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
# Since all three of the labels/entries are the same
# we can save space by generating them in a loop
entry_items = ('Enter a', 'Enter b', 'Enter c')
values = []
for x, item in enumerate(entry_items): # Using enumerate and x to assign rows
tk.Label(mainwindow, text = item,
font = ('verdana'), bg='#aaf0d1').grid(row=x) # Row assigned to x.
values.append(tk.StringVar()) # Appended StringVar to list.
tk.Entry(mainwindow,
textvariable = values[-1], # Uses the last value appended to the values list.
highlightbackground='#aaf0d1',
width=3,
bg='#aaf0d1').grid(row=x, column=1) # Row assigned to x.
tk.Button(mainwindow,
text='Obtain roots',
command = lambda vals = values: valueget(vals), # Here the button command is assigned with the values list
font = ('verdana'), bg='#aaf0d1',
highlightbackground='#aaf0d1').grid(row=3) # we know there are 3 items before this.
mainwindow.lift() # This is a method of bringing a window to the front
def valueget(vals):
# This line gets the values from the StringVars, converts them to ints,
# and returns them to their respective variables.
try:
readA, readB, readC = [int(val.get()) for val in vals]
except ValueError:
messagebox.showerror(title="Number Error", message='Values must be numbers')
return
# Here the variables are checked to see if they are 0
# Since each one is being checked if it is 0, there is no need to check if they are all 0.
for val in (readA, readB, readC):
if val == 0:
# If they are 0, shows an error message
messagebox.showerror(title="Zero Error", message='Values must not be zero')
return
# Creates a toplevel to display the results
messagewindow = tk.Toplevel()
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
tk.Label(messagewindow,
text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}',
bg='#aaf0d1',
font = ('verdana')).pack(padx = 5, pady = 2)
tk.Button(messagewindow,
text='Close',
command = messagewindow.destroy, # There is no need for a lambda for this.
font = ('verdana'),
bg = '#aaf0d1',
highlightbackground='#aaf0d1').pack(padx = 5, pady = 2)
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
messagewindow.lift() # This is a method of bringing a window to the front
def startup():
startpage = tk.Tk()
startpage.title('Solver')
# COMMENTED OUT FOR TESTING
#photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage,
# COMMENTED OUT FOR TESTING
#image = photo,
text = "TESTING", # HERE FOR TESTING
highlightbackground='#aaf0d1',
command = lambda root = startpage: mainwindow(root)).pack() # Passes the startpage to the mainwindow function.
startpage.mainloop() # The only mainloop you need.
startup()
I would recommend to improve the readability of the if-else statement for a start.
coefficients = [readA, readB, readC]
if sum(coefficients): # If they all are all zeros this will be False
if min(coefficients): # If any one is zero, this will be False
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
else:
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
errorwindow = tk.messagebox.showerror(message='none').pack()
I'm aware that this is a question that has been asked before on this site. However, I've made an honest attempt to implement the solutions put forward in those answers, and I'm still running into the same problem: Python seems to keep garbage-collecting my image, and I get an empty slot in my window where the image ought to be, as shown in the attached screenshot.
This is the part of my code where I attempt to import an image:
def make_top_title(self):
result = Frame(self.top)
text_lbl = Label(result, text="Proserpine",
font=(main_font, title_pt, "bold"))
arms_image = PhotoImage("icon.png")
arms_lbl = Label(result, image=arms_image, height=200)
arms_lbl.image = arms_image
arms_lbl.pack()
text_lbl.pack()
return result
You'll notice that I've already attempted to use the trick of preserving the image by anchoring it onto a property of a label object. You may also notice, from the attached screenshot, that I have no problem importing a custom icon - using the same image file - for this window.
I'm happy to share any more code from this program, as required.
A couple of people have suggested I share a bit more of my code. This is the whole file in which the code above is located:
### This code holds a class which allows the user to inspect a the coldstore
### object attached to its parent.
# GUI imports.
from tkinter import *
# Custom imports.
from sibstructures import data_storage
from sibstructures.numerals import index_to_label_column, \
index_to_label_row, \
index_to_label_layer
# Imports
import time
from threading import Thread
# Local constants.
main_font = "Arial"
title_pt = 60
subtitle_pt = 30
big_pt = 20
standard_pt = 15
diddy_pt = 10
standard_pad = 10
inner_pad = 5
tile_width = 15
details_height = 10
details_width = 30
grid_border = 5
spot_stack_width = tile_width+5
##############
# MAIN CLASS #
##############
# The class in question.
class CS_Viewer:
def __init__(self, parent):
self.parent = parent
self.parent_window = self.parent.get_top()
self.code = self.parent.coldstore.code
self.parent.coldstore.reconstruct()
self.max_max_layers = self.parent.coldstore.get_max_max_layers()
self.layer = 0
self.top = Frame(self.parent_window)
self.top_title = self.make_top_title()
self.subtitle = Label(self.top, text="code="+self.code,
font=(main_font, subtitle_pt, "bold"))
self.main_container = Frame(self.top)
self.spot_grid = Spot_Grid(self, self.main_container)
self.box_details = Text(self.main_container,
height=details_height, width=details_width,
state=DISABLED)
self.spot_stack = Spot_Stack(self.main_container, None,
self.box_details)
self.add_headings()
self.arrange()
# Ronseal.
def make_top_title(self):
result = Frame(self.top)
text_lbl = Label(result, text="Proserpine",
font=(main_font, title_pt, "bold"))
arms_image = PhotoImage("icon.png")
arms_lbl = Label(result, image=arms_image, height=200)
arms_lbl.image = arms_image
arms_lbl.pack()
text_lbl.pack()
return result
# Add headings to the holster widgets.
def add_headings(self):
spot_grid_label = Label(self.main_container, text="Coldstore",
font=(main_font, big_pt, "bold"))
spot_stack_label = Label(self.main_container, text="Spot",
font=(main_font, big_pt, "bold"),
width=spot_stack_width)
box_details_label = Label(self.main_container, text="Box",
font=(main_font, big_pt, "bold"))
spot_grid_label.grid(column=0, row=0)
spot_stack_label.grid(column=1, row=0)
box_details_label.grid(column=2, row=0)
# Ronseal.
def place_spot_stack(self):
self.spot_stack.get_top().grid(column=1, row=1,
padx=standard_pad, pady=standard_pad)
# Arrange the object's elements.
def arrange(self):
self.top_title.pack()
self.subtitle.pack()
self.spot_grid.get_top().grid(column=0, row=1, sticky=N,
padx=standard_pad, pady=standard_pad,
ipadx=inner_pad, ipady=inner_pad)
self.place_spot_stack()
self.box_details.grid(column=2, row=1, sticky=N,
padx=standard_pad, pady=standard_pad)
self.main_container.pack()
# Replace the spot stack widget.
def replace_spot_stack(self):
self.spot_stack.get_top().grid_forget()
self.place_spot_stack()
# Ronseal.
def get_top(self):
return self.top
################################
# HELPER CLASSES AND FUNCTIONS #
################################
# A class which holds the grid of spots.
class Spot_Grid:
def __init__(self, parent, parent_window):
self.parent = parent
self.parent_window = parent_window
self.top = Frame(self.parent_window, borderwidth=grid_border,
relief="solid")
Thread(target=self.make_grid).start()
# Fill the grid with boxes.
def make_grid(self):
cs = self.parent.parent.coldstore
for i in range(len(cs.columns)):
column_label = Label(self.top, text=str(index_to_label_column(i)),
font=(main_font, big_pt, "bold"))
column_label.grid(column=(i+1), row=0, padx=standard_pad)
for j in range(len(cs.columns[0].spots)):
if i == 0:
row_label = Label(self.top, text=str(index_to_label_row(j)),
font=(main_font, big_pt, "bold"))
row_label.grid(column=0, row=(j+1), padx=standard_pad)
tile = Spot_Tile(self, self.parent, cs.columns[i].spots[j],
self.parent.box_details)
tile.get_top().grid(column=(i+1), row=(j+1))
# Ronseal.
def get_top(self):
return self.top
# A class which holds a clickable representation of a spot.
class Spot_Tile:
def __init__(self, parent, main_ref, spot_obj, box_details_ref):
self.parent = parent
self.main_ref = main_ref
self.spot_obj = spot_obj
self.box_details_ref = box_details_ref
self.parent_window = self.parent.get_top()
self.top = Frame(self.parent_window)
Thread(target=self.make_filling).start()
# Fill the object with either a tile or a label.
def make_filling(self):
if self.spot_obj.max_layers == 0:
filling = Label(self.top, text="VOID", font=(main_font, diddy_pt),
width=tile_width)
elif self.spot_obj.layers() == 0:
filling = Button(self.top, text="free", command=None,
font=(main_font, diddy_pt, "italic"),
width=tile_width, state=DISABLED)
else:
filling = self.make_filling_button()
filling.pack()
# Make the filling object if it is a button.
def make_filling_button(self):
result = Button(self.top, text=self.make_filling_button_text(),
command=self.inspect,
font=(main_font, diddy_pt), width=tile_width)
return result
# Make the text portion of the filling button.
def make_filling_button_text(self):
growers = set()
varieties = set()
fields = set()
for box in self.spot_obj.boxes:
current_epc = box.epc
current_data = data_storage.fetch_most_recent_crop(current_epc)
growers.add(current_data["grower"])
varieties.add(current_data["variety"])
fields.add(current_data["field"])
result = (set_to_string(growers)+"\n"+set_to_string(varieties)+"\n"+
set_to_string(fields)+"\n"+str(len(self.spot_obj.boxes)))
return result
# Inspect a given spot.
def inspect(self):
self.main_ref.spot_stack = Spot_Stack(self.main_ref.main_container,
self.spot_obj,
self.box_details_ref)
self.main_ref.replace_spot_stack()
# Ronseal.
def get_top(self):
return self.top
# A class which holds a representation of the boxes on a given spot.
class Spot_Stack:
def __init__(self, parent_window, spot_obj, box_details_ref):
self.parent_window = parent_window
self.spot_obj = spot_obj
self.box_details_ref = box_details_ref
self.top = Frame(self.parent_window)
if self.spot_obj is None:
self.fill_empty()
else:
Thread(target=self.add_boxes).start()
# "Fill in" the representation if the spot object is empty.
def fill_empty(self):
label = Label(self.top, text="Select spot",
font=(main_font, standard_pt, "italic"), width=tile_width)
label.pack()
# Add representations of the spot's boxes.
def add_boxes(self):
no_of_boxes = len(self.spot_obj.boxes)
if no_of_boxes == 0:
empty_label = Label(self.top, text="Empty spot",
font=(main_font, standard_pt, "italic"),
width=tile_width)
empty_label.pack()
else:
for i in range(no_of_boxes):
backwards_index = (no_of_boxes-1)-i
box_tile = Box_Tile(self.top, self.spot_obj.boxes[backwards_index],
backwards_index, self.box_details_ref)
box_tile.get_top().pack()
# Ronseal.
def get_top(self):
return self.top
# A class which holds a clickable representation of a box.
class Box_Tile:
def __init__(self, parent_window, box, index, box_details_ref):
self.parent_window = parent_window
self.box = box
self.index = index
self.box_details_ref = box_details_ref
self.top = Frame(self.parent_window)
self.make_filling()
# Fill the object with either a tile or a label.
def make_filling(self):
label = Label(self.top, text=str(index_to_label_layer(self.index)),
font=(main_font, standard_pt))
filling = Button(self.top, text=self.box.epc, command=self.inspect,
font=(main_font, standard_pt), width=tile_width)
label.grid(column=0, row=0, padx=standard_pad)
filling.grid(column=1, row=0)
# Ronseal.
def get_top(self):
return self.top
# Inspect the data for this particular box in more detail.
def inspect(self):
text_to_insert = data_storage.fetch_most_recent_crop(self.box.epc)
self.box_details_ref.config(state=NORMAL)
self.box_details_ref.delete("1.0", END)
self.box_details_ref.insert(END, text_to_insert)
self.box_details_ref.config(state=DISABLED)
# Turns a set into a string, with items thereof separated by commas.
def set_to_string(the_set):
result = ""
the_list = list(the_set)
# Reversal is necessary, since .add() seems to add items to the FRONT of
# the set.
the_list.reverse()
for item in the_list:
if the_list.index(item) == 0:
result = item
else:
result = result+", "+item
return result
This is the other file, which, with its fellow, makes up the whole program:
### This code holds a class which manages transitions between windows, and
### also oversees their interactions with the Coldstore object.
# Imports.
from pathlib import Path
# GUI imports.
from tkinter import *
# Custom imports.
from sibstructures.coldstore import Coldstore
# Local imports.
from cs_viewer import CS_Viewer
# Constants.
path_to_db = str(Path.home())+"/cseye/source/proserpine/data.db"
##############
# MAIN CLASS #
##############
# The class in question.
class Comptroller:
def __init__(self):
self.coldstore = Coldstore(proserpine_mode=True,
proserpine_path=path_to_db)
self.gui = Tk()
self.top = Frame(self.gui)
self.window = CS_Viewer(self)
self.arrange()
# Return the top-level GUI object.
def get_top(self):
return self.top
# Arrange the widgets.
def arrange(self):
self.window.get_top().pack()
self.top.pack()
# Run the "mainloop" method on the GUI object.
def run_me(self):
self.gui.title("Proserpine")
self.gui.iconphoto(True, PhotoImage(file="icon.png"))
self.gui.mainloop()
###################
# RUN AND WRAP UP #
###################
def run():
comptroller = Comptroller()
comptroller.run_me()
if __name__ == "__main__":
run()
The argument to PhotoImage("...") is wrong. It should be PhotoImage(file="...").
I I have 2 functions and the second function should run after the button in the first one has been clicked. This works fine, however I need the number that has been entered to go into a variable and so far the .get() function is not working and im not sure what to do.
I have looked at a lot of different examples, including login and sign up gui's however none of them have been able to help.
from tkinter import *
import tkinter.messagebox as box
def enter_rle_1():
enter_rle = Tk() #do not remove
enter_rle.title('Enter RLE') #do not remove
frame = Frame(enter_rle) #do not remove
label_linecount = Label(enter_rle,text = 'Linecount:')
label_linecount.pack(padx=15,pady= 5)
linecount = Entry(enter_rle,bd =5)
linecount.pack(padx=15, pady=5)
ok_button = Button(enter_rle, text="Next", command = linecount_button_clicked)
ok_button.pack(side = RIGHT , padx =5)
frame.pack(padx=100,pady = 19)
enter_rle.mainloop()
def linecount_button_clicked():
Linecount = linecount.get()
if int(Linecount) < 3:
tm.showinfo("Error", "Enter a number over 3")
elif int(Linecount) > 1000000000:
tm.showinfo("Error", "Enter a number under 1,000,000,000")
else:
print("fdsfd")
enter_rle_1()
I expect there to be a popup telling me the number is too big or too small, depending on wether the number is or not, and if it is a good number, i just have it set as a print as some code to test to see if it works before i move on.
Define a String variable for the Entry widget (Make sure it is global defined):
global linecount_STR
linecount_STR = StringVar()
linecount_STR.set('Enter value here')
linecount = Entry(enter_rle,bd =5,textvariable=linecount_STR)
linecount.pack(padx=15, pady=5)
The number entered there can then be read with int(linecount_STR.get()).
i suggest an OO approach, look this code
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class Main(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.vcmd = (self.register(self.validate), '%d', '%P', '%S')
self.linecount = tk.IntVar()
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1)
f = ttk.Frame()
ttk.Label(f, text = "Linecount").pack()
self.txTest = ttk.Entry(f,
validate='key',
validatecommand=self.vcmd,
textvariable=self.linecount).pack()
w = ttk.Frame()
ttk.Button(w, text="Next", command=self.on_callback).pack()
ttk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
def on_callback(self,):
#print ("self.text = {}".format(self.linecount.get()))
x = self.linecount.get()
if x < 3:
msg = "Enter a number over 3"
elif x > 1000000000:
msg = "Enter a number under 1,000,000,000"
else:
msg = "You ahve enter {0}".format(x)
messagebox.showinfo(self.parent.title(), msg, parent=self)
def on_close(self):
self.parent.on_exit()
def validate(self, action, value, text,):
# action=1 -> insert
if action == '1':
if text in '0123456789':
try:
int(value)
return True
except ValueError:
return False
else:
return False
else:
return True
class App(tk.Tk):
"""Start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_title()
self.set_style()
frame = Main(self,)
frame.pack(fill=tk.BOTH, expand=1)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self):
s = "{0}".format('Enter RLE')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel(self.title(), "Do you want to quit?", parent=self):
self.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()
notice this line at the begin....
self.linecount = tk.IntVar()# declare an integer variable
self.vcmd = (self.register(self.validate), '%d', '%P', '%S')#register a function to allow only integer in your text widget.
I am trying to write my first app using Tkinter. I can't understand at all how it is possible to pass the data on variables in the 2D array (entered by user) from one class to another. Tried to change something, but nothing turned out. I will be very grateful for any help or advice.
from Tkinter import *
date_index = [2017, 2018, 2019, 2020, 2021]
product_name = ['product 1', 'product 2', 'product 3', 'product 4', 'product 5']
class main:
def __init__(self, master):
self.master = master
self.master.title('revenue calc')
Button(self.master, text = 'quantity', command=self.q_button).pack()
Button(self.master, text = 'prices', command=self.p_button).pack()
self.master.mainloop()
def q_button(self):
q_child(self.master)
def p_button(self):
p_child(self.master)
class q_child:
def __init__(self, master):
self.slave = Toplevel(master)
self.slave.title('quantity')
self.corner_frame = Frame(self.slave)
self.corner_frame.grid(row=0, column=0)
self.left_frame = Frame(self.slave)
self.left_frame.grid(row=1, column=0)
self.head_frame = Frame(self.slave)
self.head_frame.grid(row=0, column=1)
self.main_frame = Frame(self.slave)
self.main_frame.grid(row=1, column=1)
self.button_frame = Frame(self.slave)
self.button_frame.grid(row=2, column=1)
for i in range(len(product_name)):
self.testlabel = Label(self.left_frame, text = product_name[i])
self.testlabel.grid(row=i, column=0)
for j in range(len(date_index)):
self.testlabel1 = Label(self.head_frame, width = 5, text = date_index[j])
self.testlabel1.grid(row=0, column=j)
self.q0 = []
for j in range(len(date_index)):
self.q0.append([])
for i in range(len(product_name)):
self.q0[j].append(Entry(self.slave, width = 5, text=""))
self.q0[j][i].grid(row=j, column=i, in_ = self.main_frame)
self.save_q_button = Button(self.button_frame, text = 'save', command = self.save_q_data)
self.save_q_button.pack()
def save_q_data(self):
self.q = []
for j in range(len(date_index)):
self.q.append([])
for i in range(len(product_name)):
self.q[j].append(float(self.q0[j][i].get()))
class p_child:
def __init__(self, master):
self.slave = Toplevel(master)
self.slave.title('prices')
self.corner_frame = Frame(self.slave)
self.corner_frame.grid(row=0, column=0)
self.left_frame = Frame(self.slave)
self.left_frame.grid(row=1, column=0)
self.head_frame = Frame(self.slave)
self.head_frame.grid(row=0, column=1)
self.main_frame = Frame(self.slave)
self.main_frame.grid(row=1, column=1)
self.button_frame = Frame(self.slave)
self.button_frame.grid(row=2, column=1)
for i in range(len(product_name)):
self.testlabel = Label(self.left_frame, text = product_name[i])
self.testlabel.grid(row=i, column=0)
for j in range(len(date_index)):
self.testlabel1 = Label(self.head_frame, width = 5, text = date_index[j])
self.testlabel1.grid(row=0, column=j)
self.p0 = []
for j in range(len(date_index)):
self.p0.append([])
for i in range(len(product_name)):
self.p0[j].append(Entry(self.slave, width = 5, text=""))
self.p0[j][i].grid(row=j, column=i, in_ = self.main_frame)
self.save_p_button = Button(self.button_frame, text = 'save', command = self.save_p_data)
self.save_p_button.pack()
def save_p_data(self):
self.rev = []
self.revall = []
self.p = []
for j in range(len(date_index)):
self.rev.append([])
self.p.append([])
self.s = 0
for i in range(len(product_name)):
self.p[j].append(float(self.p0[j][i].get()))
self.rev[j].append(self.p[j][i]*q[j][i]) # NameError: global name 'q' is not defined
self.s += self.rev[j][i]
self.revall.append(self.s)
root = Tk()
main(root)
See below a simplified version of your code which shows how to pass data (in this case the text of a single Entry box) from your TopLevel() window back to your main window.
Basically, in your q_child class, you store the data you want to return in an attribute called, for example, self.data, so that when you return to the main class, you can access it by calling q.data.
You can even store this data in the main window's master attribute under a name like q_data, so that it can be accessed in the p_child class, through master.q_data
import Tkinter as tk
class main:
def __init__(self, master):
self.master = master
self.master.q_data = "No data entered"
tk.Button(self.master, text='quantity', command=self.q_button).pack()
tk.Button(self.master, text='prices', command=self.p_button).pack()
self.master.mainloop()
def q_button(self):
# Create a TopLevel window to get user input
q = q_child(self.master)
# Wait for the user to close the TopLevel window
self.master.wait_window(q.slave)
# Store the data input by the user in the main window's "master" attribute
self.master.q_data = q.data
def p_button(self):
# Create a TopLevel window to use the user input data
p = p_child(self.master)
# Wait for the user to close the TopLevel window
self.master.wait_window(p.slave)
class q_child:
def __init__(self, master):
# Create a TopLevel window, and grab focus
self.slave = tk.Toplevel(master)
self.slave.grab_set()
# Add an Entry box and a button
self.q_entry = tk.Entry(self.slave, text="")
self.q_entry.pack()
tk.Button(self.slave, text='save', command=self.save_q_data).pack()
# Initialize the data to be returned
self.data = "No data entered"
def save_q_data(self):
# Update the data to be returned with the Entry box content
self.data = self.q_entry.get()
# Close the TopLevel window
self.slave.destroy()
class p_child:
def __init__(self, master):
# Create a TopLevel window, and grab focus
self.slave = tk.Toplevel(master)
self.slave.grab_set()
# Retrieve the user-input data from the "master"
q_data = master.q_data
# Show the data on a label
tk.Label(self.slave, text=q_data).pack()
# Add a button to go back
tk.Button(self.slave, text='back', command=self.slave.destroy).pack()
root = tk.Tk()
main(root)
Important: self.master.wait_window(q.slave) ensures that the main class waits for the TopLevel window to be closed before continuing to run.